Skip to content

Commit

Permalink
offset_from: the *absolute* difference must fit in an isize
Browse files Browse the repository at this point in the history
  • Loading branch information
RalfJung committed Jul 3, 2024
1 parent 7d97c59 commit ebabe18
Show file tree
Hide file tree
Showing 5 changed files with 29 additions and 47 deletions.
4 changes: 2 additions & 2 deletions compiler/rustc_const_eval/src/interpret/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,9 +301,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
}
// The signed form of the intrinsic allows this. If we interpret the
// difference as isize, we'll get the proper signed difference. If that
// seems *positive*, they were more than isize::MAX apart.
// seems *positive* or equal to isize::MIN, they were more than isize::MAX apart.
let dist = val.to_target_isize(self)?;
if dist >= 0 {
if dist >= 0 || i128::from(dist) == self.pointer_size().signed_int_min() {
throw_ub_custom!(
fluent::const_eval_offset_from_underflow,
name = intrinsic_name,
Expand Down
24 changes: 4 additions & 20 deletions library/core/src/ptr/const_ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -623,26 +623,10 @@ impl<T: ?Sized> *const T {
/// * The distance between the pointers, in bytes, must be an exact multiple
/// of the size of `T`.
///
/// * The distance between the pointers, **in bytes**, cannot overflow an `isize`.
///
/// * The distance being in bounds cannot rely on "wrapping around" the address space.
///
/// Rust types are never larger than `isize::MAX` and Rust allocations never wrap around the
/// address space, so two pointers within some value of any Rust type `T` will always satisfy
/// the last two conditions. The standard library also generally ensures that allocations
/// never reach a size where an offset is a concern. For instance, `Vec` and `Box` ensure they
/// never allocate more than `isize::MAX` bytes, so `ptr_into_vec.offset_from(vec.as_ptr())`
/// always satisfies the last two conditions.
///
/// Most platforms fundamentally can't even construct such a large allocation.
/// For instance, no known 64-bit platform can ever serve a request
/// for 2<sup>63</sup> bytes due to page-table limitations or splitting the address space.
/// However, some 32-bit and 16-bit platforms may successfully serve a request for
/// more than `isize::MAX` bytes with things like Physical Address
/// Extension. As such, memory acquired directly from allocators or memory
/// mapped files *may* be too large to handle with this function.
/// (Note that [`offset`] and [`add`] also have a similar limitation and hence cannot be used on
/// such large allocations either.)
/// As a consequence, the absolute distance between the pointers, **in bytes**, computed on
/// mathematical integers (without "wrapping around"), cannot overflow an `isize`. This is
/// implied by the in-bounds requirement, and the fact that no allocated object can be larger
/// than `isize::MAX`.
///
/// The requirement for pointers to be derived from the same allocated object is primarily
/// needed for `const`-compatibility: the distance between pointers into *different* allocated
Expand Down
24 changes: 4 additions & 20 deletions library/core/src/ptr/mut_ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -848,26 +848,10 @@ impl<T: ?Sized> *mut T {
/// * The distance between the pointers, in bytes, must be an exact multiple
/// of the size of `T`.
///
/// * The distance between the pointers, **in bytes**, cannot overflow an `isize`.
///
/// * The distance being in bounds cannot rely on "wrapping around" the address space.
///
/// Rust types are never larger than `isize::MAX` and Rust allocations never wrap around the
/// address space, so two pointers within some value of any Rust type `T` will always satisfy
/// the last two conditions. The standard library also generally ensures that allocations
/// never reach a size where an offset is a concern. For instance, `Vec` and `Box` ensure they
/// never allocate more than `isize::MAX` bytes, so `ptr_into_vec.offset_from(vec.as_ptr())`
/// always satisfies the last two conditions.
///
/// Most platforms fundamentally can't even construct such a large allocation.
/// For instance, no known 64-bit platform can ever serve a request
/// for 2<sup>63</sup> bytes due to page-table limitations or splitting the address space.
/// However, some 32-bit and 16-bit platforms may successfully serve a request for
/// more than `isize::MAX` bytes with things like Physical Address
/// Extension. As such, memory acquired directly from allocators or memory
/// mapped files *may* be too large to handle with this function.
/// (Note that [`offset`] and [`add`] also have a similar limitation and hence cannot be used on
/// such large allocations either.)
/// As a consequence, the absolute distance between the pointers, **in bytes**, computed on
/// mathematical integers (without "wrapping around"), cannot overflow an `isize`. This is
/// implied by the in-bounds requirement, and the fact that no allocated object can be larger
/// than `isize::MAX`.
///
/// The requirement for pointers to be derived from the same allocated object is primarily
/// needed for `const`-compatibility: the distance between pointers into *different* allocated
Expand Down
8 changes: 8 additions & 0 deletions tests/ui/consts/offset_from_ub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,14 @@ pub const TOO_FAR_APART2: isize = {
unsafe { ptr_offset_from(ptr1, ptr2) } //~ERROR evaluation of constant value failed
//~| too far before
};
pub const TOO_FAR_APART3: isize = {
let ptr1 = &0u8 as *const u8;
let ptr2 = ptr1.wrapping_offset(isize::MIN);
// The result of this would be `isize::MIN`, which *does* fit in an `isize`, but its
// absolute value does not. (Also anyway there cannot be an allocation of that size.)
unsafe { ptr_offset_from(ptr1, ptr2) } //~ERROR evaluation of constant value failed
//~| too far before
};

const WRONG_ORDER_UNSIGNED: usize = {
let a = ['a', 'b', 'c'];
Expand Down
16 changes: 11 additions & 5 deletions tests/ui/consts/offset_from_ub.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,19 @@ LL | unsafe { ptr_offset_from(ptr1, ptr2) }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called when first pointer is too far before second

error[E0080]: evaluation of constant value failed
--> $DIR/offset_from_ub.rs:99:14
--> $DIR/offset_from_ub.rs:100:14
|
LL | unsafe { ptr_offset_from(ptr1, ptr2) }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called when first pointer is too far before second

error[E0080]: evaluation of constant value failed
--> $DIR/offset_from_ub.rs:107:14
|
LL | unsafe { ptr_offset_from_unsigned(p, p.add(2) ) }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from_unsigned` called when first pointer has smaller offset than second: 0 < 8

error[E0080]: evaluation of constant value failed
--> $DIR/offset_from_ub.rs:106:14
--> $DIR/offset_from_ub.rs:114:14
|
LL | unsafe { ptr_offset_from_unsigned(ptr2, ptr1) }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from_unsigned` called when first pointer is too far ahead of second
Expand All @@ -79,7 +85,7 @@ error[E0080]: evaluation of constant value failed
note: inside `std::ptr::const_ptr::<impl *const u8>::offset_from`
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
note: inside `OFFSET_VERY_FAR1`
--> $DIR/offset_from_ub.rs:115:14
--> $DIR/offset_from_ub.rs:123:14
|
LL | unsafe { ptr2.offset_from(ptr1) }
| ^^^^^^^^^^^^^^^^^^^^^^
Expand All @@ -92,11 +98,11 @@ error[E0080]: evaluation of constant value failed
note: inside `std::ptr::const_ptr::<impl *const u8>::offset_from`
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
note: inside `OFFSET_VERY_FAR2`
--> $DIR/offset_from_ub.rs:121:14
--> $DIR/offset_from_ub.rs:129:14
|
LL | unsafe { ptr1.offset_from(ptr2.wrapping_offset(1)) }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 13 previous errors
error: aborting due to 14 previous errors

For more information about this error, try `rustc --explain E0080`.

0 comments on commit ebabe18

Please sign in to comment.