1
Fork 0

Auto merge of #134424 - 1c3t3a:null-checks, r=saethlin

Insert null checks for pointer dereferences when debug assertions are enabled

Similar to how the alignment is already checked, this adds a check
for null pointer dereferences in debug mode. It is implemented similarly
to the alignment check as a `MirPass`.

This inserts checks in the same places as the `CheckAlignment` pass and additionally
also inserts checks for `Borrows`, so code like
```rust
let ptr: *const u32 = std::ptr::null();
let val: &u32 = unsafe { &*ptr };
```
will have a check inserted on dereference. This is done because null references
are UB. The alignment check doesn't cover these places, because in `&(*ptr).field`,
the exact requirement is that the final reference must be aligned. This is something to
consider further enhancements of the alignment check.

For now this is implemented as a separate `MirPass`, to make it easy to disable
this check if necessary.

This is related to a 2025H1 project goal for better UB checks in debug
mode: https://github.com/rust-lang/rust-project-goals/pull/177.

r? `@saethlin`
This commit is contained in:
bors 2025-01-31 15:56:53 +00:00
commit aa4cfd0809
33 changed files with 551 additions and 164 deletions

View file

@ -17,6 +17,9 @@ middle_assert_gen_resume_after_panic = `gen` fn or block cannot be further itera
middle_assert_misaligned_ptr_deref =
misaligned pointer dereference: address must be a multiple of {$required} but is {$found}
middle_assert_null_ptr_deref =
null pointer dereference occurred
middle_assert_op_overflow =
attempt to compute `{$left} {$op} {$right}`, which would overflow

View file

@ -1076,6 +1076,7 @@ pub enum AssertKind<O> {
ResumedAfterReturn(CoroutineKind),
ResumedAfterPanic(CoroutineKind),
MisalignedPointerDereference { required: O, found: O },
NullPointerDereference,
}
#[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)]

View file

@ -206,6 +206,7 @@ impl<O> AssertKind<O> {
ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
LangItem::PanicGenFnNonePanic
}
NullPointerDereference => LangItem::PanicNullPointerDereference,
BoundsCheck { .. } | MisalignedPointerDereference { .. } => {
bug!("Unexpected AssertKind")
@ -271,6 +272,7 @@ impl<O> AssertKind<O> {
"\"misaligned pointer dereference: address must be a multiple of {{}} but is {{}}\", {required:?}, {found:?}"
)
}
NullPointerDereference => write!(f, "\"null pointer dereference occured\""),
ResumedAfterReturn(CoroutineKind::Coroutine(_)) => {
write!(f, "\"coroutine resumed after completion\"")
}
@ -341,7 +343,7 @@ impl<O> AssertKind<O> {
ResumedAfterPanic(CoroutineKind::Coroutine(_)) => {
middle_assert_coroutine_resume_after_panic
}
NullPointerDereference => middle_assert_null_ptr_deref,
MisalignedPointerDereference { .. } => middle_assert_misaligned_ptr_deref,
}
}
@ -374,7 +376,7 @@ impl<O> AssertKind<O> {
add!("left", format!("{left:#?}"));
add!("right", format!("{right:#?}"));
}
ResumedAfterReturn(_) | ResumedAfterPanic(_) => {}
ResumedAfterReturn(_) | ResumedAfterPanic(_) | NullPointerDereference => {}
MisalignedPointerDereference { required, found } => {
add!("required", format!("{required:#?}"));
add!("found", format!("{found:#?}"));

View file

@ -636,7 +636,7 @@ macro_rules! make_mir_visitor {
OverflowNeg(op) | DivisionByZero(op) | RemainderByZero(op) => {
self.visit_operand(op, location);
}
ResumedAfterReturn(_) | ResumedAfterPanic(_) => {
ResumedAfterReturn(_) | ResumedAfterPanic(_) | NullPointerDereference => {
// Nothing to visit
}
MisalignedPointerDereference { required, found } => {