Skip to content

Commit 2137d19

Browse files
authored
Rollup merge of #127275 - RalfJung:offset-from-isize-min, r=Amanieu
offset_from, offset: clearly separate safety requirements the user needs to prove from corollaries that automatically follow By landing #116675 we decided that objects larger than `isize::MAX` cannot exist in the address space of a Rust program, which lets us simplify these rules. For `offset_from`, we can even state that the *absolute* distance fits into an `isize`, and therefore exclude `isize::MIN`. This PR also changes Miri to treat an `isize::MIN` difference like the other isize-overflowing cases.
2 parents 28cc0b6 + 9ba492f commit 2137d19

File tree

6 files changed

+139
-272
lines changed

6 files changed

+139
-272
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

+38-88
Original file line numberDiff line numberDiff line change
@@ -390,37 +390,26 @@ impl<T: ?Sized> *const T {
390390
if self.is_null() { None } else { Some(unsafe { &*(self as *const MaybeUninit<T>) }) }
391391
}
392392

393-
/// Calculates the offset from a pointer.
393+
/// Adds an offset to a pointer.
394394
///
395395
/// `count` is in units of T; e.g., a `count` of 3 represents a pointer
396396
/// offset of `3 * size_of::<T>()` bytes.
397397
///
398398
/// # Safety
399399
///
400-
/// If any of the following conditions are violated, the result is Undefined
401-
/// Behavior:
400+
/// If any of the following conditions are violated, the result is Undefined Behavior:
402401
///
403-
/// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting
404-
/// pointer must be either in bounds or at the end of the same [allocated object].
405-
/// (If it is zero, then the function is always well-defined.)
402+
/// * The computed offset, `count * size_of::<T>()` bytes, must not overflow `isize`.
406403
///
407-
/// * The computed offset, **in bytes**, cannot overflow an `isize`.
404+
/// * If the computed offset is non-zero, then `self` must be derived from a pointer to some
405+
/// [allocated object], and the entire memory range between `self` and the result must be in
406+
/// bounds of that allocated object. In particular, this range must not "wrap around" the edge
407+
/// of the address space.
408408
///
409-
/// * The offset being in bounds cannot rely on "wrapping around" the address
410-
/// space. That is, the infinite-precision sum, **in bytes** must fit in a usize.
411-
///
412-
/// The compiler and standard library generally tries to ensure allocations
413-
/// never reach a size where an offset is a concern. For instance, `Vec`
414-
/// and `Box` ensure they never allocate more than `isize::MAX` bytes, so
415-
/// `vec.as_ptr().add(vec.len())` is always safe.
416-
///
417-
/// Most platforms fundamentally can't even construct such an allocation.
418-
/// For instance, no known 64-bit platform can ever serve a request
419-
/// for 2<sup>63</sup> bytes due to page-table limitations or splitting the address space.
420-
/// However, some 32-bit and 16-bit platforms may successfully serve a request for
421-
/// more than `isize::MAX` bytes with things like Physical Address
422-
/// Extension. As such, memory acquired directly from allocators or memory
423-
/// mapped files *may* be too large to handle with this function.
409+
/// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset
410+
/// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement.
411+
/// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec<T>`) is always
412+
/// safe.
424413
///
425414
/// Consider using [`wrapping_offset`] instead if these constraints are
426415
/// difficult to satisfy. The only advantage of this method is that it
@@ -611,8 +600,7 @@ impl<T: ?Sized> *const T {
611600
///
612601
/// # Safety
613602
///
614-
/// If any of the following conditions are violated, the result is Undefined
615-
/// Behavior:
603+
/// If any of the following conditions are violated, the result is Undefined Behavior:
616604
///
617605
/// * `self` and `origin` must either
618606
///
@@ -623,26 +611,10 @@ impl<T: ?Sized> *const T {
623611
/// * The distance between the pointers, in bytes, must be an exact multiple
624612
/// of the size of `T`.
625613
///
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.)
614+
/// As a consequence, the absolute distance between the pointers, in bytes, computed on
615+
/// mathematical integers (without "wrapping around"), cannot overflow an `isize`. This is
616+
/// implied by the in-bounds requirement, and the fact that no allocated object can be larger
617+
/// than `isize::MAX` bytes.
646618
///
647619
/// The requirement for pointers to be derived from the same allocated object is primarily
648620
/// needed for `const`-compatibility: the distance between pointers into *different* allocated
@@ -879,37 +851,26 @@ impl<T: ?Sized> *const T {
879851
}
880852
}
881853

