Unsafe 1 @WaffleLapkin @BoxyUwU @m-ou-se @Victoronz
Warning: this quiz is still "work-in-progress", some questions might not have good explanations (or any at all), formatting/structure/titles/etc are not final, and so on. You might want to return here on a later date.
Note: the question is whether the code, as executed, is UB or whether it is sound. When the snippet has UB, explain exactly where it is UB and why.
#![allow(deref_nullptr)]
fn main() {
// 1. UB?
unsafe {
_ = *std::ptr::null::<u32>();
}
}
#![allow(deref_nullptr, unused_parens)]
fn main() {
// 2. UB?
unsafe {
_ = (*std::ptr::null::<u32>());
}
}
#![allow(deref_nullptr, unused_parens)]
fn main() {
// 3. UB?
unsafe {
_ = (*std::ptr::null::<u32>(),);
}
}
#![allow(deref_nullptr, unused_braces)]
fn main() {
// 4. UB?
unsafe {
_ = { *std::ptr::null::<u32>() };
}
}
#![allow(deref_nullptr)]
fn main() {
// 5. UB?
_ = unsafe { *std::ptr::null::<u32>() };
}
Solution
Examples 1 and 2 are fine, while 3, 4 and 5 are UB:
error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance)
--> examples/unsafe_1_3.rs:6:14
|
6 | _ = (*std::ptr::null::<u32>(),);
| ^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance)
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `main` at examples/unsafe_1_3.rs:6:14: 6:38
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error
error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance)
--> examples/unsafe_1_4.rs:6:15
|
6 | _ = { *std::ptr::null::<u32>() };
| ^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance)
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `main` at examples/unsafe_1_4.rs:6:15: 6:39
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error
error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance)
--> examples/unsafe_1_5.rs:5:18
|
5 | _ = unsafe { *std::ptr::null::<u32>() };
| ^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance)
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `main` at examples/unsafe_1_5.rs:5:18: 5:42
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error
Loading a value from a null pointer is undefined behavior in Rust. However, dereferencing a pointer does not always cause a load, it only creates a place that can then be implicitly coerced to a value.
In examples 1 and 2 the dereference produces a place which is immediately discarded by the assignment to _
(note that parenthesis do not affect anything other than precedence of operators).
In example 3 the place created by the dereference is coerced to a value because (...,)
creates a single element tuple (note the ,
!).
In examples 4 and 5 the place created by the dereference is coerced to a value, because it is returned from a block (note that normal and unsafe
blocks behave the same) which causes UB.
To learn more about differences between places and values read an article by Ralf Jung, "What is a place expression?".