Skip to content

miri: improve error when offset_from preconditions are violated #136438

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
Feb 3, 2025
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
4 changes: 3 additions & 1 deletion compiler/rustc_const_eval/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,9 @@ const_eval_nullary_intrinsic_fail =
could not evaluate nullary intrinsic

const_eval_offset_from_different_allocations =
`{$name}` called on pointers into different allocations
`{$name}` called on two different pointers that are not both derived from the same allocation
const_eval_offset_from_out_of_bounds =
`{$name}` called on two different pointers where the memory range between them is not in-bounds of an allocation
const_eval_offset_from_overflow =
`{$name}` called when first pointer is too far ahead of second
const_eval_offset_from_test =
Expand Down
20 changes: 19 additions & 1 deletion compiler/rustc_const_eval/src/interpret/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,25 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {

// Check that the memory between them is dereferenceable at all, starting from the
// origin pointer: `dist` is `a - b`, so it is based on `b`.
self.check_ptr_access_signed(b, dist, CheckInAllocMsg::OffsetFromTest)?;
self.check_ptr_access_signed(b, dist, CheckInAllocMsg::OffsetFromTest)
.map_err_kind(|_| {
// This could mean they point to different allocations, or they point to the same allocation
// but not the entire range between the pointers is in-bounds.
if let Ok((a_alloc_id, ..)) = self.ptr_try_get_alloc_id(a, 0)
&& let Ok((b_alloc_id, ..)) = self.ptr_try_get_alloc_id(b, 0)
&& a_alloc_id == b_alloc_id
{
err_ub_custom!(
fluent::const_eval_offset_from_out_of_bounds,
name = intrinsic_name,
)
} else {
err_ub_custom!(
fluent::const_eval_offset_from_different_allocations,
name = intrinsic_name,
)
}
})?;
// Then check that this is also dereferenceable from `a`. This ensures that they are
// derived from the same allocation.
self.check_ptr_access_signed(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
fn main() {
unsafe {
(&1_u8 as *const u8).offset_from(&2_u8); //~ERROR: not both derived from the same allocation
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
error: Undefined Behavior: `ptr_offset_from` called on two different pointers that are not both derived from the same allocation
--> tests/fail/intrinsics/ptr_offset_from_different_allocs.rs:LL:CC
|
LL | (&1_u8 as *const u8).offset_from(&2_u8);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called on two different pointers that are not both derived from the same 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 tests/fail/intrinsics/ptr_offset_from_different_allocs.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

Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ fn main() {
let _ = p1.byte_offset_from(p1);

// UB because different pointers.
let _ = p1.byte_offset_from(p2); //~ERROR: no provenance
let _ = p1.byte_offset_from(p2); //~ERROR: not both derived from the same allocation
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
error: Undefined Behavior: out-of-bounds `offset_from` origin: expected a pointer to the end of 1 byte of memory, but got 0xb[noalloc] which is a dangling pointer (it has no provenance)
error: Undefined Behavior: `ptr_offset_from` called on two different pointers that are not both derived from the same allocation
--> tests/fail/intrinsics/ptr_offset_from_different_ints.rs:LL:CC
|
LL | let _ = p1.byte_offset_from(p2);
| ^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from` origin: expected a pointer to the end of 1 byte of memory, but got 0xb[noalloc] which is a dangling pointer (it has no provenance)
| ^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called on two different pointers that are not both derived from the same 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
Expand Down
7 changes: 7 additions & 0 deletions src/tools/miri/tests/fail/intrinsics/ptr_offset_from_oob.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
fn main() {
let mem = [0u8; 1];
let ptr = mem.as_ptr();
unsafe {
ptr.wrapping_add(4).offset_from(ptr); //~ERROR: the memory range between them is not in-bounds of an allocation
}
}
15 changes: 15 additions & 0 deletions src/tools/miri/tests/fail/intrinsics/ptr_offset_from_oob.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
error: Undefined Behavior: `ptr_offset_from` called on two different pointers where the memory range between them is not in-bounds of an allocation
--> tests/fail/intrinsics/ptr_offset_from_oob.rs:LL:CC
|
LL | ptr.wrapping_add(4).offset_from(ptr);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called on two different pointers where the memory range between them is not in-bounds of an 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 tests/fail/intrinsics/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

4 changes: 2 additions & 2 deletions tests/ui/const-ptr/forbidden_slices.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ LL | from_ptr_range(ptr..ptr.add(1))
error[E0080]: could not evaluate static initializer
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
= note: `ptr_offset_from_unsigned` called on pointers into different allocations
= note: `ptr_offset_from_unsigned` called on two different pointers that are not both derived from the same allocation
|
note: inside `std::ptr::const_ptr::<impl *const u32>::sub_ptr`
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
Expand All @@ -205,7 +205,7 @@ LL | pub static R9: &[u32] = unsafe { from_ptr_range(&D0..(&D0 as *const u32).ad
error[E0080]: could not evaluate static initializer
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
= note: `ptr_offset_from_unsigned` called on pointers into different allocations
= note: `ptr_offset_from_unsigned` called on two different pointers that are not both derived from the same allocation
|
note: inside `std::ptr::const_ptr::<impl *const u32>::sub_ptr`
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
Expand Down
10 changes: 5 additions & 5 deletions tests/ui/consts/offset_from_ub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub const DIFFERENT_ALLOC: usize = {
let uninit2 = std::mem::MaybeUninit::<Struct>::uninit();
let field_ptr: *const Struct = &uninit2 as *const _ as *const Struct;
let offset = unsafe { ptr_offset_from(field_ptr, base_ptr) }; //~ERROR evaluation of constant value failed
//~| pointers into different allocations
//~| not both derived from the same allocation
offset as usize
};

Expand All @@ -37,7 +37,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
//~| dangling pointer
//~| not both derived from the same allocation
};

const OUT_OF_BOUNDS_1: isize = {
Expand All @@ -46,7 +46,7 @@ const OUT_OF_BOUNDS_1: isize = {
let end_ptr = (start_ptr).wrapping_add(length);
// First ptr is out of bounds
unsafe { ptr_offset_from(end_ptr, start_ptr) } //~ERROR evaluation of constant value failed
//~| expected a pointer to 10 bytes of memory
//~| the memory range between them is not in-bounds of an allocation
};

const OUT_OF_BOUNDS_2: isize = {
Expand All @@ -55,7 +55,7 @@ const OUT_OF_BOUNDS_2: isize = {
let end_ptr = (start_ptr).wrapping_add(length);
// Second ptr is out of bounds
unsafe { ptr_offset_from(start_ptr, end_ptr) } //~ERROR evaluation of constant value failed
//~| expected a pointer to the end of 10 bytes of memory
//~| the memory range between them is not in-bounds of an allocation
};

pub const DIFFERENT_ALLOC_UNSIGNED: usize = {
Expand All @@ -64,7 +64,7 @@ pub const DIFFERENT_ALLOC_UNSIGNED: usize = {
let uninit2 = std::mem::MaybeUninit::<Struct>::uninit();
let field_ptr: *const Struct = &uninit2 as *const _ as *const Struct;
unsafe { ptr_offset_from_unsigned(field_ptr, base_ptr) } //~ERROR evaluation of constant value failed
//~| pointers into different allocations
//~| not both derived from the same allocation
};

pub const TOO_FAR_APART1: isize = {
Expand Down
14 changes: 7 additions & 7 deletions tests/ui/consts/offset_from_ub.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ error[E0080]: evaluation of constant value failed
--> $DIR/offset_from_ub.rs:19:27
|
LL | let offset = unsafe { ptr_offset_from(field_ptr, base_ptr) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called on pointers into different allocations
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called on two different pointers that are not both derived from the same allocation

error[E0080]: evaluation of constant value failed
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
= note: `ptr_offset_from` called on pointers into different allocations
= note: `ptr_offset_from` called on two different pointers that are not both derived from the same allocation
|
note: inside `std::ptr::const_ptr::<impl *const u8>::offset_from`
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
Expand All @@ -27,25 +27,25 @@ error[E0080]: evaluation of constant value failed
--> $DIR/offset_from_ub.rs:39:14
|
LL | unsafe { ptr_offset_from(ptr2, ptr1) }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from` origin: expected a pointer to $BYTES bytes of memory, but got 0x8[noalloc] which is a dangling pointer (it has no provenance)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called on two different pointers that are not both derived from the same allocation

error[E0080]: evaluation of constant value failed
--> $DIR/offset_from_ub.rs:48:14
|
LL | unsafe { ptr_offset_from(end_ptr, start_ptr) }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from` origin: expected a pointer to $BYTES bytes of memory, but got ALLOC0 which is only $BYTES bytes from the end of the allocation
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called on two different pointers where the memory range between them is not in-bounds of an allocation

error[E0080]: evaluation of constant value failed
--> $DIR/offset_from_ub.rs:57:14
|
LL | unsafe { ptr_offset_from(start_ptr, end_ptr) }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from` origin: expected a pointer to the end of $BYTES bytes of memory, but got ALLOC1+0xa which does not have enough space to the beginning of the allocation
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called on two different pointers where the memory range between them is not in-bounds of an allocation

error[E0080]: evaluation of constant value failed
--> $DIR/offset_from_ub.rs:66:14
|
LL | unsafe { ptr_offset_from_unsigned(field_ptr, base_ptr) }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from_unsigned` called on pointers into different allocations
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from_unsigned` called on two different pointers that are not both derived from the same allocation

error[E0080]: evaluation of constant value failed
--> $DIR/offset_from_ub.rs:73:14
Expand Down Expand Up @@ -80,7 +80,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` origin: expected a pointer to $BYTES bytes of memory, but got a null pointer
= note: `ptr_offset_from` called on two different pointers that are not both derived from the same 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