> Overflow errors can happen pretty easily No they can’t. Overflows aren’t a real problem. Do not add checked_mul to all your maths. Thankfully Rust changed overflow behavior from “undefined” to “well defined twos-complement”.
The vast majority of code that does arithmetic will not produce a correct result with two's complement. It is simply assuming that the values involved are small enough that it won't matter. Sometimes it is a correct assumption, but whenever it involves anything derived from inputs, it can go very wrong.
Pitfalls of Safe Rust
81–90 of 145 posts
Re: Pitfalls of Safe Rust
#82Earlier quoted context omitted.
I don't see why people would drop the "memory" part of "memory safe" and just promote the false advertising of "safe rust"
It sounds like you should read the docs. It's just a subject-specific abbreviation, not an advertising trick.
Re: Pitfalls of Safe Rust
#83Earlier quoted context omitted.
Almost exclusively isn't the same as exclusively. Notably the log4shell[1] vulnerability wasn't due to buffer overruns, and happened in a memory safe language. [1]: https://en.m.wikipedia.org/wiki/Log4Shell
In fact "exclusively" doesn't belong in the statement at all. A very small number of successful RCE attacks use exploits at all, and of those, most target (often simple command) injection vulnerabilities like Log4Shell. If you think back to the big breaches over the last five years, though -- SolarWinds, Colonial Pipeline, Uber, Okta (and through them Cloudflare), Change Healthcare, etc. -- all of these were basic ac…
Re: Pitfalls of Safe Rust
#84Title is slightly misleading but the content is good. It's the "Safe Rust" in the title that's weird to me. These apply to Rust altogether, you don't avoid them by writing unsafe Rust code. They also aren't unique to Rust. A less baity title might be "Rust pitfalls: Runtime correctness beyond memory safety."
It is consistent with the way the Rust community uses "safe": as "passes static checks and thus protects from many runtime errors." This regularly drives C++ programmers mad: the statement "C++ is all unsafe" is taken as some kind of hyperbole, attack or dogma, while the intent may well be to factually point out the lack of statically checked guarantees. It is subtle but not inconsistent that strong static checks ("s…
I thought the C++ language did that.
Re: Pitfalls of Safe Rust
#85Earlier quoted context omitted.
Unfortunately, operator[] on std::vector is inherently unsafe. You can potentially try to ban it (using at() instead), but that has its own problems. There’s a great talk by Louis Brandy called “Curiously Recurring C++ Bugs at Facebook” [0] that covers this really well, along with std::map’s operator[] and some more tricky bugs. An interesting question to ask if you try to watch that talk is: How does Rust design aro…
Thank you for sharing. Seems I still have more to learn! It seems the bug you are flagging here is a null reference bug - I know Rust has Optional as a workaround for “null” Are there any pitfalls in Rust when Optional does not return anything? Or does Optional close this bug altogether? I saw Optional pop up in Java to quiet down complaints on null pointer bugs but remained skeptical whether or not it was better to…
This is an issue with the C++ standardization process as much as with the language itself. AIUI when std::optional (and std::variant, which has similar issues) were defined, there was a push to get new syntax into the language itself that would’ve been similar to Rust’s match statement.
However, that never made it through the standardization process, so we ended up with “library variants” that are not safe in all circumstances.
Here’s one of the papers from that time, though there are many others arguing different sides: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p00...
Re: Pitfalls of Safe Rust
#86Earlier quoted context omitted.
Compared to C/C++ "as" feels so much safe r. Now that Rust and we the programmers have evolved with it, I too feel that "as" for narrowing conversion is a small foot gun.
I'm struggling to see how you would implement narrowing conversion in a way that is harder for programmers to misuse when they aren't being mindful, while also being pleasant to use when you really do want to just drop higher bits. Like, you could conceivably have something like a "try_narrow" trait which wraps the truncated value inside an Err when it doesn't fit, and it would probably be harder to accidentally misu…
Re: Pitfalls of Safe Rust
#87Earlier quoted context omitted.
The commonly given response to this question is two-fold, and both parts have a similar root cause: smart pointers and "safety" being bolted-on features developed decades after the fact. The first part is the standard library itself. You can put your data in a vec for instance, but if you want to iterate, the standard library gives you back a regular pointer that can be dereferenced unchecked, and is intended to be i…
Yep. Safe rust also protects you from UB resulting from incorrect multi-threaded code. In C++ (and C#, Java, Go and many other “memory safe languages”), it’s very easy to mess up multithreaded code. Bugs from multithreading are often insanely difficult to reproduce and debug. Rust’s safety guardrails make many of these bugs impossible. This is also great for performance. C++ libraries have to decide whether it’s bett…
Re: Pitfalls of Safe Rust
#88Earlier quoted context omitted.
There is, since the zero is used as a niche value optimisation for enums, so that Option > occupies the same amount of memory as u32. But this can be used with other enums too, and in those cases, having a zero NonZero would essentially transmute the enum into an unexpected variant, which may cause an invariant to break, thus potentially causing memory unsafety in whatever required that invariant.
> which may cause an invariant to break, thus potentially causing memory unsafety in whatever required that invariant By that standard anything and everything might be tainted as "unsafe", which is precisely GP's point. Whether the unsafety should be blamed on the outside code that's allowed to create a 0-valued NonZero or on the code that requires this purported invariant in the first place is ultimately a matter of…
The issue is that this could potentially allow creating a struct whose invariants are broken in safe rust. This breaks encapsulation, which means modules which use unsafe code (like `std::vec`) have no way to stop safe code from calling them with the invariants they rely on for safety broken. Let me give an example starting with an enum definition:
// Assume std::vec has this definition
struct Vec {
capacity: usize,
length: usize,
arena: * T
}
enum Example {
First {
capacity: usize,
length: usize,
arena: usize,
discriminator: NonZero
},
Second {
vec: Vec
}
}
Now assume the compiler has used niche optimization so that if the byte corresponding to `discriminator` is 0, then the enum is `Example::Second`, while if the byte corresponding to `discriminator` is not 0, then the enum is `Example::First` with discriminator being equal to its given non-zero value. Furthermore, assume that `Example::First`'s `capacity`, `length`, and `arena` fields are in the in the same position as the fields of the same name for `Example::Second.vec`. If we allow `fn NonZero::new_unchecked(u8) -> NonZero` to be a safe function, we can create an invalid Vec: fn main() {
let evil = NonZero::new_unchecked(0);
// We write as an Example::First,
// but this is read as an Example::Second
// because discriminator == 0 and niche optimization
let first = Example::First {
capacity: 9001, length: 9001,
arena: 0x20202020,
discriminator: evil
}
if let Example::Second{ vec: bad_vec } = first {
// If the layout of Example is as I described,
// and no optimizations occur, we should end up in here.
// This writes 255 to address 0x20202020
bad_vec[0] = 255;
}
}
So if we allowed new_unchecked to be safe, then it would be impossible to write a sound definition of Vec.Re: Pitfalls of Safe Rust
#89> Overflow errors can happen pretty easily No they can’t. Overflows aren’t a real problem. Do not add checked_mul to all your maths. Thankfully Rust changed overflow behavior from “undefined” to “well defined twos-complement”.
I'm a big fan of liberal use of saturating_mul/add/sub whenever there is a conceivable risk of coming withing a couple orders of magnitude of overflow. Or checked_*() or whatever the best behavior in the given case is. For my code it happens to mostly be saturating. Overflow bugs are a real pain, and so easy to prevent in Rust with just a function call. It's pretty high on my list of favorite improvements over C/C++
Re: Pitfalls of Safe Rust
#90Earlier quoted context omitted.
What makes you think this is the case? Having done a bunch of formal verification I can say that overflows are probably the most common type of bug by far.
Yeah, they're so common they've become a part of our culture when it comes to interacting with computers. Arithmetic overflows have become the punchline of video game exploits. Unsigned underflow is also one of the most dangerous types. You go from one of the smallest values to one of the biggest values.
Don’t do arithmetic with u8 or probably even u16.