From 022681964901a8fee3488ffc2a0e4bf8b473e514 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 8 Aug 2026 13:45:20 +0200 Subject: [PATCH] MaybeDangling: ensure references fit inside the address space --- .../src/interpret/validity.rs | 48 +++++++++++++------ .../validity/maybe_dangling_ref_too_big.rs | 7 +++ .../maybe_dangling_ref_too_big.stderr | 13 +++++ 3 files changed, 54 insertions(+), 14 deletions(-) create mode 100644 src/tools/miri/tests/fail/validity/maybe_dangling_ref_too_big.rs create mode 100644 src/tools/miri/tests/fail/validity/maybe_dangling_ref_too_big.stderr diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 328b8b83a947f..d0bcc52fc9734 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -647,6 +647,25 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { None } } else { + // We are not checking dereferenceability, but we still want to ensure that the pointer + // *could* be dereferenceable in *some* memory: we have to be able to compute the + // address at the end of this range without overflowing.. + let scalar = Scalar::from_maybe_pointer(place.ptr(), self.ecx); + // Skip this if we don't know the absolute address (during CTFE). + if let Ok(addr) = scalar.try_to_scalar_int() { + // Try to compute the end address. + let addr = Size::from_bytes(addr.to_target_usize(*self.ecx.tcx)); + if addr.checked_add(size, self.ecx).is_none() { + throw_validation_failure!( + self.path, + format!( + "encountered a {ptr_kind} that is too close to the end of the address space for a pointee of {} bytes", + size.bytes(), + ) + ) + } + } + // Pointer remains unchanged. None }; @@ -658,20 +677,6 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { self.reset_pointer_provenance(value, &ptr)?; } - // Check alignment after dereferenceable (if both are violated, trigger the error above). - try_validation!( - self.ecx.check_ptr_align( - place.ptr(), - align, - ), - self.path, - Ub(AlignmentCheckFailed(Misalignment { required, has }, _msg)) => format!( - "encountered an unaligned {ptr_kind} (required {required_bytes} byte alignment but found {found_bytes})", - required_bytes = required.bytes(), - found_bytes = has.bytes() - ), - ); - // Make sure this is non-null. This is obviously needed when `may_dangle` is set, // but even if we did check dereferenceability above that would still allow null // pointers if `size` is zero. @@ -686,6 +691,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { ) ) } + // Do not allow references to uninhabited types. if !place.layout.ty.is_opsem_inhabited(*self.ecx.tcx, self.ecx.typing_env) { let ty = place.layout.ty; @@ -695,6 +701,20 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { ) } + // Check alignment after dereferenceable (if both are violated, trigger the error above). + try_validation!( + self.ecx.check_ptr_align( + place.ptr(), + align, + ), + self.path, + Ub(AlignmentCheckFailed(Misalignment { required, has }, _msg)) => format!( + "encountered an unaligned {ptr_kind} (required {required_bytes} byte alignment but found {found_bytes})", + required_bytes = required.bytes(), + found_bytes = has.bytes() + ), + ); + // Recursive checking (but not inside `MaybeDangling` of course). if let Some(ref_tracking) = self.ref_tracking.as_deref_mut() && !self.may_dangle diff --git a/src/tools/miri/tests/fail/validity/maybe_dangling_ref_too_big.rs b/src/tools/miri/tests/fail/validity/maybe_dangling_ref_too_big.rs new file mode 100644 index 0000000000000..350e46a31df64 --- /dev/null +++ b/src/tools/miri/tests/fail/validity/maybe_dangling_ref_too_big.rs @@ -0,0 +1,7 @@ +#![feature(maybe_dangling)] +use std::mem::{transmute, MaybeDangling}; + +fn main() { + let _x: MaybeDangling<&i8> = unsafe { transmute(usize::MAX) }; + //~^ERROR: too close to the end of the address space +} diff --git a/src/tools/miri/tests/fail/validity/maybe_dangling_ref_too_big.stderr b/src/tools/miri/tests/fail/validity/maybe_dangling_ref_too_big.stderr new file mode 100644 index 0000000000000..f0966586d4dc7 --- /dev/null +++ b/src/tools/miri/tests/fail/validity/maybe_dangling_ref_too_big.stderr @@ -0,0 +1,13 @@ +error: Undefined Behavior: constructing invalid value of type std::mem::MaybeDangling<&i8>: encountered a reference that is too close to the end of the address space for a pointee of 1 bytes + --> tests/fail/validity/maybe_dangling_ref_too_big.rs:LL:CC + | +LL | let _x: MaybeDangling<&i8> = unsafe { transmute(usize::MAX) }; + | ^^^^^^^^^^^^^^^^^^^^^ 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: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error +