Null Pointer Derefs @WaffleLapkin @BoxyUwU @m-ou-se @Victoronz
fn main() {
// 1. UB?
unsafe {
_ = *std::ptr::null::<u32>();
}
}
fn main() {
// 2. UB?
unsafe {
_ = (*std::ptr::null::<u32>());
}
}
fn main() {
// 3. UB?
unsafe {
_ = (*std::ptr::null::<u32>(),);
}
}
fn main() {
// 4. UB?
unsafe {
_ = { *std::ptr::null::<u32>() };
}
}
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: attempting to access 4 bytes, but got null pointer
--> examples/unsafe_1_3.rs:7:14
|
7 | _ = (*std::ptr::null::<u32>(),);
| ^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
|
= 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:7:14: 7: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: attempting to access 4 bytes, but got null pointer
--> examples/unsafe_1_4.rs:7:15
|
7 | _ = { *std::ptr::null::<u32>() };
| ^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
|
= 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:7:15: 7: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: attempting to access 4 bytes, but got null pointer
--> examples/unsafe_1_5.rs:6:18
|
6 | _ = unsafe { *std::ptr::null::<u32>() };
| ^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
|
= 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:6:18: 6: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?".