From 273d253ce6855381c9b7c36d3ea92581dd54becb Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 3 Jul 2024 16:04:37 +0200 Subject: [PATCH 1/2] offset_from: "the difference must fit in an isize" is a corollary also, isize::MIN is an impossible distance --- .../src/interpret/intrinsics.rs | 4 +-- library/core/src/ptr/const_ptr.rs | 27 +++----------- library/core/src/ptr/mut_ptr.rs | 27 +++----------- library/core/src/ptr/non_null.rs | 35 +++++-------------- tests/ui/consts/offset_from_ub.rs | 8 +++++ tests/ui/consts/offset_from_ub.stderr | 16 ++++++--- 6 files changed, 40 insertions(+), 77 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 1d54da267ee..d86f1a7a34f 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -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, diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 5537d26669a..039dc6d7a49 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -611,8 +611,7 @@ impl *const T { /// /// # Safety /// - /// If any of the following conditions are violated, the result is Undefined - /// Behavior: + /// If any of the following conditions are violated, the result is Undefined Behavior: /// /// * `self` and `origin` must either /// @@ -623,26 +622,10 @@ impl *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 263 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` bytes. /// /// 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 diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 30d5b2cfc5a..cbd3de1268a 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -836,8 +836,7 @@ impl *mut T { /// /// # Safety /// - /// If any of the following conditions are violated, the result is Undefined - /// Behavior: + /// If any of the following conditions are violated, the result is Undefined Behavior: /// /// * `self` and `origin` must either /// @@ -848,26 +847,10 @@ impl *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 263 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` bytes. /// /// 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 diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 0504a0fc32f..0b1bd5aeebb 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -761,38 +761,21 @@ impl NonNull { /// /// # Safety /// - /// If any of the following conditions are violated, the result is Undefined - /// Behavior: + /// If any of the following conditions are violated, the result is Undefined Behavior: /// - /// * Both `self` and `origin` must be either in bounds or one - /// byte past the end of the same [allocated object]. + /// * `self` and `origin` must either /// - /// * Both pointers must be *derived from* a pointer to the same object. - /// (See below for an example.) + /// * both be *derived from* a pointer to the same [allocated object], and the memory range between + /// the two pointers must be either empty or in bounds of that object. (See below for an example.) + /// * or both be derived from an integer literal/constant, and point to the same address. /// /// * 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 263 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` bytes. /// /// 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 diff --git a/tests/ui/consts/offset_from_ub.rs b/tests/ui/consts/offset_from_ub.rs index 57767e96596..e71f88b8d5f 100644 --- a/tests/ui/consts/offset_from_ub.rs +++ b/tests/ui/consts/offset_from_ub.rs @@ -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']; diff --git a/tests/ui/consts/offset_from_ub.stderr b/tests/ui/consts/offset_from_ub.stderr index 65f75a6e058..7caf6247b9e 100644 --- a/tests/ui/consts/offset_from_ub.stderr +++ b/tests/ui/consts/offset_from_ub.stderr @@ -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 @@ -79,7 +85,7 @@ error[E0080]: evaluation of constant value failed note: inside `std::ptr::const_ptr::::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) } | ^^^^^^^^^^^^^^^^^^^^^^ @@ -92,11 +98,11 @@ error[E0080]: evaluation of constant value failed note: inside `std::ptr::const_ptr::::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`. From 9ba492f2797250b2fcaa38d483ca5c23bf0bd8dd Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 3 Jul 2024 16:42:33 +0200 Subject: [PATCH 2/2] also remove redundant requirements from offset() --- library/core/src/ptr/const_ptr.rs | 101 ++++++++++-------------------- library/core/src/ptr/mut_ptr.rs | 99 ++++++++++------------------- library/core/src/ptr/non_null.rs | 96 ++++++++++------------------ 3 files changed, 100 insertions(+), 196 deletions(-) diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 039dc6d7a49..68cf8203433 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -390,37 +390,26 @@ impl *const T { if self.is_null() { None } else { Some(unsafe { &*(self as *const MaybeUninit) }) } } - /// Calculates the offset from a pointer. + /// Adds an offset to a pointer. /// /// `count` is in units of T; e.g., a `count` of 3 represents a pointer /// offset of `3 * size_of::()` bytes. /// /// # Safety /// - /// If any of the following conditions are violated, the result is Undefined - /// Behavior: + /// If any of the following conditions are violated, the result is Undefined Behavior: /// - /// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting - /// pointer must be either in bounds or at the end of the same [allocated object]. - /// (If it is zero, then the function is always well-defined.) + /// * The computed offset, `count * size_of::()` bytes, must not overflow `isize`. /// - /// * The computed offset, **in bytes**, cannot overflow an `isize`. + /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some + /// [allocated object], and the entire memory range between `self` and the result must be in + /// bounds of that allocated object. In particular, this range must not "wrap around" the edge + /// of the address space. /// - /// * The offset being in bounds cannot rely on "wrapping around" the address - /// space. That is, the infinite-precision sum, **in bytes** must fit in a usize. - /// - /// The compiler and standard library generally tries to ensure 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 - /// `vec.as_ptr().add(vec.len())` is always safe. - /// - /// Most platforms fundamentally can't even construct such an allocation. - /// For instance, no known 64-bit platform can ever serve a request - /// for 263 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. + /// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset + /// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement. + /// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec`) is always + /// safe. /// /// Consider using [`wrapping_offset`] instead if these constraints are /// difficult to satisfy. The only advantage of this method is that it @@ -622,7 +611,7 @@ impl *const T { /// * The distance between the pointers, in bytes, must be an exact multiple /// of the size of `T`. /// - /// As a consequence, the absolute distance between the pointers, **in bytes**, computed on + /// 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` bytes. @@ -862,37 +851,26 @@ impl *const T { } } - /// Calculates the offset from a pointer (convenience for `.offset(count as isize)`). + /// Adds an offset to a pointer (convenience for `.offset(count as isize)`). /// /// `count` is in units of T; e.g., a `count` of 3 represents a pointer /// offset of `3 * size_of::()` bytes. /// /// # Safety /// - /// If any of the following conditions are violated, the result is Undefined - /// Behavior: + /// If any of the following conditions are violated, the result is Undefined Behavior: /// - /// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting - /// pointer must be either in bounds or at the end of the same [allocated object]. - /// (If it is zero, then the function is always well-defined.) + /// * The computed offset, `count * size_of::()` bytes, must not overflow `isize`. /// - /// * The computed offset, **in bytes**, cannot overflow an `isize`. + /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some + /// [allocated object], and the entire memory range between `self` and the result must be in + /// bounds of that allocated object. In particular, this range must not "wrap around" the edge + /// of the address space. /// - /// * The offset being in bounds cannot rely on "wrapping around" the address - /// space. That is, the infinite-precision sum must fit in a `usize`. - /// - /// The compiler and standard library generally tries to ensure 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 - /// `vec.as_ptr().add(vec.len())` is always safe. - /// - /// Most platforms fundamentally can't even construct such an allocation. - /// For instance, no known 64-bit platform can ever serve a request - /// for 263 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. + /// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset + /// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement. + /// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec`) is always + /// safe. /// /// Consider using [`wrapping_add`] instead if these constraints are /// difficult to satisfy. The only advantage of this method is that it @@ -946,7 +924,7 @@ impl *const T { unsafe { self.cast::().add(count).with_metadata_of(self) } } - /// Calculates the offset from a pointer (convenience for + /// Subtracts an offset from a pointer (convenience for /// `.offset((count as isize).wrapping_neg())`). /// /// `count` is in units of T; e.g., a `count` of 3 represents a pointer @@ -954,30 +932,19 @@ impl *const T { /// /// # Safety /// - /// If any of the following conditions are violated, the result is Undefined - /// Behavior: + /// If any of the following conditions are violated, the result is Undefined Behavior: /// - /// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting - /// pointer must be either in bounds or at the end of the same [allocated object]. - /// (If it is zero, then the function is always well-defined.) + /// * The computed offset, `count * size_of::()` bytes, must not overflow `isize`. /// - /// * The computed offset cannot exceed `isize::MAX` **bytes**. + /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some + /// [allocated object], and the entire memory range between `self` and the result must be in + /// bounds of that allocated object. In particular, this range must not "wrap around" the edge + /// of the address space. /// - /// * The offset being in bounds cannot rely on "wrapping around" the address - /// space. That is, the infinite-precision sum must fit in a usize. - /// - /// The compiler and standard library generally tries to ensure 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 - /// `vec.as_ptr().add(vec.len()).sub(vec.len())` is always safe. - /// - /// Most platforms fundamentally can't even construct such an allocation. - /// For instance, no known 64-bit platform can ever serve a request - /// for 263 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. + /// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset + /// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement. + /// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec`) is always + /// safe. /// /// Consider using [`wrapping_sub`] instead if these constraints are /// difficult to satisfy. The only advantage of this method is that it diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index cbd3de1268a..0dc910db5b9 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -404,37 +404,26 @@ impl *mut T { if self.is_null() { None } else { Some(unsafe { &*(self as *const MaybeUninit) }) } } - /// Calculates the offset from a pointer. + /// Adds an offset to a pointer. /// /// `count` is in units of T; e.g., a `count` of 3 represents a pointer /// offset of `3 * size_of::()` bytes. /// /// # Safety /// - /// If any of the following conditions are violated, the result is Undefined - /// Behavior: + /// If any of the following conditions are violated, the result is Undefined Behavior: /// - /// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting - /// pointer must be either in bounds or at the end of the same [allocated object]. - /// (If it is zero, then the function is always well-defined.) + /// * The computed offset, `count * size_of::()` bytes, must not overflow `isize`. /// - /// * The computed offset, **in bytes**, cannot overflow an `isize`. + /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some + /// [allocated object], and the entire memory range between `self` and the result must be in + /// bounds of that allocated object. In particular, this range must not "wrap around" the edge + /// of the address space. /// - /// * The offset being in bounds cannot rely on "wrapping around" the address - /// space. That is, the infinite-precision sum, **in bytes** must fit in a usize. - /// - /// The compiler and standard library generally tries to ensure 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 - /// `vec.as_ptr().add(vec.len())` is always safe. - /// - /// Most platforms fundamentally can't even construct such an allocation. - /// For instance, no known 64-bit platform can ever serve a request - /// for 263 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. + /// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset + /// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement. + /// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec`) is always + /// safe. /// /// Consider using [`wrapping_offset`] instead if these constraints are /// difficult to satisfy. The only advantage of this method is that it @@ -1003,37 +992,26 @@ impl *mut T { unsafe { (self as *const T).sub_ptr(origin) } } - /// Calculates the offset from a pointer (convenience for `.offset(count as isize)`). + /// Adds an offset to a pointer (convenience for `.offset(count as isize)`). /// /// `count` is in units of T; e.g., a `count` of 3 represents a pointer /// offset of `3 * size_of::()` bytes. /// /// # Safety /// - /// If any of the following conditions are violated, the result is Undefined - /// Behavior: + /// If any of the following conditions are violated, the result is Undefined Behavior: /// - /// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting - /// pointer must be either in bounds or at the end of the same [allocated object]. - /// (If it is zero, then the function is always well-defined.) + /// * The computed offset, `count * size_of::()` bytes, must not overflow `isize`. /// - /// * The computed offset, **in bytes**, cannot overflow an `isize`. + /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some + /// [allocated object], and the entire memory range between `self` and the result must be in + /// bounds of that allocated object. In particular, this range must not "wrap around" the edge + /// of the address space. /// - /// * The offset being in bounds cannot rely on "wrapping around" the address - /// space. That is, the infinite-precision sum must fit in a `usize`. - /// - /// The compiler and standard library generally tries to ensure 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 - /// `vec.as_ptr().add(vec.len())` is always safe. - /// - /// Most platforms fundamentally can't even construct such an allocation. - /// For instance, no known 64-bit platform can ever serve a request - /// for 263 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. + /// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset + /// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement. + /// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec`) is always + /// safe. /// /// Consider using [`wrapping_add`] instead if these constraints are /// difficult to satisfy. The only advantage of this method is that it @@ -1087,7 +1065,7 @@ impl *mut T { unsafe { self.cast::().add(count).with_metadata_of(self) } } - /// Calculates the offset from a pointer (convenience for + /// Subtracts an offset from a pointer (convenience for /// `.offset((count as isize).wrapping_neg())`). /// /// `count` is in units of T; e.g., a `count` of 3 represents a pointer @@ -1095,30 +1073,19 @@ impl *mut T { /// /// # Safety /// - /// If any of the following conditions are violated, the result is Undefined - /// Behavior: + /// If any of the following conditions are violated, the result is Undefined Behavior: /// - /// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting - /// pointer must be either in bounds or at the end of the same [allocated object]. - /// (If it is zero, then the function is always well-defined.) + /// * The computed offset, `count * size_of::()` bytes, must not overflow `isize`. /// - /// * The computed offset cannot exceed `isize::MAX` **bytes**. + /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some + /// [allocated object], and the entire memory range between `self` and the result must be in + /// bounds of that allocated object. In particular, this range must not "wrap around" the edge + /// of the address space. /// - /// * The offset being in bounds cannot rely on "wrapping around" the address - /// space. That is, the infinite-precision sum must fit in a usize. - /// - /// The compiler and standard library generally tries to ensure 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 - /// `vec.as_ptr().add(vec.len()).sub(vec.len())` is always safe. - /// - /// Most platforms fundamentally can't even construct such an allocation. - /// For instance, no known 64-bit platform can ever serve a request - /// for 263 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. + /// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset + /// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement. + /// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec`) is always + /// safe. /// /// Consider using [`wrapping_sub`] instead if these constraints are /// difficult to satisfy. The only advantage of this method is that it diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 0b1bd5aeebb..75a99e14fda 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -476,36 +476,26 @@ impl NonNull { unsafe { NonNull { pointer: self.as_ptr() as *mut U } } } - /// Calculates the offset from a pointer. + /// Adds an offset to a pointer. /// /// `count` is in units of T; e.g., a `count` of 3 represents a pointer /// offset of `3 * size_of::()` bytes. /// /// # Safety /// - /// If any of the following conditions are violated, the result is Undefined - /// Behavior: + /// If any of the following conditions are violated, the result is Undefined Behavior: /// - /// * Both the starting and resulting pointer must be either in bounds or one - /// byte past the end of the same [allocated object]. + /// * The computed offset, `count * size_of::()` bytes, must not overflow `isize`. /// - /// * The computed offset, **in bytes**, cannot overflow an `isize`. + /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some + /// [allocated object], and the entire memory range between `self` and the result must be in + /// bounds of that allocated object. In particular, this range must not "wrap around" the edge + /// of the address space. /// - /// * The offset being in bounds cannot rely on "wrapping around" the address - /// space. That is, the infinite-precision sum, **in bytes** must fit in a usize. - /// - /// The compiler and standard library generally tries to ensure 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 - /// `vec.as_ptr().add(vec.len())` is always safe. - /// - /// Most platforms fundamentally can't even construct such an allocation. - /// For instance, no known 64-bit platform can ever serve a request - /// for 263 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. + /// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset + /// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement. + /// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec`) is always + /// safe. /// /// [allocated object]: crate::ptr#allocated-object /// @@ -562,36 +552,26 @@ impl NonNull { unsafe { NonNull { pointer: self.pointer.byte_offset(count) } } } - /// Calculates the offset from a pointer (convenience for `.offset(count as isize)`). + /// Adds an offset to a pointer (convenience for `.offset(count as isize)`). /// /// `count` is in units of T; e.g., a `count` of 3 represents a pointer /// offset of `3 * size_of::()` bytes. /// /// # Safety /// - /// If any of the following conditions are violated, the result is Undefined - /// Behavior: + /// If any of the following conditions are violated, the result is Undefined Behavior: /// - /// * Both the starting and resulting pointer must be either in bounds or one - /// byte past the end of the same [allocated object]. + /// * The computed offset, `count * size_of::()` bytes, must not overflow `isize`. /// - /// * The computed offset, **in bytes**, cannot overflow an `isize`. + /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some + /// [allocated object], and the entire memory range between `self` and the result must be in + /// bounds of that allocated object. In particular, this range must not "wrap around" the edge + /// of the address space. /// - /// * The offset being in bounds cannot rely on "wrapping around" the address - /// space. That is, the infinite-precision sum must fit in a `usize`. - /// - /// The compiler and standard library generally tries to ensure 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 - /// `vec.as_ptr().add(vec.len())` is always safe. - /// - /// Most platforms fundamentally can't even construct such an allocation. - /// For instance, no known 64-bit platform can ever serve a request - /// for 263 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. + /// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset + /// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement. + /// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec`) is always + /// safe. /// /// [allocated object]: crate::ptr#allocated-object /// @@ -649,7 +629,7 @@ impl NonNull { unsafe { NonNull { pointer: self.pointer.byte_add(count) } } } - /// Calculates the offset from a pointer (convenience for + /// Subtracts an offset from a pointer (convenience for /// `.offset((count as isize).wrapping_neg())`). /// /// `count` is in units of T; e.g., a `count` of 3 represents a pointer @@ -657,29 +637,19 @@ impl NonNull { /// /// # Safety /// - /// If any of the following conditions are violated, the result is Undefined - /// Behavior: + /// If any of the following conditions are violated, the result is Undefined Behavior: /// - /// * Both the starting and resulting pointer must be either in bounds or one - /// byte past the end of the same [allocated object]. + /// * The computed offset, `count * size_of::()` bytes, must not overflow `isize`. /// - /// * The computed offset cannot exceed `isize::MAX` **bytes**. + /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some + /// [allocated object], and the entire memory range between `self` and the result must be in + /// bounds of that allocated object. In particular, this range must not "wrap around" the edge + /// of the address space. /// - /// * The offset being in bounds cannot rely on "wrapping around" the address - /// space. That is, the infinite-precision sum must fit in a usize. - /// - /// The compiler and standard library generally tries to ensure 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 - /// `vec.as_ptr().add(vec.len()).sub(vec.len())` is always safe. - /// - /// Most platforms fundamentally can't even construct such an allocation. - /// For instance, no known 64-bit platform can ever serve a request - /// for 263 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. + /// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset + /// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement. + /// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec`) is always + /// safe. /// /// [allocated object]: crate::ptr#allocated-object ///