Skip to content

Commit 5d49329

Browse files
committed
offset, offset_from: allow zero-byte offset on arbitrary pointers
1 parent 23a3d77 commit 5d49329

File tree

4 files changed

+45
-40
lines changed

4 files changed

+45
-40
lines changed

library/core/src/intrinsics.rs

+8-4
Original file line numberDiff line numberDiff line change
@@ -1444,10 +1444,10 @@ extern "rust-intrinsic" {
14441444
///
14451445
/// # Safety
14461446
///
1447-
/// Both the starting and resulting pointer must be either in bounds or one
1448-
/// byte past the end of an allocated object. If either pointer is out of
1449-
/// bounds or arithmetic overflow occurs then any further use of the
1450-
/// returned value will result in undefined behavior.
1447+
/// If the computed offset is non-zero, then both the starting and resulting pointer must be
1448+
/// either in bounds or one byte past the end of an allocated object. If either pointer is out
1449+
/// of bounds or arithmetic overflow occurs then any further use of the returned value will
1450+
/// result in undefined behavior.
14511451
///
14521452
/// The stabilized version of this intrinsic is [`pointer::offset`].
14531453
#[must_use = "returns a new pointer rather than modifying its argument"]
@@ -2361,6 +2361,10 @@ extern "rust-intrinsic" {
23612361
pub fn nontemporal_store<T>(ptr: *mut T, val: T);
23622362

23632363
/// See documentation of `<*const T>::offset_from` for details.
2364+
///
2365+
/// However, the actual safety contract of the intrinsic is slightly weaker than the one
2366+
/// for these functions: when `ptr.addr() == base.addr()`, the intrinsic is always
2367+
/// well-defined, no matter the provenance of these pointers.
23642368
#[rustc_const_stable(feature = "const_ptr_offset_from", since = "1.65.0")]
23652369
#[rustc_nounwind]
23662370
pub fn ptr_offset_from<T>(ptr: *const T, base: *const T) -> isize;

library/core/src/ptr/const_ptr.rs

+17-14
Original file line numberDiff line numberDiff line change
@@ -417,8 +417,9 @@ impl<T: ?Sized> *const T {
417417
/// If any of the following conditions are violated, the result is Undefined
418418
/// Behavior:
419419
///
420-
/// * Both the starting and resulting pointer must be either in bounds or one
421-
/// byte past the end of the same [allocated object].
420+
/// * If the computed offset is non-zero, then both the starting and resulting pointer must be
421+
/// either in bounds or one byte past the end of the same [allocated object].
422+
/// (If it is zero, then the function is allays well-defined.)
422423
///
423424
/// * The computed offset, **in bytes**, cannot overflow an `isize`.
424425
///
@@ -628,11 +629,11 @@ impl<T: ?Sized> *const T {
628629
/// If any of the following conditions are violated, the result is Undefined
629630
/// Behavior:
630631
///
631-
/// * Both `self` and `origin` must be either in bounds or one
632-
/// byte past the end of the same [allocated object].
632+
/// * `self` and `origin` must either
633633
///
634-
/// * Both pointers must be *derived from* a pointer to the same object.
635-
/// (See below for an example.)
634+
/// * both be *derived from* a pointer to the same [allocated object], and they must be either
635+
/// in bounds or one byte past the end of that object. (See below for an example.)
636+
/// * or both be derived from an integer literal/constant, and point to the same address.
636637
///
637638
/// * The distance between the pointers, in bytes, must be an exact multiple
638639
/// of the size of `T`.
@@ -695,14 +696,14 @@ impl<T: ?Sized> *const T {
695696
/// let ptr1 = Box::into_raw(Box::new(0u8)) as *const u8;
696697
/// let ptr2 = Box::into_raw(Box::new(1u8)) as *const u8;
697698
/// let diff = (ptr2 as isize).wrapping_sub(ptr1 as isize);
698-
/// // Make ptr2_other an "alias" of ptr2, but derived from ptr1.
699-
/// let ptr2_other = (ptr1 as *const u8).wrapping_offset(diff);
699+
/// // Make ptr2_other an "alias" of ptr2.add(1), but derived from ptr1.
700+
/// let ptr2_other = (ptr1 as *const u8).wrapping_offset(diff).wrapping_offset(1);
700701
/// assert_eq!(ptr2 as usize, ptr2_other as usize);
701702
/// // Since ptr2_other and ptr2 are derived from pointers to different objects,
702703
/// // computing their offset is undefined behavior, even though
703-
/// // they point to the same address!
704+
/// // they point to addresses that are in-bounds of the same object!
704705
/// unsafe {
705-
/// let zero = ptr2_other.offset_from(ptr2); // Undefined Behavior
706+
/// let one = ptr2_other.offset_from(ptr2); // Undefined Behavior
706707
/// }
707708
/// ```
708709
#[stable(feature = "ptr_offset_from", since = "1.47.0")]
@@ -894,8 +895,9 @@ impl<T: ?Sized> *const T {
894895
/// If any of the following conditions are violated, the result is Undefined
895896
/// Behavior:
896897
///
897-
/// * Both the starting and resulting pointer must be either in bounds or one
898-
/// byte past the end of the same [allocated object].
898+
/// * If the computed offset is non-zero, then both the starting and resulting pointer must be
899+
/// either in bounds or one byte past the end of the same [allocated object].
900+
/// (If it is zero, then the function is allays well-defined.)
899901
///
900902
/// * The computed offset, **in bytes**, cannot overflow an `isize`.
901903
///
@@ -978,8 +980,9 @@ impl<T: ?Sized> *const T {
978980
/// If any of the following conditions are violated, the result is Undefined
979981
/// Behavior:
980982
///
981-
/// * Both the starting and resulting pointer must be either in bounds or one
982-
/// byte past the end of the same [allocated object].
983+
/// * If the computed offset is non-zero, then both the starting and resulting pointer must be
984+
/// either in bounds or one byte past the end of the same [allocated object].
985+
/// (If it is zero, then the function is allays well-defined.)
983986
///
984987
/// * The computed offset cannot exceed `isize::MAX` **bytes**.
985988
///

library/core/src/ptr/mod.rs

+3-8
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,13 @@
1515
//! The precise rules for validity are not determined yet. The guarantees that are
1616
//! provided at this point are very minimal:
1717
//!
18-
//! * A [null] pointer is *never* valid, not even for accesses of [size zero][zst].
18+
//! * For operations of [size zero][zst], *every* pointer is valid, including the [null] pointer.
19+
//! The following points are only concerned with non-zero-sized accesses.
20+
//! * A [null] pointer is *never* valid.
1921
//! * For a pointer to be valid, it is necessary, but not always sufficient, that the pointer
2022
//! be *dereferenceable*: the memory range of the given size starting at the pointer must all be
2123
//! within the bounds of a single allocated object. Note that in Rust,
2224
//! every (stack-allocated) variable is considered a separate allocated object.
23-
//! * Even for operations of [size zero][zst], the pointer must not be pointing to deallocated
24-
//! memory, i.e., deallocation makes pointers invalid even for zero-sized operations. However,
25-
//! casting any non-zero integer *literal* to a pointer is valid for zero-sized accesses, even if
26-
//! some memory happens to exist at that address and gets deallocated. This corresponds to writing
27-
//! your own allocator: allocating zero-sized objects is not very hard. The canonical way to
28-
//! obtain a pointer that is valid for zero-sized accesses is [`NonNull::dangling`].
29-
//FIXME: mention `ptr::invalid` above, once it is stable.
3025
//! * All accesses performed by functions in this module are *non-atomic* in the sense
3126
//! of [atomic operations] used to synchronize between threads. This means it is
3227
//! undefined behavior to perform two concurrent accesses to the same location from different

library/core/src/ptr/mut_ptr.rs

+17-14
Original file line numberDiff line numberDiff line change
@@ -430,8 +430,9 @@ impl<T: ?Sized> *mut T {
430430
/// If any of the following conditions are violated, the result is Undefined
431431
/// Behavior:
432432
///
433-
/// * Both the starting and resulting pointer must be either in bounds or one
434-
/// byte past the end of the same [allocated object].
433+
/// * If the computed offset is non-zero, then both the starting and resulting pointer must be
434+
/// either in bounds or one byte past the end of the same [allocated object].
435+
/// (If it is zero, then the function is allays well-defined.)
435436
///
436437
/// * The computed offset, **in bytes**, cannot overflow an `isize`.
437438
///
@@ -802,11 +803,11 @@ impl<T: ?Sized> *mut T {
802803
/// If any of the following conditions are violated, the result is Undefined
803804
/// Behavior:
804805
///
805-
/// * Both `self` and `origin` must be either in bounds or one
806-
/// byte past the end of the same [allocated object].
806+
/// * `self` and `origin` must either
807807
///
808-
/// * Both pointers must be *derived from* a pointer to the same object.
809-
/// (See below for an example.)
808+
/// * both be *derived from* a pointer to the same [allocated object], and they must be either
809+
/// in bounds or one byte past the end of that object. (See below for an example.)
810+
/// * or both be derived from an integer literal/constant, and point to the same address.
810811
///
811812
/// * The distance between the pointers, in bytes, must be an exact multiple
812813
/// of the size of `T`.
@@ -869,14 +870,14 @@ impl<T: ?Sized> *mut T {
869870
/// let ptr1 = Box::into_raw(Box::new(0u8));
870871
/// let ptr2 = Box::into_raw(Box::new(1u8));
871872
/// let diff = (ptr2 as isize).wrapping_sub(ptr1 as isize);
872-
/// // Make ptr2_other an "alias" of ptr2, but derived from ptr1.
873-
/// let ptr2_other = (ptr1 as *mut u8).wrapping_offset(diff);
873+
/// // Make ptr2_other an "alias" of ptr2.add(1), but derived from ptr1.
874+
/// let ptr2_other = (ptr1 as *mut u8).wrapping_offset(diff).wrapping_offset(1);
874875
/// assert_eq!(ptr2 as usize, ptr2_other as usize);
875876
/// // Since ptr2_other and ptr2 are derived from pointers to different objects,
876877
/// // computing their offset is undefined behavior, even though
877-
/// // they point to the same address!
878+
/// // they point to addresses that are in-bounds of the same object!
878879
/// unsafe {
879-
/// let zero = ptr2_other.offset_from(ptr2); // Undefined Behavior
880+
/// let one = ptr2_other.offset_from(ptr2); // Undefined Behavior
880881
/// }
881882
/// ```
882883
#[stable(feature = "ptr_offset_from", since = "1.47.0")]
@@ -993,8 +994,9 @@ impl<T: ?Sized> *mut T {
993994
/// If any of the following conditions are violated, the result is Undefined
994995
/// Behavior:
995996
///
996-
/// * Both the starting and resulting pointer must be either in bounds or one
997-
/// byte past the end of the same [allocated object].
997+
/// * If the computed offset is non-zero, then both the starting and resulting pointer must be
998+
/// either in bounds or one byte past the end of the same [allocated object].
999+
/// (If it is zero, then the function is allays well-defined.)
9981000
///
9991001
/// * The computed offset, **in bytes**, cannot overflow an `isize`.
10001002
///
@@ -1077,8 +1079,9 @@ impl<T: ?Sized> *mut T {
10771079
/// If any of the following conditions are violated, the result is Undefined
10781080
/// Behavior:
10791081
///
1080-
/// * Both the starting and resulting pointer must be either in bounds or one
1081-
/// byte past the end of the same [allocated object].
1082+
/// * If the computed offset is non-zero, then both the starting and resulting pointer must be
1083+
/// either in bounds or one byte past the end of the same [allocated object].
1084+
/// (If it is zero, then the function is allays well-defined.)
10821085
///
10831086
/// * The computed offset cannot exceed `isize::MAX` **bytes**.
10841087
///

0 commit comments

Comments
 (0)