6 ms·
In Rust it's pretty limited. Here is the full list, from the Rustonomicron: Dereferencing null or dangling pointers Reading uninitialized memory Br
by justinpombrio 9y ago
In Rust it's pretty limited. Here is the full list, from the Rustonomicron:
Dereferencing null or dangling pointers
Reading uninitialized memory
Breaking the pointer aliasing rules
Producing invalid primitive values:
dangling/null references
a bool that isn't 0 or 1
an undefined enum discriminant
a char outside the ranges [0x0, 0xD7FF] and [0xE000, 0x10FFFF]
a non-utf8 str
Unwinding into another language
Causing a data race
Note that all of these are inside of unsafe blocks. Besides unsafe blocks, Rust has no undefined behavior, and the compiler will prevent you from doing any of these things.
https://doc.rust-lang.org/nomicon/meet-safe-and-unsafe.html https://doc.rust-lang.org/nomicon/meet-safe-and-unsafe.html
- hornetblack 9y agoAn important point: if you provide a safe wrapper around some unsafe code it's up to the programmer to ensure the above is true.
- tom_mellior 9y ago> Note that all of these are inside of unsafe blocks. Besides unsafe blocks, Rust has no undefined behavior, and the compiler will prevent you from doing any of these things. It's true that unsafe is needed to get these problems, but they can also occur outside of unsafe blocks. See an example here: https://gankro.github.io/blah/only-in-rust/#unbound-lifetimes https://gankro.github.io/blah/only-in-rust/#unbound-lifetime... Your own code need not use "unsafe" at all, but the program may still crash in your code if you called some function that internally does unsafe things to mess up your memory. EDIT: I should say that the linked code does not crash, it only reads uninitialized memory. It seems to me like the same hole could be used to make things crash, but I don't have a ready-made example.
- adrianN 9y agoThe important quote from the article you linked is: "But what happens when we throw some unsafe code at the issue?", so the claim that safe Rust doesn't have UB (that isn't considered a bug in the compiler) still stands. If you mix in unsafe code, bad things may happen, regardless of whether you wrote the unsafe yourself or rely on a library.
- tom_mellior 9y ago> the claim that safe Rust doesn't have UB (that isn't considered a bug in the compiler) still stands Yes! > If you mix in unsafe code, bad things may happen Yes! My point was only that those bad things may also happen after the unsafe block that is their root cause. This is in reply to parent's "all of these [UBs] are inside of unsafe blocks". They aren't. They are caused by something inside unsafe blocks, but they may also materialize after.
- steveklabnik 9y agoSee also https://doc.rust-lang.org/nomicon/working-with-unsafe.html https://doc.rust-lang.org/nomicon/working-with-unsafe.html > unsafe does more than pollute a whole function: it pollutes a whole module. Generally, the only bullet-proof way to limit the scope of unsafe code is at the module boundary with privacy.
- tom_mellior 9y agoNot to belabor the point, but... > the only bullet-proof way to limit the scope of unsafe code is at the module boundary with privacy I understand how this is meant in the context of that page, and it is true that modules protect against users messing with modules' internal invariants. But does that also work the other way around? In the example in https://gankro.github.io/blah/only-in-rust/#unbound-lifetimes https://gankro.github.io/blah/only-in-rust/#unbound-lifetime... it's not the caller messing with the callee's state, it's the callee messing up the caller's state. Do modules help at all here? That is, would the function fn foo<'a>(input: *const u32) -> &'a u32 { unsafe { return &*input } } become less dangerous, or maybe impossible to call, when put into a module?
- steveklabnik 9y agoI would argue that this is not the caller making the mistake; it's this function. That is, since this function is safe, any safe code should be able to call it and not generate UB. It's not the caller's fault here, it's the incorrect implementation.
- tom_mellior 9y ago> It's not the caller's fault here, it's the incorrect implementation. I agree. But then this shows that it's possible to write "safe" Rust code that only calls "safe" external code and still (to a first approximation) have the possibility of undefined behavior showing up at any point. In other words, Rust's famous static, compiler-enforced guarantees are not guarantees at all, more like firm promises. Nothing wrong with that; it's easy to write memory-corrupting code in other safe-by-default languages like Haskell or OCaml as well. But it seems like Rust's marketing materials do try to suggest otherwise, and many people get wrong impressions (look at the first post in this thread, and the other comments on this article saying that Rust's "safe" code is 100% free from undefined behavior).
- jasode 9y ago>, Rust has no undefined behavior, Correct me if I'm wrong but I don't think such a strong universal statement can be true (even outside unsafe blocks) because LLVM has corner cases of "undefined behavior". (And since Rust is relies on LLVM, ...) Maybe it's more accurate to say that Rust minimizes undefined behavior as a design goal, or it doesn't have intentional undefined behavior.
- Jare 9y agoIf safe Rust code does not invoke any of the UB corners of LLVM then Rust can claim to be free from UB. I don't know enough to guarantee or verify it, but it's my current understanding that this is the case.
- jasode 9y ago>If safe Rust code does not invoke any of the UB corners of LLVM then Rust can claim to be free from UB. Sure and I believe that adding a conditional qualification such as "if one does not invoke UB of LLVM" restates my point: one can't make a universal statement that "safe Rust has zero undefined behavior." E.g., as of this writing, the following "safe Rust" UB issue (3+ years ago) last had comments 21 days ago and I believe it's still open: https://github.com/rust-lang/rust/issues/10184 https://github.com/rust-lang/rust/issues/10184
- ivanbakel 9y ago>a conditional qualification such as "if one does not invoke UB of LLVM" A conditional qualification which is intended to be unconditionally true of safe Rust code, outside bugs in the compiler. The universal statement is totally possible, because your conditional is equivalent to saying "if you write valid code".
- jasode 9y ago>which is _intended_ to be unconditionally true of safe Rust code, I emphasized "intended" because it seems like we're talking past each other. You: re-emphasizing Rust's specified design goal. Me: emphasizing the current state of Rust compiler as reality which makes the statement "safe Rust has no undefined behavior" as not true. (In other words, I emphasize the unintentional UB whereas you do not.) >, because your conditional is equivalent to saying "if you write valid code". If you look at the github issue, "1.04E+17 as u8" is valid safe Rust code which invokes UB.