2025-03-09 14:38:16 +01:00
|
|
|
// check-fail
|
|
|
|
|
// run-rustfix
|
2025-03-31 15:50:56 +07:00
|
|
|
#![allow(unnecessary_transmutes)]
|
2025-03-09 14:38:16 +01:00
|
|
|
|
2025-03-31 15:50:56 +07:00
|
|
|
use std::{mem, ptr};
|
2025-03-09 14:38:16 +01:00
|
|
|
|
|
|
|
|
unsafe fn null_ptr() {
|
|
|
|
|
ptr::write(
|
2025-03-31 15:50:56 +07:00
|
|
|
//~^ ERROR calling this function with a null pointer is undefined behavior
|
2025-03-09 14:38:16 +01:00
|
|
|
ptr::null_mut() as *mut u32,
|
|
|
|
|
mem::transmute::<[u8; 4], _>([0, 0, 0, 255]),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let null_ptr = ptr::null_mut();
|
|
|
|
|
ptr::write(
|
2025-03-31 15:50:56 +07:00
|
|
|
//~^ ERROR calling this function with a null pointer is undefined behavior
|
2025-03-09 14:38:16 +01:00
|
|
|
null_ptr as *mut u32,
|
|
|
|
|
mem::transmute::<[u8; 4], _>([0, 0, 0, 255]),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let _: &[usize] = std::slice::from_raw_parts(ptr::null(), 0);
|
|
|
|
|
//~^ ERROR calling this function with a null pointer is undefined behavior
|
|
|
|
|
let _: &[usize] = std::slice::from_raw_parts(ptr::null_mut(), 0);
|
|
|
|
|
//~^ ERROR calling this function with a null pointer is undefined behavior
|
|
|
|
|
let _: &[usize] = std::slice::from_raw_parts(0 as *mut _, 0);
|
|
|
|
|
//~^ ERROR calling this function with a null pointer is undefined behavior
|
|
|
|
|
let _: &[usize] = std::slice::from_raw_parts(mem::transmute(0usize), 0);
|
|
|
|
|
//~^ ERROR calling this function with a null pointer is undefined behavior
|
|
|
|
|
|
|
|
|
|
let _: &[usize] = std::slice::from_raw_parts_mut(ptr::null_mut(), 0);
|
|
|
|
|
//~^ ERROR calling this function with a null pointer is undefined behavior
|
|
|
|
|
|
|
|
|
|
ptr::copy::<usize>(ptr::null(), ptr::NonNull::dangling().as_ptr(), 0);
|
|
|
|
|
//~^ ERROR calling this function with a null pointer is undefined behavior
|
|
|
|
|
ptr::copy::<usize>(ptr::NonNull::dangling().as_ptr(), ptr::null_mut(), 0);
|
|
|
|
|
//~^ ERROR calling this function with a null pointer is undefined behavior
|
|
|
|
|
|
|
|
|
|
ptr::copy_nonoverlapping::<usize>(ptr::null(), ptr::NonNull::dangling().as_ptr(), 0);
|
|
|
|
|
//~^ ERROR calling this function with a null pointer is undefined behavior
|
|
|
|
|
ptr::copy_nonoverlapping::<usize>(
|
2025-03-31 15:50:56 +07:00
|
|
|
//~^ ERROR calling this function with a null pointer is undefined behavior
|
2025-03-09 14:38:16 +01:00
|
|
|
ptr::NonNull::dangling().as_ptr(),
|
|
|
|
|
ptr::null_mut(),
|
2025-03-31 15:50:56 +07:00
|
|
|
0,
|
2025-03-09 14:38:16 +01:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
|
struct A(usize);
|
|
|
|
|
let mut v = A(200);
|
|
|
|
|
|
|
|
|
|
let _a: A = ptr::read(ptr::null());
|
|
|
|
|
//~^ ERROR calling this function with a null pointer is undefined behavior
|
|
|
|
|
let _a: A = ptr::read(ptr::null_mut());
|
|
|
|
|
//~^ ERROR calling this function with a null pointer is undefined behavior
|
|
|
|
|
|
|
|
|
|
let _a: A = ptr::read_unaligned(ptr::null());
|
|
|
|
|
//~^ ERROR calling this function with a null pointer is undefined behavior
|
|
|
|
|
let _a: A = ptr::read_unaligned(ptr::null_mut());
|
|
|
|
|
//~^ ERROR calling this function with a null pointer is undefined behavior
|
|
|
|
|
|
fix: don't panic on volatile access to null
According to
https://discourse.llvm.org/t/rfc-volatile-access-to-non-dereferenceable-memory-may-be-well-defined/86303/4,
LLVM allows volatile operations on null and handles it correctly. This
should be allowed in Rust as well, because I/O memory may be hard-coded
to address 0 in some cases, like the AVR chip ATtiny1626.
A test case that ensured a failure when passing null to volatile was
removed, since it's now valid.
Due to the addition of `maybe_is_aligned` to `ub_checks`,
`maybe_is_aligned_and_not_null` was refactored to use it.
docs: revise restrictions on volatile operations
A distinction between usage on Rust memory vs. non-Rust memory was
introduced. Documentation was reworded to explain what that means, and
make explicit that:
- No trapping can occur from volatile operations;
- On Rust memory, all safety rules must be respected;
- On Rust memory, the primary difference from regular access is that
volatile always involves a memory dereference;
- On Rust memory, the only data affected by an operation is the one
pointed to in the argument(s) of the function;
- On Rust memory, provenance follows the same rules as non-volatile
access;
- On non-Rust memory, any address known to not contain Rust memory is
valid (including 0 and usize::MAX);
- On non-Rust memory, no Rust memory may be affected (it is implicit
that any other non-Rust memory may be affected, though, even if not
referenced by the pointer). This should be relevant when, for example,
reading register A causes a flag to change in register B, or writing
to A causes B to change in some way. Everything affected mustn't be
inside an allocation.
- On non-Rust memory, provenance is irrelevant and a pointer with none
can be used in a valid way.
fix: don't lint null as UB for volatile
Also remove a now-unneeded `allow` line.
fix: additional wording nits
2025-04-20 18:43:54 -03:00
|
|
|
// These two should *not* fire the lint.
|
2025-03-09 14:38:16 +01:00
|
|
|
let _a: A = ptr::read_volatile(ptr::null());
|
|
|
|
|
let _a: A = ptr::read_volatile(ptr::null_mut());
|
|
|
|
|
|
|
|
|
|
let _a: A = ptr::replace(ptr::null_mut(), v);
|
|
|
|
|
//~^ ERROR calling this function with a null pointer is undefined behavior
|
|
|
|
|
|
|
|
|
|
ptr::swap::<A>(ptr::null_mut(), &mut v);
|
|
|
|
|
//~^ ERROR calling this function with a null pointer is undefined behavior
|
|
|
|
|
ptr::swap::<A>(&mut v, ptr::null_mut());
|
|
|
|
|
//~^ ERROR calling this function with a null pointer is undefined behavior
|
|
|
|
|
|
|
|
|
|
ptr::swap_nonoverlapping::<A>(ptr::null_mut(), &mut v, 0);
|
|
|
|
|
//~^ ERROR calling this function with a null pointer is undefined behavior
|
|
|
|
|
ptr::swap_nonoverlapping::<A>(&mut v, ptr::null_mut(), 0);
|
|
|
|
|
//~^ ERROR calling this function with a null pointer is undefined behavior
|
|
|
|
|
|
|
|
|
|
ptr::write(ptr::null_mut(), v);
|
|
|
|
|
//~^ ERROR calling this function with a null pointer is undefined behavior
|
|
|
|
|
|
|
|
|
|
ptr::write_unaligned(ptr::null_mut(), v);
|
|
|
|
|
//~^ ERROR calling this function with a null pointer is undefined behavior
|
|
|
|
|
|
fix: don't panic on volatile access to null
According to
https://discourse.llvm.org/t/rfc-volatile-access-to-non-dereferenceable-memory-may-be-well-defined/86303/4,
LLVM allows volatile operations on null and handles it correctly. This
should be allowed in Rust as well, because I/O memory may be hard-coded
to address 0 in some cases, like the AVR chip ATtiny1626.
A test case that ensured a failure when passing null to volatile was
removed, since it's now valid.
Due to the addition of `maybe_is_aligned` to `ub_checks`,
`maybe_is_aligned_and_not_null` was refactored to use it.
docs: revise restrictions on volatile operations
A distinction between usage on Rust memory vs. non-Rust memory was
introduced. Documentation was reworded to explain what that means, and
make explicit that:
- No trapping can occur from volatile operations;
- On Rust memory, all safety rules must be respected;
- On Rust memory, the primary difference from regular access is that
volatile always involves a memory dereference;
- On Rust memory, the only data affected by an operation is the one
pointed to in the argument(s) of the function;
- On Rust memory, provenance follows the same rules as non-volatile
access;
- On non-Rust memory, any address known to not contain Rust memory is
valid (including 0 and usize::MAX);
- On non-Rust memory, no Rust memory may be affected (it is implicit
that any other non-Rust memory may be affected, though, even if not
referenced by the pointer). This should be relevant when, for example,
reading register A causes a flag to change in register B, or writing
to A causes B to change in some way. Everything affected mustn't be
inside an allocation.
- On non-Rust memory, provenance is irrelevant and a pointer with none
can be used in a valid way.
fix: don't lint null as UB for volatile
Also remove a now-unneeded `allow` line.
fix: additional wording nits
2025-04-20 18:43:54 -03:00
|
|
|
// This one should *not* fire the lint.
|
2025-03-09 14:38:16 +01:00
|
|
|
ptr::write_volatile(ptr::null_mut(), v);
|
|
|
|
|
|
|
|
|
|
ptr::write_bytes::<usize>(ptr::null_mut(), 42, 0);
|
|
|
|
|
//~^ ERROR calling this function with a null pointer is undefined behavior
|
|
|
|
|
|
|
|
|
|
// with indirections
|
|
|
|
|
let const_ptr = null_ptr as *const u8;
|
|
|
|
|
let _a: u8 = ptr::read(const_ptr);
|
|
|
|
|
//~^ ERROR calling this function with a null pointer is undefined behavior
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsafe fn zst() {
|
|
|
|
|
struct Zst; // zero-sized type
|
|
|
|
|
|
|
|
|
|
std::slice::from_raw_parts::<()>(ptr::null(), 0);
|
|
|
|
|
//~^ ERROR calling this function with a null pointer is undefined behavior
|
|
|
|
|
std::slice::from_raw_parts::<Zst>(ptr::null(), 0);
|
|
|
|
|
//~^ ERROR calling this function with a null pointer is undefined behavior
|
|
|
|
|
std::slice::from_raw_parts_mut::<()>(ptr::null_mut(), 0);
|
|
|
|
|
//~^ ERROR calling this function with a null pointer is undefined behavior
|
|
|
|
|
std::slice::from_raw_parts_mut::<Zst>(ptr::null_mut(), 0);
|
|
|
|
|
//~^ ERROR calling this function with a null pointer is undefined behavior
|
|
|
|
|
|
|
|
|
|
ptr::read::<()>(ptr::null());
|
|
|
|
|
ptr::read::<Zst>(ptr::null());
|
|
|
|
|
|
|
|
|
|
ptr::write(ptr::null_mut(), ());
|
|
|
|
|
ptr::write(ptr::null_mut(), Zst);
|
|
|
|
|
|
|
|
|
|
ptr::copy(ptr::null::<()>(), ptr::null_mut::<()>(), 1);
|
|
|
|
|
ptr::copy(ptr::null::<Zst>(), ptr::null_mut::<Zst>(), 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsafe fn not_invalid() {
|
|
|
|
|
// Simplified false-positive from std quicksort implementation
|
|
|
|
|
|
|
|
|
|
let mut a = ptr::null_mut();
|
|
|
|
|
let mut b = ();
|
|
|
|
|
|
|
|
|
|
loop {
|
|
|
|
|
if false {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
a = &raw mut b;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ptr::write(a, ());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() {}
|