diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index b79d7441acac7..20f0f27517ffe 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -69,9 +69,6 @@ const_eval_deref_function_pointer = accessing {$allocation} which contains a function const_eval_deref_vtable_pointer = accessing {$allocation} which contains a vtable -const_eval_different_allocations = - `{$name}` called on pointers into different allocations - const_eval_division_by_zero = dividing by zero const_eval_division_overflow = @@ -234,12 +231,18 @@ const_eval_not_enough_caller_args = const_eval_nullary_intrinsic_fail = could not evaluate nullary intrinsic +const_eval_offset_from_different_allocations = + `{$name}` called on pointers into different allocations +const_eval_offset_from_different_integers = + `{$name}` called on different pointers without provenance (i.e., without an associated allocation) const_eval_offset_from_overflow = `{$name}` called when first pointer is too far ahead of second - -const_eval_offset_from_test = out-of-bounds `offset_from` +const_eval_offset_from_test = + out-of-bounds `offset_from` const_eval_offset_from_underflow = `{$name}` called when first pointer is too far before second +const_eval_offset_from_unsigned_overflow = + `ptr_offset_from_unsigned` called when first pointer has smaller offset than second: {$a_offset} < {$b_offset} const_eval_operator_non_const = cannot call non-const operator in {const_eval_const_context}s @@ -381,8 +384,6 @@ const_eval_unreachable = entering unreachable code const_eval_unreachable_unwind = unwinding past a stack frame that does not allow unwinding -const_eval_unsigned_offset_from_overflow = - `ptr_offset_from_unsigned` called when first pointer has smaller offset than second: {$a_offset} < {$b_offset} const_eval_unsized_local = unsized locals are not supported const_eval_unstable_const_fn = `{$def_path}` is not yet stable as a const fn diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 88ce5a7cbebba..c53f068c9a46a 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -8,21 +8,16 @@ use rustc_middle::ty::layout::{LayoutOf as _, ValidityRequirement}; use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::{Ty, TyCtxt}; use rustc_middle::{ - mir::{ - self, - interpret::{ - Allocation, ConstAllocation, GlobalId, InterpResult, PointerArithmetic, Scalar, - }, - BinOp, ConstValue, NonDivergingIntrinsic, - }, + mir::{self, BinOp, ConstValue, NonDivergingIntrinsic}, ty::layout::TyAndLayout, }; use rustc_span::symbol::{sym, Symbol}; use rustc_target::abi::Size; use super::{ - memory::MemoryKind, util::ensure_monomorphic_enough, CheckInAllocMsg, ImmTy, InterpCx, - MPlaceTy, Machine, OpTy, Pointer, + memory::MemoryKind, util::ensure_monomorphic_enough, Allocation, CheckInAllocMsg, + ConstAllocation, GlobalId, ImmTy, InterpCx, InterpResult, MPlaceTy, Machine, OpTy, Pointer, + PointerArithmetic, Scalar, }; use crate::fluent_generated as fluent; @@ -249,14 +244,22 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { match (self.ptr_try_get_alloc_id(a), self.ptr_try_get_alloc_id(b)) { (Err(a), Err(b)) => { // Neither pointer points to an allocation. - // If these are inequal or null, this *will* fail the deref check below. + // This is okay only if they are the same. + if a != b { + // We'd catch this below in the "dereferenceable" check, but + // show a nicer error for this particular case. + throw_ub_custom!( + fluent::const_eval_offset_from_different_integers, + name = intrinsic_name, + ); + } (a, b) } (Err(_), _) | (_, Err(_)) => { // We managed to find a valid allocation for one pointer, but not the other. // That means they are definitely not pointing to the same allocation. throw_ub_custom!( - fluent::const_eval_different_allocations, + fluent::const_eval_offset_from_different_allocations, name = intrinsic_name, ); } @@ -264,7 +267,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Found allocation for both. They must be into the same allocation. if a_alloc_id != b_alloc_id { throw_ub_custom!( - fluent::const_eval_different_allocations, + fluent::const_eval_offset_from_different_allocations, name = intrinsic_name, ); } @@ -286,7 +289,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // a < b if intrinsic_name == sym::ptr_offset_from_unsigned { throw_ub_custom!( - fluent::const_eval_unsigned_offset_from_overflow, + fluent::const_eval_offset_from_unsigned_overflow, a_offset = a_offset, b_offset = b_offset, ); diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_different_ints.rs b/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_different_ints.rs new file mode 100644 index 0000000000000..57b4fd0dedb7c --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_different_ints.rs @@ -0,0 +1,21 @@ +#![feature(strict_provenance)] +use core::ptr; + +fn main() { + unsafe { + let base = ptr::without_provenance::<()>(10); + let unit = &*base; + let p1 = unit as *const (); + + let base = ptr::without_provenance::<()>(11); + let unit = &*base; + let p2 = unit as *const (); + + // Seems to work because they are same pointer + // even though it's dangling. + let _ = p1.byte_offset_from(p1); + + // UB because different pointers. + let _ = p1.byte_offset_from(p2); //~ERROR: different pointers without provenance + } +} diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_different_ints.stderr b/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_different_ints.stderr new file mode 100644 index 0000000000000..6e9e5633fecc6 --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_different_ints.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: `ptr_offset_from` called on different pointers without provenance (i.e., without an associated allocation) + --> $DIR/ptr_offset_from_different_ints.rs:LL:CC + | +LL | let _ = p1.byte_offset_from(p2); + | ^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called on different pointers without provenance (i.e., without an associated allocation) + | + = 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_different_ints.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/tests/ui/consts/offset_from_ub.rs b/tests/ui/consts/offset_from_ub.rs index 51163e650d6aa..e0dd27079156f 100644 --- a/tests/ui/consts/offset_from_ub.rs +++ b/tests/ui/consts/offset_from_ub.rs @@ -42,7 +42,7 @@ pub const DIFFERENT_INT: isize = { // offset_from with two different integers: l let ptr1 = 8 as *const u8; let ptr2 = 16 as *const u8; unsafe { ptr_offset_from(ptr2, ptr1) } //~ERROR evaluation of constant value failed - //~| 0x8[noalloc] is a dangling pointer + //~| different pointers without provenance }; const OUT_OF_BOUNDS_1: isize = { @@ -81,13 +81,13 @@ pub const DIFFERENT_ALLOC_UNSIGNED: usize = { }; pub const TOO_FAR_APART1: isize = { - let ptr1 = ptr::null::(); + let ptr1 = &0u8 as *const u8; let ptr2 = ptr1.wrapping_add(isize::MAX as usize + 42); unsafe { ptr_offset_from(ptr2, ptr1) } //~ERROR evaluation of constant value failed //~| too far ahead }; pub const TOO_FAR_APART2: isize = { - let ptr1 = ptr::null::(); + let ptr1 = &0u8 as *const u8; let ptr2 = ptr1.wrapping_add(isize::MAX as usize + 42); unsafe { ptr_offset_from(ptr1, ptr2) } //~ERROR evaluation of constant value failed //~| too far before @@ -100,7 +100,7 @@ const WRONG_ORDER_UNSIGNED: usize = { //~| first pointer has smaller offset than second: 0 < 8 }; pub const TOO_FAR_APART_UNSIGNED: usize = { - let ptr1 = ptr::null::(); + let ptr1 = &0u8 as *const u8; let ptr2 = ptr1.wrapping_add(isize::MAX as usize + 42); // This would fit into a `usize` but we still don't allow it. unsafe { ptr_offset_from_unsigned(ptr2, ptr1) } //~ERROR evaluation of constant value failed diff --git a/tests/ui/consts/offset_from_ub.stderr b/tests/ui/consts/offset_from_ub.stderr index 4fbb2f00100a6..e3bac8d5e31ad 100644 --- a/tests/ui/consts/offset_from_ub.stderr +++ b/tests/ui/consts/offset_from_ub.stderr @@ -33,7 +33,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/offset_from_ub.rs:44:14 | LL | unsafe { ptr_offset_from(ptr2, ptr1) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from`: 0x8[noalloc] is a dangling pointer (it has no provenance) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called on different pointers without provenance (i.e., without an associated allocation) error[E0080]: evaluation of constant value failed --> $DIR/offset_from_ub.rs:53:14 @@ -86,7 +86,7 @@ LL | unsafe { ptr_offset_from_unsigned(ptr2, ptr1) } error[E0080]: evaluation of constant value failed --> $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: `ptr_offset_from` called on different pointers without provenance (i.e., without an associated allocation) | note: inside `std::ptr::const_ptr::::offset_from` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL @@ -99,7 +99,7 @@ LL | unsafe { ptr2.offset_from(ptr1) } error[E0080]: evaluation of constant value failed --> $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: `ptr_offset_from` called on different pointers without provenance (i.e., without an associated allocation) | note: inside `std::ptr::const_ptr::::offset_from` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL