First of all, that's security, not safety. Lots of software exists in an environment where it isn't going to be attacked.
> Things like checked arithmetic matter and should be enabled by default in safe builds.
Rust offers actual checked arithmetic - for example, suppose I have a 32-bit signed integer (i32) and I want to add something to it, in Rust I can choose the most appropriate of:
add - also presented as the + operator, in debug this will panic on overflow, but in production I can choose, either panic or just have it wrap silently
checked_add - at runtime check for overflow and get either None or Some(i32)
overflowing_add - this time instead of Option we get a pair back, with our (wrapped if necessary) numeric answer and a boolean saying if we overflowed
saturating_add - addition saturates at the minimum and maximum thresholds
unchecked_add - even in debug builds this is not checked (yes it's unsafe)
wrapping_add - this performs the most likely native hardware behaviour, wrapping numbers around at the limits
You might also realise your variable should inherently have certain arithmetic, for example a 16-bit PCM sample should always have saturating arithmetic (getting this wrong is one reason badly written older audio software can sound bad) so you can make them Saturating<i16> or maybe you actually want wrapping arithmetic on the simulated 32-bit CPU registers in your Motorola 68000 simulator, so you use Wrapping<u32> for them.
The debug behaviour for the trivial + operator is not intended to be your best defence, if you're sure you want wrapping, write that down, if you're sure you want to check, write the check. But sure, if you somehow want your release code to panic, but don't want to write the panics out by hand, you can tell the compiler you want this in release builds.
> By definition, buffer bleeds can happen in safe Rust.
Nope.
> The borrow checker can protect against UAF and overflow, but it can't protect against all kinds of underflow
And it doesn't, in safe Rust the actual bounds checks are emitted, and they only get elided in most cases because in idiomatic Rust it's clear to the compiler that the checks are unnecessary (e.g. iterating over items in a vector, the compiler knows how long the vector is, and it can see we're starting at the beginning and stopping at the end, so, we don't need the bounds check and it won't actually be emitted in the machine code). thing[index] is bounds checked in safe Rust.
> No language is actually 100% memory safe, not with respect to buffer bleeds.
Many languages are 100% memory safe. Most fascinating here is WUFFS which - in exchange for its restricted purpose - gets to be both entirely safe and faster than the C (or C++, or Rust) you'd actually write.
You literally can't write a "buffer bleed" type goof in WUFFS. I don't mean "You won't because it's so easy to get it right" or even "It will warn you about the problem at runtime so you can fix it". I mean code which can exhibit that bug does not compile.