Skip to content

Commit 273d253

Browse files
committed
offset_from: "the difference must fit in an isize" is a corollary
also, isize::MIN is an impossible distance
1 parent 7d97c59 commit 273d253

File tree

6 files changed

+40
-77
lines changed

6 files changed

+40
-77
lines changed

compiler/rustc_const_eval/src/interpret/intrinsics.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -301,9 +301,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
301301
}
302302
// The signed form of the intrinsic allows this. If we interpret the
303303
// difference as isize, we'll get the proper signed difference. If that
304-
// seems *positive*, they were more than isize::MAX apart.
304+
// seems *positive* or equal to isize::MIN, they were more than isize::MAX apart.
305305
let dist = val.to_target_isize(self)?;
306-
if dist >= 0 {
306+
if dist >= 0 || i128::from(dist) == self.pointer_size().signed_int_min() {
307307
throw_ub_custom!(
308308
fluent::const_eval_offset_from_underflow,
309309
name = intrinsic_name,

library/core/src/ptr/const_ptr.rs

+5-22
Original file line numberDiff line numberDiff line change
@@ -611,8 +611,7 @@ impl<T: ?Sized> *const T {
611611
///
612612
/// # Safety
613613
///
614-
/// If any of the following conditions are violated, the result is Undefined
615-
/// Behavior:
614+
/// If any of the following conditions are violated, the result is Undefined Behavior:
616615
///
617616
/// * `self` and `origin` must either
618617
///
@@ -623,26 +622,10 @@ impl<T: ?Sized> *const T {
623622
/// * The distance between the pointers, in bytes, must be an exact multiple
624623
/// of the size of `T`.
625624
///
626-
/// * The distance between the pointers, **in bytes**, cannot overflow an `isize`.
627-
///
628-
/// * The distance being in bounds cannot rely on "wrapping around" the address space.
629-
///
630-
/// Rust types are never larger than `isize::MAX` and Rust allocations never wrap around the
631-
/// address space, so two pointers within some value of any Rust type `T` will always satisfy
632-
/// the last two conditions. The standard library also generally ensures that allocations
633-
/// never reach a size where an offset is a concern. For instance, `Vec` and `Box` ensure they
634-
/// never allocate more than `isize::MAX` bytes, so `ptr_into_vec.offset_from(vec.as_ptr())`
635-
/// always satisfies the last two conditions.
636-
///
637-
/// Most platforms fundamentally can't even construct such a large allocation.
638-
/// For instance, no known 64-bit platform can ever serve a request
639-
/// for 2<sup>63</sup> bytes due to page-table limitations or splitting the address space.
640-
/// However, some 32-bit and 16-bit platforms may successfully serve a request for
641-
/// more than `isize::MAX` bytes with things like Physical Address
642-
/// Extension. As such, memory acquired directly from allocators or memory
643-
/// mapped files *may* be too large to handle with this function.
644-
/// (Note that [`offset`] and [`add`] also have a similar limitation and hence cannot be used on
645-
/// such large allocations either.)
625+
/// As a consequence, the absolute distance between the pointers, **in bytes**, computed on
626+
/// mathematical integers (without "wrapping around"), cannot overflow an `isize`. This is
627+
/// implied by the in-bounds requirement, and the fact that no allocated object can be larger
628+
/// than `isize::MAX` bytes.
646629
///
647630
/// The requirement for pointers to be derived from the same allocated object is primarily
648631
/// needed for `const`-compatibility: the distance between pointers into *different* allocated

library/core/src/ptr/mut_ptr.rs

+5-22
Original file line numberDiff line numberDiff line change
@@ -836,8 +836,7 @@ impl<T: ?Sized> *mut T {
836836
///
837837
/// # Safety
838838
///
839-
/// If any of the following conditions are violated, the result is Undefined
840-
/// Behavior:
839+
/// If any of the following conditions are violated, the result is Undefined Behavior:
841840
///
842841
/// * `self` and `origin` must either
843842
///
@@ -848,26 +847,10 @@ impl<T: ?Sized> *mut T {
848847
/// * The distance between the pointers, in bytes, must be an exact multiple
849848
/// of the size of `T`.
850849
///
851-
/// * The distance between the pointers, **in bytes**, cannot overflow an `isize`.
852-
///
853-
/// * The distance being in bounds cannot rely on "wrapping around" the address space.
854-
///
855-
/// Rust types are never larger than `isize::MAX` and Rust allocations never wrap around the
856-
/// address space, so two pointers within some value of any Rust type `T` will always satisfy
857-
/// the last two conditions. The standard library also generally ensures that allocations
858-
/// never reach a size where an offset is a concern. For instance, `Vec` and `Box` ensure they
859-
/// never allocate more than `isize::MAX` bytes, so `ptr_into_vec.offset_from(vec.as_ptr())`
860-
/// always satisfies the last two conditions.
861-
///
862-
/// Most platforms fundamentally can't even construct such a large allocation.
863-
/// For instance, no known 64-bit platform can ever serve a request
864-
/// for 2<sup>63</sup> bytes due to page-table limitations or splitting the address space.
865-
/// However, some 32-bit and 16-bit platforms may successfully serve a request for
866-
/// more than `isize::MAX` bytes with things like Physical Address
867-
/// Extension. As such, memory acquired directly from allocators or memory
868-
/// mapped files *may* be too large to handle with this function.
869-
/// (Note that [`offset`] and [`add`] also have a similar limitation and hence cannot be used on
870-
/// such large allocations either.)
850+
/// As a consequence, the absolute distance between the pointers, in bytes, computed on
851+
/// mathematical integers (without "wrapping around"), cannot overflow an `isize`. This is
852+
/// implied by the in-bounds requirement, and the fact that no allocated object can be larger
853+
/// than `isize::MAX` bytes.
871854
///
872855
/// The requirement for pointers to be derived from the same allocated object is primarily
873856
/// needed for `const`-compatibility: the distance between pointers into *different* allocated

library/core/src/ptr/non_null.rs

+9-26
Original file line numberDiff line numberDiff line change
@@ -761,38 +761,21 @@ impl<T: ?Sized> NonNull<T> {
761761
///
762762
/// # Safety
763763
///
764-
/// If any of the following conditions are violated, the result is Undefined
765-
/// Behavior:
764+
/// If any of the following conditions are violated, the result is Undefined Behavior:
766765
///
767-
/// * Both `self` and `origin` must be either in bounds or one
768-
/// byte past the end of the same [allocated object].
766+
/// * `self` and `origin` must either
769767
///
770-
/// * Both pointers must be *derived from* a pointer to the same object.
771-
/// (See below for an example.)
768+
/// * both be *derived from* a pointer to the same [allocated object], and the memory range between
769+
/// the two pointers must be either empty or in bounds of that object. (See below for an example.)
770+
/// * or both be derived from an integer literal/constant, and point to the same address.
772771
///
773772
/// * The distance between the pointers, in bytes, must be an exact multiple
774773
/// of the size of `T`.
775774
///
776-
/// * The distance between the pointers, **in bytes**, cannot overflow an `isize`.
777-
///
778-
/// * The distance being in bounds cannot rely on "wrapping around" the address space.
779-
///
780-
/// Rust types are never larger than `isize::MAX` and Rust allocations never wrap around the
781-
/// address space, so two pointers within some value of any Rust type `T` will always satisfy
782-
/// the last two conditions. The standard library also generally ensures that allocations
783-
/// never reach a size where an offset is a concern. For instance, `Vec` and `Box` ensure they
784-
/// never allocate more than `isize::MAX` bytes, so `ptr_into_vec.offset_from(vec.as_ptr())`
785-
/// always satisfies the last two conditions.
786-
///
787-
/// Most platforms fundamentally can't even construct such a large allocation.
788-
/// For instance, no known 64-bit platform can ever serve a request
789-
/// for 2<sup>63</sup> bytes due to page-table limitations or splitting the address space.
790-
/// However, some 32-bit and 16-bit platforms may successfully serve a request for
791-
/// more than `isize::MAX` bytes with things like Physical Address
792-
/// Extension. As such, memory acquired directly from allocators or memory
793-
/// mapped files *may* be too large to handle with this function.
794-
/// (Note that [`offset`] and [`add`] also have a similar limitation and hence cannot be used on
795-
/// such large allocations either.)
775+
/// As a consequence, the absolute distance between the pointers, in bytes, computed on
776+
/// mathematical integers (without "wrapping around"), cannot overflow an `isize`. This is
777+
/// implied by the in-bounds requirement, and the fact that no allocated object can be larger
778+
/// than `isize::MAX` bytes.
796779
///
797780
/// The requirement for pointers to be derived from the same allocated object is primarily
798781
/// needed for `const`-compatibility: the distance between pointers into *different* allocated

tests/ui/consts/offset_from_ub.rs

+8
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,14 @@ pub const TOO_FAR_APART2: isize = {
9292
unsafe { ptr_offset_from(ptr1, ptr2) } //~ERROR evaluation of constant value failed
9393
//~| too far before
9494
};
95+
pub const TOO_FAR_APART3: isize = {
96+
let ptr1 = &0u8 as *const u8;
97+
let ptr2 = ptr1.wrapping_offset(isize::MIN);
98+
// The result of this would be `isize::MIN`, which *does* fit in an `isize`, but its
99+
// absolute value does not. (Also anyway there cannot be an allocation of that size.)
100+
unsafe { ptr_offset_from(ptr1, ptr2) } //~ERROR evaluation of constant value failed
101+
//~| too far before
102+
};
95103

96104
const WRONG_ORDER_UNSIGNED: usize = {
97105
let a = ['a', 'b', 'c'];

tests/ui/consts/offset_from_ub.stderr

+11-5
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,19 @@ LL | unsafe { ptr_offset_from(ptr1, ptr2) }
6060
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called when first pointer is too far before second
6161

6262
error[E0080]: evaluation of constant value failed
63-
--> $DIR/offset_from_ub.rs:99:14
63+
--> $DIR/offset_from_ub.rs:100:14
64+
|
65+
LL | unsafe { ptr_offset_from(ptr1, ptr2) }
66+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called when first pointer is too far before second
67+
68+
error[E0080]: evaluation of constant value failed
69+
--> $DIR/offset_from_ub.rs:107:14
6470
|
6571
LL | unsafe { ptr_offset_from_unsigned(p, p.add(2) ) }
6672
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from_unsigned` called when first pointer has smaller offset than second: 0 < 8
6773

6874
error[E0080]: evaluation of constant value failed
69-
--> $DIR/offset_from_ub.rs:106:14
75+
--> $DIR/offset_from_ub.rs:114:14
7076
|
7177
LL | unsafe { ptr_offset_from_unsigned(ptr2, ptr1) }
7278
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from_unsigned` called when first pointer is too far ahead of second
@@ -79,7 +85,7 @@ error[E0080]: evaluation of constant value failed
7985
note: inside `std::ptr::const_ptr::<impl *const u8>::offset_from`
8086
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
8187
note: inside `OFFSET_VERY_FAR1`
82-
--> $DIR/offset_from_ub.rs:115:14
88+
--> $DIR/offset_from_ub.rs:123:14
8389
|
8490
LL | unsafe { ptr2.offset_from(ptr1) }
8591
| ^^^^^^^^^^^^^^^^^^^^^^
@@ -92,11 +98,11 @@ error[E0080]: evaluation of constant value failed
9298
note: inside `std::ptr::const_ptr::<impl *const u8>::offset_from`
9399
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
94100
note: inside `OFFSET_VERY_FAR2`
95-
--> $DIR/offset_from_ub.rs:121:14
101+
--> $DIR/offset_from_ub.rs:129:14
96102
|
97103
LL | unsafe { ptr1.offset_from(ptr2.wrapping_offset(1)) }
98104
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
99105

100-
error: aborting due to 13 previous errors
106+
error: aborting due to 14 previous errors
101107

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

0 commit comments

Comments
 (0)