Skip to content

interpret/miri: better errors on failing offset_from #124923

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions compiler/rustc_const_eval/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down
29 changes: 16 additions & 13 deletions compiler/rustc_const_eval/src/interpret/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -249,22 +244,30 @@ 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,
);
}
(Ok((a_alloc_id, a_offset, _)), Ok((b_alloc_id, b_offset, _))) => {
// 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,
);
}
Expand All @@ -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,
);
Expand Down
Original file line number Diff line number Diff line change
@@ -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
}
}
Original file line number Diff line number Diff line change
@@ -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

8 changes: 4 additions & 4 deletions tests/ui/consts/offset_from_ub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down Expand Up @@ -81,13 +81,13 @@ pub const DIFFERENT_ALLOC_UNSIGNED: usize = {
};

pub const TOO_FAR_APART1: isize = {
let ptr1 = ptr::null::<u8>();
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::<u8>();
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
Expand All @@ -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::<u8>();
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
Expand Down
6 changes: 3 additions & 3 deletions tests/ui/consts/offset_from_ub.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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::<impl *const u8>::offset_from`
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
Expand All @@ -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::<impl *const u8>::offset_from`
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
Expand Down
Loading