882-
/// Calculates the offset from a pointer (convenience for `.offset(count as isize)`).
854+
/// Adds an offset to a pointer (convenience for `.offset(count as isize)`).
883855
///
884856
/// `count` is in units of T; e.g., a `count` of 3 represents a pointer
885857
/// offset of `3 * size_of::<T>()` bytes.
886858
///
887859
/// # Safety
888860
///
889-
/// If any of the following conditions are violated, the result is Undefined
890-
/// Behavior:
861+
/// If any of the following conditions are violated, the result is Undefined Behavior:
891862
///
892-
/// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting
893-
/// pointer must be either in bounds or at the end of the same [allocated object].
894-
/// (If it is zero, then the function is always well-defined.)
863+
/// * The computed offset, `count * size_of::<T>()` bytes, must not overflow `isize`.
895864
///
896-
/// * The computed offset, **in bytes**, cannot overflow an `isize`.
865+
/// * If the computed offset is non-zero, then `self` must be derived from a pointer to some
866+
/// [allocated object], and the entire memory range between `self` and the result must be in
867+
/// bounds of that allocated object. In particular, this range must not "wrap around" the edge
868+
/// of the address space.
897869
///
898-
/// * The offset being in bounds cannot rely on "wrapping around" the address
899-
/// space. That is, the infinite-precision sum must fit in a `usize`.
900-
///
901-
/// The compiler and standard library generally tries to ensure allocations
902-
/// never reach a size where an offset is a concern. For instance, `Vec`
903-
/// and `Box` ensure they never allocate more than `isize::MAX` bytes, so
904-
/// `vec.as_ptr().add(vec.len())` is always safe.
905-
///
906-
/// Most platforms fundamentally can't even construct such an allocation.
907-
/// For instance, no known 64-bit platform can ever serve a request
908-
/// for 2<sup>63</sup> bytes due to page-table limitations or splitting the address space.
909-
/// However, some 32-bit and 16-bit platforms may successfully serve a request for
910-
/// more than `isize::MAX` bytes with things like Physical Address
911-
/// Extension. As such, memory acquired directly from allocators or memory
912-
/// mapped files *may* be too large to handle with this function.
870+
/// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset
871+
/// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement.
872+
/// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec<T>`) is always
873+
/// safe.
913874
///
914875
/// Consider using [`wrapping_add`] instead if these constraints are
915876
/// difficult to satisfy. The only advantage of this method is that it
@@ -963,38 +924,27 @@ impl<T: ?Sized> *const T {
963924
unsafe { self.cast::<u8>().add(count).with_metadata_of(self) }
964925
}
965926

966-
/// Calculates the offset from a pointer (convenience for
927+
/// Subtracts an offset from a pointer (convenience for
967928
/// `.offset((count as isize).wrapping_neg())`).
968929
///
969930
/// `count` is in units of T; e.g., a `count` of 3 represents a pointer
970931
/// offset of `3 * size_of::<T>()` bytes.
971932
///
972933
/// # Safety
973934
///
974-
/// If any of the following conditions are violated, the result is Undefined
975-
/// Behavior:
976-
///
977-
/// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting
978-
/// pointer must be either in bounds or at the end of the same [allocated object].
979-
/// (If it is zero, then the function is always well-defined.)
980-
///
981-
/// * The computed offset cannot exceed `isize::MAX` **bytes**.
935+
/// If any of the following conditions are violated, the result is Undefined Behavior:
982936
///
983-
/// * The offset being in bounds cannot rely on "wrapping around" the address
984-
/// space. That is, the infinite-precision sum must fit in a usize.
937+
/// * The computed offset, `count * size_of::<T>()` bytes, must not overflow `isize`.
985938
///
986-
/// The compiler and standard library generally tries to ensure allocations
987-
/// never reach a size where an offset is a concern. For instance, `Vec`
988-
/// and `Box` ensure they never allocate more than `isize::MAX` bytes, so
989-
/// `vec.as_ptr().add(vec.len()).sub(vec.len())` is always safe.
939+
/// * If the computed offset is non-zero, then `self` must be derived from a pointer to some
940+
/// [allocated object], and the entire memory range between `self` and the result must be in
941+
/// bounds of that allocated object. In particular, this range must not "wrap around" the edge
942+
/// of the address space.
990943
///
991-
/// Most platforms fundamentally can't even construct such an allocation.
992-
/// For instance, no known 64-bit platform can ever serve a request
993-
/// for 2<sup>63</sup> bytes due to page-table limitations or splitting the address space.
994-
/// However, some 32-bit and 16-bit platforms may successfully serve a request for
995-
/// more than `isize::MAX` bytes with things like Physical Address
996-
/// Extension. As such, memory acquired directly from allocators or memory
997-
/// mapped files *may* be too large to handle with this function.
944+
/// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset
945+
/// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement.
946+
/// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec<T>`) is always
947+
/// safe.
998948
///
999949
/// Consider using [`wrapping_sub`] instead if these constraints are
1000950
/// difficult to satisfy. The only advantage of this method is that it

0 commit comments

Comments
 (0)