diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 836e548ae2b7f..310fd462d5f8c 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -25,9 +25,9 @@ use rustc_target::spec::abi::Abi as CallAbi; use crate::errors::{LongRunning, LongRunningWarn}; use crate::fluent_generated as fluent; use crate::interpret::{ - self, compile_time_machine, err_ub, throw_exhaust, throw_inval, throw_ub_custom, + self, compile_time_machine, err_ub, throw_exhaust, throw_inval, throw_ub_custom, throw_unsup, throw_unsup_format, AllocId, AllocRange, ConstAllocation, CtfeProvenance, FnArg, FnVal, Frame, - ImmTy, InterpCx, InterpResult, MPlaceTy, OpTy, Pointer, PointerArithmetic, Scalar, + GlobalAlloc, ImmTy, InterpCx, InterpResult, MPlaceTy, OpTy, Pointer, PointerArithmetic, Scalar, }; use super::error::*; @@ -759,11 +759,21 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, ecx: &InterpCx<'mir, 'tcx, Self>, alloc_id: AllocId, ) -> InterpResult<'tcx> { + // Check if this is the currently evaluated static. if Some(alloc_id) == ecx.machine.static_root_ids.map(|(id, _)| id) { - Err(ConstEvalErrKind::RecursiveStatic.into()) - } else { - Ok(()) + return Err(ConstEvalErrKind::RecursiveStatic.into()); } + // If this is another static, make sure we fire off the query to detect cycles. + // But only do that when checks for static recursion are enabled. + if ecx.machine.static_root_ids.is_some() { + if let Some(GlobalAlloc::Static(def_id)) = ecx.tcx.try_get_global_alloc(alloc_id) { + if ecx.tcx.is_foreign_item(def_id) { + throw_unsup!(ExternStatic(def_id)); + } + ecx.ctfe_query(|tcx| tcx.eval_static_initializer(def_id))?; + } + } + Ok(()) } } diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index dce4d56f7e007..72dad562695e2 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -255,6 +255,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { name = intrinsic_name, ); } + // This will always return 0. (a, b) } (Err(_), _) | (_, Err(_)) => { diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 350fd480fbaae..737f2fd8bb983 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -413,6 +413,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// to the allocation it points to. Supports both shared and mutable references, as the actual /// checking is offloaded to a helper closure. /// + /// `alloc_size` will only get called for non-zero-sized accesses. + /// /// Returns `None` if and only if the size is 0. fn check_and_deref_ptr( &self, @@ -425,18 +427,19 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { M::ProvenanceExtra, ) -> InterpResult<'tcx, (Size, Align, T)>, ) -> InterpResult<'tcx, Option> { + // Everything is okay with size 0. + if size.bytes() == 0 { + return Ok(None); + } + Ok(match self.ptr_try_get_alloc_id(ptr) { Err(addr) => { - // We couldn't get a proper allocation. This is only okay if the access size is 0, - // and the address is not null. - if size.bytes() > 0 || addr == 0 { - throw_ub!(DanglingIntPointer(addr, msg)); - } - None + // We couldn't get a proper allocation. + throw_ub!(DanglingIntPointer(addr, msg)); } Ok((alloc_id, offset, prov)) => { let (alloc_size, _alloc_align, ret_val) = alloc_size(alloc_id, offset, prov)?; - // Test bounds. This also ensures non-null. + // Test bounds. // It is sufficient to check this for the end pointer. Also check for overflow! if offset.checked_add(size, &self.tcx).map_or(true, |end| end > alloc_size) { throw_ub!(PointerOutOfBounds { @@ -447,14 +450,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { msg, }) } - // Ensure we never consider the null pointer dereferenceable. - if M::Provenance::OFFSET_IS_ADDR { - assert_ne!(ptr.addr(), Size::ZERO); - } - // We can still be zero-sized in this branch, in which case we have to - // return `None`. - if size.bytes() == 0 { None } else { Some(ret_val) } + Some(ret_val) } }) } @@ -641,16 +638,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { size, CheckInAllocMsg::MemoryAccessTest, |alloc_id, offset, prov| { - if !self.memory.validation_in_progress.get() { - // We want to call the hook on *all* accesses that involve an AllocId, - // including zero-sized accesses. That means we have to do it here - // rather than below in the `Some` branch. - M::before_alloc_read(self, alloc_id)?; - } let alloc = self.get_alloc_raw(alloc_id)?; Ok((alloc.size(), alloc.align, (alloc_id, offset, prov, alloc))) }, )?; + // We want to call the hook on *all* accesses that involve an AllocId, including zero-sized + // accesses. That means we cannot rely on the closure above or the `Some` branch below. We + // do this after `check_and_deref_ptr` to ensure some basic sanity has already been checked. + if !self.memory.validation_in_progress.get() { + if let Ok((alloc_id, ..)) = self.ptr_try_get_alloc_id(ptr) { + M::before_alloc_read(self, alloc_id)?; + } + } if let Some((alloc_id, offset, prov, alloc)) = ptr_and_alloc { let range = alloc_range(offset, size); diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 2bd4d9dc07a51..e36d30184c858 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -434,6 +434,11 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' found_bytes: has.bytes() }, ); + // Make sure this is non-null. We checked dereferenceability above, but if `size` is zero + // that does not imply non-null. + if self.ecx.scalar_may_be_null(Scalar::from_maybe_pointer(place.ptr(), self.ecx))? { + throw_validation_failure!(self.path, NullPtr { ptr_kind }) + } // Do not allow pointers to uninhabited types. if place.layout.abi.is_uninhabited() { let ty = place.layout.ty; @@ -456,8 +461,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' // `!` is a ZST and we want to validate it. if let Ok((alloc_id, _offset, _prov)) = self.ecx.ptr_try_get_alloc_id(place.ptr()) { let mut skip_recursive_check = false; - let alloc_actual_mutbl = mutability(self.ecx, alloc_id); - if let GlobalAlloc::Static(did) = self.ecx.tcx.global_alloc(alloc_id) { + if let Some(GlobalAlloc::Static(did)) = self.ecx.tcx.try_get_global_alloc(alloc_id) + { let DefKind::Static { nested, .. } = self.ecx.tcx.def_kind(did) else { bug!() }; // Special handling for pointers to statics (irrespective of their type). assert!(!self.ecx.tcx.is_thread_local_static(did)); @@ -495,6 +500,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' // If this allocation has size zero, there is no actual mutability here. let (size, _align, _alloc_kind) = self.ecx.get_alloc_info(alloc_id); if size != Size::ZERO { + let alloc_actual_mutbl = mutability(self.ecx, alloc_id); // Mutable pointer to immutable memory is no good. if ptr_expected_mutbl == Mutability::Mut && alloc_actual_mutbl == Mutability::Not @@ -831,6 +837,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> trace!("visit_value: {:?}, {:?}", *op, op.layout); // Check primitive types -- the leaves of our recursive descent. + // We assume that the Scalar validity range does not restrict these values + // any further than `try_visit_primitive` does! if self.try_visit_primitive(op)? { return Ok(()); } diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index d58e1dbb3e4d0..8e468a8ec3dd6 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -1483,10 +1483,10 @@ extern "rust-intrinsic" { /// /// # Safety /// - /// Both the starting and resulting pointer must be either in bounds or one - /// byte past the end of an allocated object. If either pointer is out of - /// bounds or arithmetic overflow occurs then any further use of the - /// returned value will result in undefined behavior. + /// If the computed offset is non-zero, then both the starting and resulting pointer must be + /// either in bounds or at the end of an allocated object. If either pointer is out + /// of bounds or arithmetic overflow occurs then any further use of the returned value will + /// result in undefined behavior. /// /// The stabilized version of this intrinsic is [`pointer::offset`]. #[must_use = "returns a new pointer rather than modifying its argument"] @@ -1502,7 +1502,7 @@ extern "rust-intrinsic" { /// # Safety /// /// Unlike the `offset` intrinsic, this intrinsic does not restrict the - /// resulting pointer to point into or one byte past the end of an allocated + /// resulting pointer to point into or at the end of an allocated /// object, and it wraps with two's complement arithmetic. The resulting /// value is not necessarily valid to be used to actually access memory. /// diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 73bb256518d89..c8065b2e70906 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -465,8 +465,9 @@ impl *const T { /// If any of the following conditions are violated, the result is Undefined /// Behavior: /// - /// * Both the starting and resulting pointer must be either in bounds or one - /// byte past the end of the same [allocated object]. + /// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting + /// pointer must be either in bounds or at the end of the same [allocated object]. + /// (If it is zero, then the function is always well-defined.) /// /// * The computed offset, **in bytes**, cannot overflow an `isize`. /// @@ -676,11 +677,11 @@ impl *const T { /// If any of the following conditions are violated, the result is Undefined /// Behavior: /// - /// * Both `self` and `origin` must be either in bounds or one - /// byte past the end of the same [allocated object]. + /// * `self` and `origin` must either /// - /// * Both pointers must be *derived from* a pointer to the same object. - /// (See below for an example.) + /// * both be *derived from* a pointer to the same [allocated object], and the memory range between + /// the two pointers must be either empty or in bounds of that object. (See below for an example.) + /// * or both be derived from an integer literal/constant, and point to the same address. /// /// * The distance between the pointers, in bytes, must be an exact multiple /// of the size of `T`. @@ -951,8 +952,9 @@ impl *const T { /// If any of the following conditions are violated, the result is Undefined /// Behavior: /// - /// * Both the starting and resulting pointer must be either in bounds or one - /// byte past the end of the same [allocated object]. + /// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting + /// pointer must be either in bounds or at the end of the same [allocated object]. + /// (If it is zero, then the function is always well-defined.) /// /// * The computed offset, **in bytes**, cannot overflow an `isize`. /// @@ -1035,8 +1037,9 @@ impl *const T { /// If any of the following conditions are violated, the result is Undefined /// Behavior: /// - /// * Both the starting and resulting pointer must be either in bounds or one - /// byte past the end of the same [allocated object]. + /// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting + /// pointer must be either in bounds or at the end of the same [allocated object]. + /// (If it is zero, then the function is always well-defined.) /// /// * The computed offset cannot exceed `isize::MAX` **bytes**. /// diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index f87b6bbfcfb26..d2bbdc84d4dd1 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -15,18 +15,13 @@ //! The precise rules for validity are not determined yet. The guarantees that are //! provided at this point are very minimal: //! -//! * A [null] pointer is *never* valid, not even for accesses of [size zero][zst]. +//! * For operations of [size zero][zst], *every* pointer is valid, including the [null] pointer. +//! The following points are only concerned with non-zero-sized accesses. +//! * A [null] pointer is *never* valid. //! * For a pointer to be valid, it is necessary, but not always sufficient, that the pointer //! be *dereferenceable*: the memory range of the given size starting at the pointer must all be //! within the bounds of a single allocated object. Note that in Rust, //! every (stack-allocated) variable is considered a separate allocated object. -//! * Even for operations of [size zero][zst], the pointer must not be pointing to deallocated -//! memory, i.e., deallocation makes pointers invalid even for zero-sized operations. However, -//! casting any non-zero integer *literal* to a pointer is valid for zero-sized accesses, even if -//! some memory happens to exist at that address and gets deallocated. This corresponds to writing -//! your own allocator: allocating zero-sized objects is not very hard. The canonical way to -//! obtain a pointer that is valid for zero-sized accesses is [`NonNull::dangling`]. -//FIXME: mention `ptr::dangling` above, once it is stable. //! * All accesses performed by functions in this module are *non-atomic* in the sense //! of [atomic operations] used to synchronize between threads. This means it is //! undefined behavior to perform two concurrent accesses to the same location from different diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 7856a1d858177..c53953400addd 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -480,8 +480,9 @@ impl *mut T { /// If any of the following conditions are violated, the result is Undefined /// Behavior: /// - /// * Both the starting and resulting pointer must be either in bounds or one - /// byte past the end of the same [allocated object]. + /// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting + /// pointer must be either in bounds or at the end of the same [allocated object]. + /// (If it is zero, then the function is always well-defined.) /// /// * The computed offset, **in bytes**, cannot overflow an `isize`. /// @@ -904,11 +905,11 @@ impl *mut T { /// If any of the following conditions are violated, the result is Undefined /// Behavior: /// - /// * Both `self` and `origin` must be either in bounds or one - /// byte past the end of the same [allocated object]. + /// * `self` and `origin` must either /// - /// * Both pointers must be *derived from* a pointer to the same object. - /// (See below for an example.) + /// * both be *derived from* a pointer to the same [allocated object], and the memory range between + /// the two pointers must be either empty or in bounds of that object. (See below for an example.) + /// * or both be derived from an integer literal/constant, and point to the same address. /// /// * The distance between the pointers, in bytes, must be an exact multiple /// of the size of `T`. @@ -1095,8 +1096,9 @@ impl *mut T { /// If any of the following conditions are violated, the result is Undefined /// Behavior: /// - /// * Both the starting and resulting pointer must be either in bounds or one - /// byte past the end of the same [allocated object]. + /// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting + /// pointer must be either in bounds or at the end of the same [allocated object]. + /// (If it is zero, then the function is always well-defined.) /// /// * The computed offset, **in bytes**, cannot overflow an `isize`. /// @@ -1179,8 +1181,9 @@ impl *mut T { /// If any of the following conditions are violated, the result is Undefined /// Behavior: /// - /// * Both the starting and resulting pointer must be either in bounds or one - /// byte past the end of the same [allocated object]. + /// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting + /// pointer must be either in bounds or at the end of the same [allocated object]. + /// (If it is zero, then the function is always well-defined.) /// /// * The computed offset cannot exceed `isize::MAX` **bytes**. /// diff --git a/src/tools/miri/tests/fail/dangling_pointers/dangling_zst_deref.rs b/src/tools/miri/tests/fail/dangling_pointers/dangling_zst_deref.rs deleted file mode 100644 index a1fefe04ab69c..0000000000000 --- a/src/tools/miri/tests/fail/dangling_pointers/dangling_zst_deref.rs +++ /dev/null @@ -1,10 +0,0 @@ -// Make sure we find these even with many checks disabled. -//@compile-flags: -Zmiri-disable-alignment-check -Zmiri-disable-stacked-borrows -Zmiri-disable-validation - -fn main() { - let p = { - let b = Box::new(42); - &*b as *const i32 as *const () - }; - let _x = unsafe { *p }; //~ ERROR: has been freed -} diff --git a/src/tools/miri/tests/fail/dangling_pointers/dangling_zst_deref.stderr b/src/tools/miri/tests/fail/dangling_pointers/dangling_zst_deref.stderr deleted file mode 100644 index 72b9a4a2d6cab..0000000000000 --- a/src/tools/miri/tests/fail/dangling_pointers/dangling_zst_deref.stderr +++ /dev/null @@ -1,25 +0,0 @@ -error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling - --> $DIR/dangling_zst_deref.rs:LL:CC - | -LL | let _x = unsafe { *p }; - | ^^ memory access failed: ALLOC has been freed, so this pointer is dangling - | - = 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 -help: ALLOC was allocated here: - --> $DIR/dangling_zst_deref.rs:LL:CC - | -LL | let b = Box::new(42); - | ^^^^^^^^^^^^ -help: ALLOC was deallocated here: - --> $DIR/dangling_zst_deref.rs:LL:CC - | -LL | }; - | ^ - = note: BACKTRACE (of the first span): - = note: inside `main` at $DIR/dangling_zst_deref.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_deref_zst.rs b/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_deref_zst.rs deleted file mode 100644 index 73d0b12068013..0000000000000 --- a/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_deref_zst.rs +++ /dev/null @@ -1,5 +0,0 @@ -fn main() { - // This pointer *could* be NULL so we cannot load from it, not even at ZST - let ptr = (&0u8 as *const u8).wrapping_sub(0x800) as *const (); - let _x: () = unsafe { *ptr }; //~ ERROR: out-of-bounds -} diff --git a/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_deref_zst.stderr b/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_deref_zst.stderr deleted file mode 100644 index 13c53e20b8a64..0000000000000 --- a/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_deref_zst.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: Undefined Behavior: memory access failed: ALLOC has size 1, so pointer at offset -2048 is out-of-bounds - --> $DIR/maybe_null_pointer_deref_zst.rs:LL:CC - | -LL | let _x: () = unsafe { *ptr }; - | ^^^^ memory access failed: ALLOC has size 1, so pointer at offset -2048 is out-of-bounds - | - = 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 $DIR/maybe_null_pointer_deref_zst.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_write_zst.rs b/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_write_zst.rs deleted file mode 100644 index 5537207ae424f..0000000000000 --- a/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_write_zst.rs +++ /dev/null @@ -1,8 +0,0 @@ -fn main() { - // This pointer *could* be NULL so we cannot load from it, not even at ZST. - // Not using the () type here, as writes of that type do not even have MIR generated. - // Also not assigning directly as that's array initialization, not assignment. - let zst_val = [1u8; 0]; - let ptr = (&0u8 as *const u8).wrapping_sub(0x800) as *mut [u8; 0]; - unsafe { *ptr = zst_val }; //~ ERROR: out-of-bounds -} diff --git a/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_write_zst.stderr b/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_write_zst.stderr deleted file mode 100644 index e4e23e8ace1ce..0000000000000 --- a/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_write_zst.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: Undefined Behavior: memory access failed: ALLOC has size 1, so pointer at offset -2048 is out-of-bounds - --> $DIR/maybe_null_pointer_write_zst.rs:LL:CC - | -LL | unsafe { *ptr = zst_val }; - | ^^^^^^^^^^^^^^ memory access failed: ALLOC has size 1, so pointer at offset -2048 is out-of-bounds - | - = 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 $DIR/maybe_null_pointer_write_zst.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/miri/tests/fail/dangling_pointers/null_pointer_deref_zst.rs b/src/tools/miri/tests/fail/dangling_pointers/null_pointer_deref_zst.rs deleted file mode 100644 index f8af43ff3529c..0000000000000 --- a/src/tools/miri/tests/fail/dangling_pointers/null_pointer_deref_zst.rs +++ /dev/null @@ -1,5 +0,0 @@ -#[allow(deref_nullptr)] -fn main() { - let x: () = unsafe { *std::ptr::null() }; //~ ERROR: memory access failed: null pointer is a dangling pointer - panic!("this should never print: {:?}", x); -} diff --git a/src/tools/miri/tests/fail/dangling_pointers/null_pointer_deref_zst.stderr b/src/tools/miri/tests/fail/dangling_pointers/null_pointer_deref_zst.stderr deleted file mode 100644 index 1a8794f3ceb7d..0000000000000 --- a/src/tools/miri/tests/fail/dangling_pointers/null_pointer_deref_zst.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance) - --> $DIR/null_pointer_deref_zst.rs:LL:CC - | -LL | let x: () = unsafe { *std::ptr::null() }; - | ^^^^^^^^^^^^^^^^^ 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 $DIR/null_pointer_deref_zst.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/miri/tests/fail/dangling_pointers/null_pointer_write_zst.rs b/src/tools/miri/tests/fail/dangling_pointers/null_pointer_write_zst.rs deleted file mode 100644 index edd6c8fadce46..0000000000000 --- a/src/tools/miri/tests/fail/dangling_pointers/null_pointer_write_zst.rs +++ /dev/null @@ -1,8 +0,0 @@ -#[allow(deref_nullptr)] -fn main() { - // Not using the () type here, as writes of that type do not even have MIR generated. - // Also not assigning directly as that's array initialization, not assignment. - let zst_val = [1u8; 0]; - unsafe { std::ptr::null_mut::<[u8; 0]>().write(zst_val) }; - //~^ERROR: memory access failed: null pointer is a dangling pointer -} diff --git a/src/tools/miri/tests/fail/dangling_pointers/null_pointer_write_zst.stderr b/src/tools/miri/tests/fail/dangling_pointers/null_pointer_write_zst.stderr deleted file mode 100644 index 1d4704e2a0e89..0000000000000 --- a/src/tools/miri/tests/fail/dangling_pointers/null_pointer_write_zst.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance) - --> $DIR/null_pointer_write_zst.rs:LL:CC - | -LL | unsafe { std::ptr::null_mut::<[u8; 0]>().write(zst_val) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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 $DIR/null_pointer_write_zst.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/miri/tests/fail/intrinsics/copy_null.rs b/src/tools/miri/tests/fail/intrinsics/copy_null.rs deleted file mode 100644 index 237e517f2875a..0000000000000 --- a/src/tools/miri/tests/fail/intrinsics/copy_null.rs +++ /dev/null @@ -1,15 +0,0 @@ -#![feature(intrinsics)] - -// Directly call intrinsic to avoid debug assertions in libstd -extern "rust-intrinsic" { - fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize); -} - -fn main() { - let mut data = [0u16; 4]; - let ptr = &mut data[0] as *mut u16; - // Even copying 0 elements from NULL should error. - unsafe { - copy_nonoverlapping(std::ptr::null(), ptr, 0); //~ ERROR: memory access failed: null pointer is a dangling pointer - } -} diff --git a/src/tools/miri/tests/fail/intrinsics/copy_null.stderr b/src/tools/miri/tests/fail/intrinsics/copy_null.stderr deleted file mode 100644 index d73c03475d69d..0000000000000 --- a/src/tools/miri/tests/fail/intrinsics/copy_null.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance) - --> $DIR/copy_null.rs:LL:CC - | -LL | copy_nonoverlapping(std::ptr::null(), ptr, 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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 $DIR/copy_null.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_0_plus_0.rs b/src/tools/miri/tests/fail/intrinsics/ptr_offset_0_plus_0.rs deleted file mode 100644 index e2329c1313984..0000000000000 --- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_0_plus_0.rs +++ /dev/null @@ -1,9 +0,0 @@ -//@compile-flags: -Zmiri-permissive-provenance - -#[rustfmt::skip] // fails with "left behind trailing whitespace" -fn main() { - let x = 0 as *mut i32; - let _x = x.wrapping_offset(8); // ok, this has no inbounds tag - let _x = unsafe { x.offset(0) }; // UB despite offset 0, NULL is never inbounds - //~^ERROR: null pointer is a dangling pointer -} diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_0_plus_0.stderr b/src/tools/miri/tests/fail/intrinsics/ptr_offset_0_plus_0.stderr deleted file mode 100644 index a8984c7fa1670..0000000000000 --- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_0_plus_0.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: Undefined Behavior: out-of-bounds pointer arithmetic: null pointer is a dangling pointer (it has no provenance) - --> $DIR/ptr_offset_0_plus_0.rs:LL:CC - | -LL | let _x = unsafe { x.offset(0) }; // UB despite offset 0, NULL is never inbounds - | ^^^^^^^^^^^ out-of-bounds pointer arithmetic: 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 $DIR/ptr_offset_0_plus_0.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_oob.rs b/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_oob.rs deleted file mode 100644 index 0e5acf08b2030..0000000000000 --- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_oob.rs +++ /dev/null @@ -1,7 +0,0 @@ -fn main() { - let start_ptr = &4 as *const _ as *const u8; - let length = 10; - let end_ptr = start_ptr.wrapping_add(length); - // Even if the offset is 0, a dangling OOB pointer is not allowed. - unsafe { end_ptr.offset_from(end_ptr) }; //~ERROR: pointer at offset 10 is out-of-bounds -} diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_oob.stderr b/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_oob.stderr deleted file mode 100644 index 32a4461d6bf04..0000000000000 --- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_oob.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: Undefined Behavior: out-of-bounds `offset_from`: ALLOC has size 4, so pointer at offset 10 is out-of-bounds - --> $DIR/ptr_offset_from_oob.rs:LL:CC - | -LL | unsafe { end_ptr.offset_from(end_ptr) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from`: ALLOC has size 4, so pointer at offset 10 is out-of-bounds - | - = 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 $DIR/ptr_offset_from_oob.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_ptr_plus_0.rs b/src/tools/miri/tests/fail/intrinsics/ptr_offset_ptr_plus_0.rs deleted file mode 100644 index 575e28854b1a9..0000000000000 --- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_ptr_plus_0.rs +++ /dev/null @@ -1,7 +0,0 @@ -#[rustfmt::skip] // fails with "left behind trailing whitespace" -fn main() { - let x = Box::into_raw(Box::new(0u32)); - let x = x.wrapping_offset(8); // ok, this has no inbounds tag - let _x = unsafe { x.offset(0) }; // UB despite offset 0, the pointer is not inbounds of the only object it can point to - //~^ERROR: pointer at offset 32 is out-of-bounds -} diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_ptr_plus_0.stderr b/src/tools/miri/tests/fail/intrinsics/ptr_offset_ptr_plus_0.stderr deleted file mode 100644 index 304d362bbb9f9..0000000000000 --- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_ptr_plus_0.stderr +++ /dev/null @@ -1,20 +0,0 @@ -error: Undefined Behavior: out-of-bounds pointer arithmetic: ALLOC has size 4, so pointer at offset 32 is out-of-bounds - --> $DIR/ptr_offset_ptr_plus_0.rs:LL:CC - | -LL | let _x = unsafe { x.offset(0) }; // UB despite offset 0, the pointer is not inbounds of the only object it can point to - | ^^^^^^^^^^^ out-of-bounds pointer arithmetic: ALLOC has size 4, so pointer at offset 32 is out-of-bounds - | - = 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 -help: ALLOC was allocated here: - --> $DIR/ptr_offset_ptr_plus_0.rs:LL:CC - | -LL | let x = Box::into_raw(Box::new(0u32)); - | ^^^^^^^^^^^^^^ - = note: BACKTRACE (of the first span): - = note: inside `main` at $DIR/ptr_offset_ptr_plus_0.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/miri/tests/fail/intrinsics/write_bytes_null.rs b/src/tools/miri/tests/fail/intrinsics/write_bytes_null.rs deleted file mode 100644 index 2f46c820fb73b..0000000000000 --- a/src/tools/miri/tests/fail/intrinsics/write_bytes_null.rs +++ /dev/null @@ -1,10 +0,0 @@ -#![feature(intrinsics)] - -// Directly call intrinsic to avoid debug assertions in libstd -extern "rust-intrinsic" { - fn write_bytes(dst: *mut T, val: u8, count: usize); -} - -fn main() { - unsafe { write_bytes::(std::ptr::null_mut(), 0, 0) }; //~ ERROR: memory access failed: null pointer is a dangling pointer -} diff --git a/src/tools/miri/tests/fail/intrinsics/write_bytes_null.stderr b/src/tools/miri/tests/fail/intrinsics/write_bytes_null.stderr deleted file mode 100644 index def180935cc39..0000000000000 --- a/src/tools/miri/tests/fail/intrinsics/write_bytes_null.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance) - --> $DIR/write_bytes_null.rs:LL:CC - | -LL | unsafe { write_bytes::(std::ptr::null_mut(), 0, 0) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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 $DIR/write_bytes_null.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/miri/tests/fail/zst2.rs b/src/tools/miri/tests/fail/zst2.rs deleted file mode 100644 index 04218c264a3ef..0000000000000 --- a/src/tools/miri/tests/fail/zst2.rs +++ /dev/null @@ -1,12 +0,0 @@ -fn main() { - // Not using the () type here, as writes of that type do not even have MIR generated. - // Also not assigning directly as that's array initialization, not assignment. - let zst_val = [1u8; 0]; - - // make sure ZST accesses are checked against being "truly" dangling pointers - // (into deallocated allocations). - let mut x_box = Box::new(1u8); - let x = &mut *x_box as *mut _ as *mut [u8; 0]; - drop(x_box); - unsafe { *x = zst_val }; //~ ERROR: has been freed -} diff --git a/src/tools/miri/tests/fail/zst2.stderr b/src/tools/miri/tests/fail/zst2.stderr deleted file mode 100644 index b3f65e7866dc8..0000000000000 --- a/src/tools/miri/tests/fail/zst2.stderr +++ /dev/null @@ -1,25 +0,0 @@ -error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling - --> $DIR/zst2.rs:LL:CC - | -LL | unsafe { *x = zst_val }; - | ^^^^^^^^^^^^ memory access failed: ALLOC has been freed, so this pointer is dangling - | - = 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 -help: ALLOC was allocated here: - --> $DIR/zst2.rs:LL:CC - | -LL | let mut x_box = Box::new(1u8); - | ^^^^^^^^^^^^^ -help: ALLOC was deallocated here: - --> $DIR/zst2.rs:LL:CC - | -LL | drop(x_box); - | ^^^^^^^^^^^ - = note: BACKTRACE (of the first span): - = note: inside `main` at $DIR/zst2.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/miri/tests/fail/zst3.rs b/src/tools/miri/tests/fail/zst3.rs deleted file mode 100644 index 454bef25f2234..0000000000000 --- a/src/tools/miri/tests/fail/zst3.rs +++ /dev/null @@ -1,15 +0,0 @@ -fn main() { - // Not using the () type here, as writes of that type do not even have MIR generated. - // Also not assigning directly as that's array initialization, not assignment. - let zst_val = [1u8; 0]; - - // make sure ZST accesses are checked against being "truly" dangling pointers - // (that are out-of-bounds). - let mut x_box = Box::new(1u8); - let x = (&mut *x_box as *mut u8).wrapping_offset(1); - // This one is just "at the edge", but still okay - unsafe { *(x as *mut [u8; 0]) = zst_val }; - // One byte further is OOB. - let x = x.wrapping_offset(1); - unsafe { *(x as *mut [u8; 0]) = zst_val }; //~ ERROR: out-of-bounds -} diff --git a/src/tools/miri/tests/fail/zst3.stderr b/src/tools/miri/tests/fail/zst3.stderr deleted file mode 100644 index b9495fbd2074f..0000000000000 --- a/src/tools/miri/tests/fail/zst3.stderr +++ /dev/null @@ -1,20 +0,0 @@ -error: Undefined Behavior: memory access failed: ALLOC has size 1, so pointer at offset 2 is out-of-bounds - --> $DIR/zst3.rs:LL:CC - | -LL | unsafe { *(x as *mut [u8; 0]) = zst_val }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: ALLOC has size 1, so pointer at offset 2 is out-of-bounds - | - = 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 -help: ALLOC was allocated here: - --> $DIR/zst3.rs:LL:CC - | -LL | let mut x_box = Box::new(1u8); - | ^^^^^^^^^^^^^ - = note: BACKTRACE (of the first span): - = note: inside `main` at $DIR/zst3.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/miri/tests/fail/zst1.rs b/src/tools/miri/tests/fail/zst_local_oob.rs similarity index 100% rename from src/tools/miri/tests/fail/zst1.rs rename to src/tools/miri/tests/fail/zst_local_oob.rs diff --git a/src/tools/miri/tests/fail/zst1.stderr b/src/tools/miri/tests/fail/zst_local_oob.stderr similarity index 88% rename from src/tools/miri/tests/fail/zst1.stderr rename to src/tools/miri/tests/fail/zst_local_oob.stderr index cda837da7e71c..ba1ccaa0a3c8d 100644 --- a/src/tools/miri/tests/fail/zst1.stderr +++ b/src/tools/miri/tests/fail/zst_local_oob.stderr @@ -1,5 +1,5 @@ error: Undefined Behavior: memory access failed: ALLOC has size 0, so pointer to 1 byte starting at offset 0 is out-of-bounds - --> $DIR/zst1.rs:LL:CC + --> $DIR/zst_local_oob.rs:LL:CC | LL | let _val = unsafe { *x }; | ^^ memory access failed: ALLOC has size 0, so pointer to 1 byte starting at offset 0 is out-of-bounds @@ -7,7 +7,7 @@ LL | let _val = unsafe { *x }; = 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 $DIR/zst1.rs:LL:CC + = note: inside `main` at $DIR/zst_local_oob.rs:LL:CC note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/pass/align_offset_symbolic.rs b/src/tools/miri/tests/pass/align_offset_symbolic.rs index dec3d779a789a..9647277821fa1 100644 --- a/src/tools/miri/tests/pass/align_offset_symbolic.rs +++ b/src/tools/miri/tests/pass/align_offset_symbolic.rs @@ -118,10 +118,9 @@ fn vtable() { let parts: (*const (), *const u8) = unsafe { mem::transmute(ptr) }; let vtable = parts.1; let offset = vtable.align_offset(mem::align_of::()); - let _vtable_aligned = vtable.wrapping_add(offset) as *const [TWOPTR; 0]; - // FIXME: we can't actually do the access since vtable pointers act like zero-sized allocations. - // Enable the next line once https://github.com/rust-lang/rust/issues/117945 is implemented. - //let _place = unsafe { &*vtable_aligned }; + let vtable_aligned = vtable.wrapping_add(offset) as *const [TWOPTR; 0]; + // Zero-sized deref, so no in-bounds requirement. + let _place = unsafe { &*vtable_aligned }; } fn main() { diff --git a/src/tools/miri/tests/pass/zero-sized-accesses-and-offsets.rs b/src/tools/miri/tests/pass/zero-sized-accesses-and-offsets.rs new file mode 100644 index 0000000000000..2d142bef73c4a --- /dev/null +++ b/src/tools/miri/tests/pass/zero-sized-accesses-and-offsets.rs @@ -0,0 +1,59 @@ +//! Tests specific for : zero-sized operations. +#![feature(strict_provenance)] + +use std::ptr; + +fn main() { + // Null. + test_ptr(ptr::null_mut::<()>()); + // No provenance. + test_ptr(ptr::without_provenance_mut::<()>(1)); + // Out-of-bounds. + let mut b = Box::new(0i32); + let ptr = ptr::addr_of_mut!(*b) as *mut (); + test_ptr(ptr.wrapping_byte_add(2)); + // Dangling (use-after-free). + drop(b); + test_ptr(ptr); +} + +fn test_ptr(ptr: *mut ()) { + unsafe { + // Reads and writes. + let mut val = *ptr; + *ptr = val; + ptr.read(); + ptr.write(()); + // Memory access intrinsics. + // - memcpy (1st and 2nd argument) + ptr.copy_from_nonoverlapping(&(), 1); + ptr.copy_to_nonoverlapping(&mut val, 1); + // - memmove (1st and 2nd argument) + ptr.copy_from(&(), 1); + ptr.copy_to(&mut val, 1); + // - memset + ptr.write_bytes(0u8, 1); + // Offset. + let _ = ptr.offset(0); + let _ = ptr.offset(1); // this is still 0 bytes + // Distance. + let ptr = ptr.cast::(); + ptr.offset_from(ptr); + /* + FIXME: this is disabled for now as these cases are not yet allowed. + // Distance from other "bad" pointers that have the same address, but different provenance. Some + // of this is library UB, but we don't want it to be language UB since that would violate + // provenance monotonicity: if we allow computing the distance between two ptrs with no + // provenance, we have to allow computing it between two ptrs with arbitrary provenance. + // - Distance from "no provenance" + ptr.offset_from(ptr::without_provenance_mut(ptr.addr())); + // - Distance from out-of-bounds pointer + let mut b = Box::new(0i32); + let other_ptr = ptr::addr_of_mut!(*b); + ptr.offset_from(other_ptr.with_addr(ptr.addr())); + // - Distance from use-after-free pointer + drop(b); + ptr.offset_from(other_ptr.with_addr(ptr.addr())); + */ + } +} diff --git a/tests/ui/const-ptr/forbidden_slices.rs b/tests/ui/const-ptr/forbidden_slices.rs index 85baeedf2215f..2550a3a01096b 100644 --- a/tests/ui/const-ptr/forbidden_slices.rs +++ b/tests/ui/const-ptr/forbidden_slices.rs @@ -12,7 +12,7 @@ use std::{ slice::{from_ptr_range, from_raw_parts}, }; -// Null is never valid for reads +// Null is never valid for references pub static S0: &[u32] = unsafe { from_raw_parts(ptr::null(), 0) }; //~^ ERROR: it is undefined behavior to use this value pub static S1: &[()] = unsafe { from_raw_parts(ptr::null(), 0) }; @@ -46,10 +46,11 @@ pub static S8: &[u64] = unsafe { }; pub static R0: &[u32] = unsafe { from_ptr_range(ptr::null()..ptr::null()) }; -pub static R1: &[()] = unsafe { from_ptr_range(ptr::null()..ptr::null()) }; +//~^ ERROR it is undefined behavior to use this value +pub static R1: &[()] = unsafe { from_ptr_range(ptr::null()..ptr::null()) }; // errors inside libcore pub static R2: &[u32] = unsafe { let ptr = &D0 as *const u32; - from_ptr_range(ptr..ptr.add(2)) + from_ptr_range(ptr..ptr.add(2)) // errors inside libcore }; pub static R4: &[u8] = unsafe { //~^ ERROR: it is undefined behavior to use this value diff --git a/tests/ui/const-ptr/forbidden_slices.stderr b/tests/ui/const-ptr/forbidden_slices.stderr index 250366da2a6ad..eb41a25c818a0 100644 --- a/tests/ui/const-ptr/forbidden_slices.stderr +++ b/tests/ui/const-ptr/forbidden_slices.stderr @@ -88,20 +88,16 @@ LL | pub static S8: &[u64] = unsafe { HEX_DUMP } -error[E0080]: could not evaluate static initializer - --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL - | - = note: out-of-bounds `offset_from`: null pointer is a dangling pointer (it has no provenance) - | -note: inside `std::ptr::const_ptr::::sub_ptr` - --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL -note: inside `from_ptr_range::<'_, u32>` - --> $SRC_DIR/core/src/slice/raw.rs:LL:COL -note: inside `R0` - --> $DIR/forbidden_slices.rs:48:34 +error[E0080]: it is undefined behavior to use this value + --> $DIR/forbidden_slices.rs:48:1 | LL | pub static R0: &[u32] = unsafe { from_ptr_range(ptr::null()..ptr::null()) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a null reference + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { + HEX_DUMP + } error[E0080]: could not evaluate static initializer --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL @@ -113,9 +109,9 @@ note: inside `std::ptr::const_ptr::::sub_ptr` note: inside `from_ptr_range::<'_, ()>` --> $SRC_DIR/core/src/slice/raw.rs:LL:COL note: inside `R1` - --> $DIR/forbidden_slices.rs:49:33 + --> $DIR/forbidden_slices.rs:50:33 | -LL | pub static R1: &[()] = unsafe { from_ptr_range(ptr::null()..ptr::null()) }; +LL | pub static R1: &[()] = unsafe { from_ptr_range(ptr::null()..ptr::null()) }; // errors inside libcore | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -127,13 +123,13 @@ error[E0080]: could not evaluate static initializer note: inside `std::ptr::const_ptr::::add` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL note: inside `R2` - --> $DIR/forbidden_slices.rs:52:25 + --> $DIR/forbidden_slices.rs:53:25 | -LL | from_ptr_range(ptr..ptr.add(2)) +LL | from_ptr_range(ptr..ptr.add(2)) // errors inside libcore | ^^^^^^^^^^ error[E0080]: it is undefined behavior to use this value - --> $DIR/forbidden_slices.rs:54:1 + --> $DIR/forbidden_slices.rs:55:1 | LL | pub static R4: &[u8] = unsafe { | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .[0]: encountered uninitialized memory, but expected an integer @@ -144,7 +140,7 @@ LL | pub static R4: &[u8] = unsafe { } error[E0080]: it is undefined behavior to use this value - --> $DIR/forbidden_slices.rs:59:1 + --> $DIR/forbidden_slices.rs:60:1 | LL | pub static R5: &[u8] = unsafe { | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .[0]: encountered a pointer, but expected an integer @@ -157,7 +153,7 @@ LL | pub static R5: &[u8] = unsafe { = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: it is undefined behavior to use this value - --> $DIR/forbidden_slices.rs:64:1 + --> $DIR/forbidden_slices.rs:65:1 | LL | pub static R6: &[bool] = unsafe { | ^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .[0]: encountered 0x11, but expected a boolean @@ -168,7 +164,7 @@ LL | pub static R6: &[bool] = unsafe { } error[E0080]: it is undefined behavior to use this value - --> $DIR/forbidden_slices.rs:69:1 + --> $DIR/forbidden_slices.rs:70:1 | LL | pub static R7: &[u16] = unsafe { | ^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered an unaligned reference (required 2 byte alignment but found 1) @@ -186,7 +182,7 @@ error[E0080]: could not evaluate static initializer note: inside `std::ptr::const_ptr::::add` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL note: inside `R8` - --> $DIR/forbidden_slices.rs:76:25 + --> $DIR/forbidden_slices.rs:77:25 | LL | from_ptr_range(ptr..ptr.add(1)) | ^^^^^^^^^^ @@ -201,7 +197,7 @@ note: inside `std::ptr::const_ptr::::sub_ptr` note: inside `from_ptr_range::<'_, u32>` --> $SRC_DIR/core/src/slice/raw.rs:LL:COL note: inside `R9` - --> $DIR/forbidden_slices.rs:81:34 + --> $DIR/forbidden_slices.rs:82:34 | LL | pub static R9: &[u32] = unsafe { from_ptr_range(&D0..(&D0 as *const u32).add(1)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -216,7 +212,7 @@ note: inside `std::ptr::const_ptr::::sub_ptr` note: inside `from_ptr_range::<'_, u32>` --> $SRC_DIR/core/src/slice/raw.rs:LL:COL note: inside `R10` - --> $DIR/forbidden_slices.rs:82:35 + --> $DIR/forbidden_slices.rs:83:35 | LL | pub static R10: &[u32] = unsafe { from_ptr_range(&D0..&D0) }; | ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/consts/const-compare-bytes-ub.rs b/tests/ui/consts/const-compare-bytes-ub.rs index b357bab96a4df..903ba15e62291 100644 --- a/tests/ui/consts/const-compare-bytes-ub.rs +++ b/tests/ui/consts/const-compare-bytes-ub.rs @@ -7,11 +7,11 @@ use std::mem::MaybeUninit; fn main() { const LHS_NULL: i32 = unsafe { - compare_bytes(0 as *const u8, 2 as *const u8, 0) + compare_bytes(0 as *const u8, 2 as *const u8, 1) //~^ ERROR evaluation of constant value failed }; const RHS_NULL: i32 = unsafe { - compare_bytes(1 as *const u8, 0 as *const u8, 0) + compare_bytes(1 as *const u8, 0 as *const u8, 1) //~^ ERROR evaluation of constant value failed }; const DANGLING_PTR_NON_ZERO_LENGTH: i32 = unsafe { diff --git a/tests/ui/consts/const-compare-bytes-ub.stderr b/tests/ui/consts/const-compare-bytes-ub.stderr index d8971eb9969f7..9e49706c4c836 100644 --- a/tests/ui/consts/const-compare-bytes-ub.stderr +++ b/tests/ui/consts/const-compare-bytes-ub.stderr @@ -1,14 +1,14 @@ error[E0080]: evaluation of constant value failed --> $DIR/const-compare-bytes-ub.rs:10:9 | -LL | compare_bytes(0 as *const u8, 2 as *const u8, 0) +LL | compare_bytes(0 as *const u8, 2 as *const u8, 1) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance) error[E0080]: evaluation of constant value failed --> $DIR/const-compare-bytes-ub.rs:14:9 | -LL | compare_bytes(1 as *const u8, 0 as *const u8, 0) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance) +LL | compare_bytes(1 as *const u8, 0 as *const u8, 1) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: 0x1[noalloc] is a dangling pointer (it has no provenance) error[E0080]: evaluation of constant value failed --> $DIR/const-compare-bytes-ub.rs:18:9 diff --git a/tests/ui/consts/copy-intrinsic.rs b/tests/ui/consts/copy-intrinsic.rs index 94d7bdc6bae9f..4183dc0fcd6db 100644 --- a/tests/ui/consts/copy-intrinsic.rs +++ b/tests/ui/consts/copy-intrinsic.rs @@ -23,16 +23,20 @@ const COPY_ZERO: () = unsafe { const COPY_OOB_1: () = unsafe { let mut x = 0i32; let dangle = (&mut x as *mut i32).wrapping_add(10); - // Even if the first ptr is an int ptr and this is a ZST copy, we should detect dangling 2nd ptrs. - copy_nonoverlapping(0x100 as *const i32, dangle, 0); //~ ERROR evaluation of constant value failed [E0080] - //~| pointer at offset 40 is out-of-bounds + // Zero-sized copy is fine. + copy_nonoverlapping(0x100 as *const i32, dangle, 0); + // Non-zero-sized copy is not. + copy_nonoverlapping(0x100 as *const i32, dangle, 1); //~ ERROR evaluation of constant value failed [E0080] + //~| 0x100[noalloc] is a dangling pointer }; const COPY_OOB_2: () = unsafe { let x = 0i32; let dangle = (&x as *const i32).wrapping_add(10); - // Even if the second ptr is an int ptr and this is a ZST copy, we should detect dangling 1st ptrs. - copy_nonoverlapping(dangle, 0x100 as *mut i32, 0); //~ ERROR evaluation of constant value failed [E0080] - //~| pointer at offset 40 is out-of-bounds + // Zero-sized copy is fine. + copy_nonoverlapping(dangle, 0x100 as *mut i32, 0); + // Non-zero-sized copy is not. + copy_nonoverlapping(dangle, 0x100 as *mut i32, 1); //~ ERROR evaluation of constant value failed [E0080] + //~| offset 40 is out-of-bounds }; const COPY_SIZE_OVERFLOW: () = unsafe { diff --git a/tests/ui/consts/copy-intrinsic.stderr b/tests/ui/consts/copy-intrinsic.stderr index 0e4e6a6ad6c43..d34e61cd96273 100644 --- a/tests/ui/consts/copy-intrinsic.stderr +++ b/tests/ui/consts/copy-intrinsic.stderr @@ -1,23 +1,23 @@ error[E0080]: evaluation of constant value failed - --> $DIR/copy-intrinsic.rs:27:5 + --> $DIR/copy-intrinsic.rs:29:5 | -LL | copy_nonoverlapping(0x100 as *const i32, dangle, 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: ALLOC0 has size 4, so pointer at offset 40 is out-of-bounds +LL | copy_nonoverlapping(0x100 as *const i32, dangle, 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: 0x100[noalloc] is a dangling pointer (it has no provenance) error[E0080]: evaluation of constant value failed - --> $DIR/copy-intrinsic.rs:34:5 + --> $DIR/copy-intrinsic.rs:38:5 | -LL | copy_nonoverlapping(dangle, 0x100 as *mut i32, 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: ALLOC1 has size 4, so pointer at offset 40 is out-of-bounds +LL | copy_nonoverlapping(dangle, 0x100 as *mut i32, 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: ALLOC0 has size 4, so pointer to 4 bytes starting at offset 40 is out-of-bounds error[E0080]: evaluation of constant value failed - --> $DIR/copy-intrinsic.rs:41:5 + --> $DIR/copy-intrinsic.rs:45:5 | LL | copy(&x, &mut y, 1usize << (mem::size_of::() * 8 - 1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow computing total size of `copy` error[E0080]: evaluation of constant value failed - --> $DIR/copy-intrinsic.rs:47:5 + --> $DIR/copy-intrinsic.rs:51:5 | LL | copy_nonoverlapping(&x, &mut y, 1usize << (mem::size_of::() * 8 - 1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow computing total size of `copy_nonoverlapping` diff --git a/tests/ui/consts/dangling-alloc-id-ice.rs b/tests/ui/consts/dangling-alloc-id-ice.rs index 76d6f33baf34b..6b07b8b3cc80d 100644 --- a/tests/ui/consts/dangling-alloc-id-ice.rs +++ b/tests/ui/consts/dangling-alloc-id-ice.rs @@ -10,7 +10,7 @@ union Foo<'a> { } const FOO: &() = { - //~^ ERROR it is undefined behavior to use this value + //~^ ERROR encountered dangling pointer let y = (); unsafe { Foo { y: &y }.long_live_the_unit } }; diff --git a/tests/ui/consts/dangling-alloc-id-ice.stderr b/tests/ui/consts/dangling-alloc-id-ice.stderr index 881c0b162edca..de31acf9fa441 100644 --- a/tests/ui/consts/dangling-alloc-id-ice.stderr +++ b/tests/ui/consts/dangling-alloc-id-ice.stderr @@ -1,14 +1,8 @@ -error[E0080]: it is undefined behavior to use this value +error: encountered dangling pointer in final value of constant --> $DIR/dangling-alloc-id-ice.rs:12:1 | LL | const FOO: &() = { - | ^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling reference (use-after-free) - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP - } + | ^^^^^^^^^^^^^^ error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/offset_from_ub.rs b/tests/ui/consts/offset_from_ub.rs index e0dd27079156f..57767e965962b 100644 --- a/tests/ui/consts/offset_from_ub.rs +++ b/tests/ui/consts/offset_from_ub.rs @@ -34,8 +34,8 @@ pub const NOT_MULTIPLE_OF_SIZE: isize = { pub const OFFSET_FROM_NULL: isize = { let ptr = 0 as *const u8; - unsafe { ptr_offset_from(ptr, ptr) } //~ERROR evaluation of constant value failed - //~| null pointer is a dangling pointer + // Null isn't special for zero-sized "accesses" (i.e., the range between the two pointers) + unsafe { ptr_offset_from(ptr, ptr) } }; pub const DIFFERENT_INT: isize = { // offset_from with two different integers: like DIFFERENT_ALLOC @@ -67,8 +67,8 @@ const OUT_OF_BOUNDS_SAME: isize = { let start_ptr = &4 as *const _ as *const u8; let length = 10; let end_ptr = (start_ptr).wrapping_add(length); - unsafe { ptr_offset_from(end_ptr, end_ptr) } //~ERROR evaluation of constant value failed - //~| pointer at offset 10 is out-of-bounds + // Out-of-bounds is fine as long as the range between the pointers is empty. + unsafe { ptr_offset_from(end_ptr, end_ptr) } }; pub const DIFFERENT_ALLOC_UNSIGNED: usize = { diff --git a/tests/ui/consts/offset_from_ub.stderr b/tests/ui/consts/offset_from_ub.stderr index e3bac8d5e31ad..65f75a6e05875 100644 --- a/tests/ui/consts/offset_from_ub.stderr +++ b/tests/ui/consts/offset_from_ub.stderr @@ -23,12 +23,6 @@ error[E0080]: evaluation of constant value failed LL | unsafe { ptr_offset_from(field_ptr, base_ptr as *const u16) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ exact_div: 1_isize cannot be divided by 2_isize without remainder -error[E0080]: evaluation of constant value failed - --> $DIR/offset_from_ub.rs:37:14 - | -LL | unsafe { ptr_offset_from(ptr, ptr) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from`: null pointer is a dangling pointer (it has no provenance) - error[E0080]: evaluation of constant value failed --> $DIR/offset_from_ub.rs:44:14 | @@ -47,12 +41,6 @@ error[E0080]: evaluation of constant value failed LL | unsafe { ptr_offset_from(start_ptr, end_ptr) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from`: ALLOC1 has size 4, so pointer to 10 bytes starting at offset 0 is out-of-bounds -error[E0080]: evaluation of constant value failed - --> $DIR/offset_from_ub.rs:70:14 - | -LL | unsafe { ptr_offset_from(end_ptr, end_ptr) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from`: ALLOC2 has size 4, so pointer at offset 10 is out-of-bounds - error[E0080]: evaluation of constant value failed --> $DIR/offset_from_ub.rs:79:14 | @@ -109,6 +97,6 @@ note: inside `OFFSET_VERY_FAR2` LL | unsafe { ptr1.offset_from(ptr2.wrapping_offset(1)) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 15 previous errors +error: aborting due to 13 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/offset_ub.rs b/tests/ui/consts/offset_ub.rs index 920ecb687cf5c..36e4ff1281a11 100644 --- a/tests/ui/consts/offset_ub.rs +++ b/tests/ui/consts/offset_ub.rs @@ -17,10 +17,10 @@ pub const NEGATIVE_OFFSET: *const u8 = unsafe { [0u8; 1].as_ptr().wrapping_offse pub const ZERO_SIZED_ALLOC: *const u8 = unsafe { [0u8; 0].as_ptr().offset(1) }; //~NOTE pub const DANGLING: *const u8 = unsafe { ptr::NonNull::::dangling().as_ptr().offset(4) }; //~NOTE -// Right now, a zero offset from null is UB -pub const NULL_OFFSET_ZERO: *const u8 = unsafe { ptr::null::().offset(0) }; //~NOTE - // Make sure that we don't panic when computing abs(offset*size_of::()) pub const UNDERFLOW_ABS: *const u8 = unsafe { (usize::MAX as *const u8).offset(isize::MIN) }; //~NOTE +// Offset-by-zero is allowed. +pub const NULL_OFFSET_ZERO: *const u8 = unsafe { ptr::null::().offset(0) }; + fn main() {} diff --git a/tests/ui/consts/offset_ub.stderr b/tests/ui/consts/offset_ub.stderr index b398b20393fc4..89371f06d9d65 100644 --- a/tests/ui/consts/offset_ub.stderr +++ b/tests/ui/consts/offset_ub.stderr @@ -128,19 +128,6 @@ note: inside `DANGLING` LL | pub const DANGLING: *const u8 = unsafe { ptr::NonNull::::dangling().as_ptr().offset(4) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0080]: evaluation of constant value failed - --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL - | - = note: out-of-bounds pointer arithmetic: null pointer is a dangling pointer (it has no provenance) - | -note: inside `std::ptr::const_ptr::::offset` - --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL -note: inside `NULL_OFFSET_ZERO` - --> $DIR/offset_ub.rs:21:50 - | -LL | pub const NULL_OFFSET_ZERO: *const u8 = unsafe { ptr::null::().offset(0) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error[E0080]: evaluation of constant value failed --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | @@ -149,11 +136,11 @@ error[E0080]: evaluation of constant value failed note: inside `std::ptr::const_ptr::::offset` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL note: inside `UNDERFLOW_ABS` - --> $DIR/offset_ub.rs:24:47 + --> $DIR/offset_ub.rs:21:47 | LL | pub const UNDERFLOW_ABS: *const u8 = unsafe { (usize::MAX as *const u8).offset(isize::MIN) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 12 previous errors +error: aborting due to 11 previous errors For more information about this error, try `rustc --explain E0080`.