Skip to content

Commit 5600fd0

Browse files
committed
refactor 'valid for read/write' definition: exclude null
1 parent 3f808f2 commit 5600fd0

1 file changed

Lines changed: 28 additions & 24 deletions

File tree

library/core/src/ptr/mod.rs

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,19 @@
1515
//! The precise rules for validity are not determined yet. The guarantees that are
1616
//! provided at this point are very minimal:
1717
//!
18-
//! * For memory accesses of [size zero][zst], *every* pointer is valid, including the [null]
19-
//! pointer. The following points are only concerned with non-zero-sized accesses.
20-
//! * A [null] pointer is *never* valid.
21-
//! * For a pointer to be valid, it is necessary, but not always sufficient, that the pointer be
22-
//! *dereferenceable*. The [provenance] of the pointer is used to determine which [allocation]
23-
//! it is derived from; a pointer is dereferenceable if the memory range of the given size
24-
//! starting at the pointer is entirely contained within the bounds of that allocation. Note
18+
//! * A [null] pointer is *never* valid for reads/writes.
19+
//! * For memory accesses of [size zero][zst], *every* non-null pointer is valid for reads/writes.
20+
//! The following points are only concerned with non-zero-sized accesses.
21+
//! * For a pointer to be valid for reads/writes, it is necessary, but not always sufficient, that
22+
//! the pointer be *dereferenceable*. The [provenance] of the pointer is used to determine which
23+
//! [allocation] it is derived from; a pointer is dereferenceable if the memory range of the given
24+
//! size starting at the pointer is entirely contained within the bounds of that allocation. Note
2525
//! that in Rust, every (stack-allocated) variable is considered a separate allocation.
2626
//! * All accesses performed by functions in this module are *non-atomic* in the sense
2727
//! of [atomic operations] used to synchronize between threads. This means it is
2828
//! undefined behavior to perform two concurrent accesses to the same location from different
29-
//! threads unless both accesses only read from memory. Notice that this explicitly
30-
//! includes [`read_volatile`] and [`write_volatile`]: Volatile accesses cannot
31-
//! be used for inter-thread synchronization, regardless of whether they are acting on
32-
//! Rust memory or not.
33-
//! * The result of casting a reference to a pointer is valid for as long as the
29+
//! threads unless both accesses only read from memory.
30+
//! * The result of casting a reference to a pointer is valid for reads/writes for as long as the
3431
//! underlying allocation is live and no reference (just raw pointers) is used to
3532
//! access the same memory. That is, reference and pointer accesses cannot be
3633
//! interleaved.
@@ -41,6 +38,13 @@
4138
//! information, see the [book] as well as the section in the reference devoted
4239
//! to [undefined behavior][ub].
4340
//!
41+
//! Note that some operations such as [`read`] and [`write`] do allow null pointers if the total
42+
//! size of the access is zero. However, other operations internally convert pointers into
43+
//! references. Therefore, the general notion of "valid for reads/writes" excludes null pointers,
44+
//! and the specific operations that permit null pointers mention that as an exception.
45+
//! Furthermore, [`read_volatile`] and [`write_volatile`] can be used in even more situations;
46+
//! see their documentation for details.
47+
//!
4448
//! We say that a pointer is "dangling" if it is not valid for any non-zero-sized accesses. This
4549
//! means out-of-bounds pointers, pointers to freed memory, null pointers, and pointers created with
4650
//! [`NonNull::dangling`] are all dangling.
@@ -450,9 +454,9 @@ mod mut_ptr;
450454
///
451455
/// Behavior is undefined if any of the following conditions are violated:
452456
///
453-
/// * `src` must be [valid] for reads of `count * size_of::<T>()` bytes.
457+
/// * `src` must be [valid] for reads of `count * size_of::<T>()` bytes or that number must be 0.
454458
///
455-
/// * `dst` must be [valid] for writes of `count * size_of::<T>()` bytes.
459+
/// * `dst` must be [valid] for writes of `count * size_of::<T>()` bytes or that number must be 0.
456460
///
457461
/// * Both `src` and `dst` must be properly aligned.
458462
///
@@ -568,11 +572,11 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us
568572
///
569573
/// Behavior is undefined if any of the following conditions are violated:
570574
///
571-
/// * `src` must be [valid] for reads of `count * size_of::<T>()` bytes.
575+
/// * `src` must be [valid] for reads of `count * size_of::<T>()` bytes or that number must be 0.
572576
///
573-
/// * `dst` must be [valid] for writes of `count * size_of::<T>()` bytes, and must remain valid even
574-
/// when `src` is read for `count * size_of::<T>()` bytes. (This means if the memory ranges
575-
/// overlap, the `dst` pointer must not be invalidated by `src` reads.)
577+
/// * `dst` must be [valid] for writes of `count * size_of::<T>()` bytes or that number must be 0,
578+
/// and `dst` must remain valid even when `src` is read for `count * size_of::<T>()` bytes. (This
579+
/// means if the memory ranges overlap, the `dst` pointer must not be invalidated by `src` reads.)
576580
///
577581
/// * Both `src` and `dst` must be properly aligned.
578582
///
@@ -1565,7 +1569,7 @@ pub const unsafe fn replace<T>(dst: *mut T, src: T) -> T {
15651569
///
15661570
/// Behavior is undefined if any of the following conditions are violated:
15671571
///
1568-
/// * `src` must be [valid] for reads.
1572+
/// * `src` must be [valid] for reads or `T` must be a ZST.
15691573
///
15701574
/// * `src` must be properly aligned. Use [`read_unaligned`] if this is not the
15711575
/// case.
@@ -1817,7 +1821,7 @@ pub const unsafe fn read_unaligned<T>(src: *const T) -> T {
18171821
///
18181822
/// Behavior is undefined if any of the following conditions are violated:
18191823
///
1820-
/// * `dst` must be [valid] for writes.
1824+
/// * `dst` must be [valid] for writes or `T` must be a ZST.
18211825
///
18221826
/// * `dst` must be properly aligned. Use [`write_unaligned`] if this is not the
18231827
/// case.
@@ -2040,8 +2044,8 @@ pub const unsafe fn write_unaligned<T>(dst: *mut T, src: T) {
20402044
///
20412045
/// Behavior is undefined if any of the following conditions are violated:
20422046
///
2043-
/// * `src` must be either [valid] for reads, or it must point to memory outside of all Rust
2044-
/// allocations and reading from that memory must:
2047+
/// * `src` must be either [valid] for reads, or `T` must be a ZST, or `src` must point to memory
2048+
/// outside of all Rust allocations and reading from that memory must:
20452049
/// - not trap, and
20462050
/// - not cause any memory inside a Rust allocation to be modified.
20472051
///
@@ -2128,8 +2132,8 @@ pub unsafe fn read_volatile<T>(src: *const T) -> T {
21282132
///
21292133
/// Behavior is undefined if any of the following conditions are violated:
21302134
///
2131-
/// * `dst` must be either [valid] for writes, or it must point to memory outside of all Rust
2132-
/// allocations and writing to that memory must:
2135+
/// * `dst` must be either [valid] for writes, or `T` must be a ZST, or `dst` must point to memory
2136+
/// outside of all Rust allocations and writing to that memory must:
21332137
/// - not trap, and
21342138
/// - not cause any memory inside a Rust allocation to be modified.
21352139
///

0 commit comments

Comments
 (0)