Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rename ptr::invalid -> ptr::without_provenance #117658

Merged
merged 1 commit into from
Feb 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/rustc_arena/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ impl<T> ArenaChunk<T> {
unsafe {
if mem::size_of::<T>() == 0 {
// A pointer as large as possible for zero-sized elements.
ptr::invalid_mut(!0)
ptr::without_provenance_mut(!0)
} else {
self.start().add(self.storage.len())
}
Expand Down
8 changes: 6 additions & 2 deletions library/alloc/src/rc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2804,7 +2804,9 @@ impl<T> Weak<T> {
#[must_use]
pub const fn new() -> Weak<T> {
Weak {
ptr: unsafe { NonNull::new_unchecked(ptr::invalid_mut::<RcBox<T>>(usize::MAX)) },
ptr: unsafe {
NonNull::new_unchecked(ptr::without_provenance_mut::<RcBox<T>>(usize::MAX))
},
alloc: Global,
}
}
Expand All @@ -2829,7 +2831,9 @@ impl<T, A: Allocator> Weak<T, A> {
#[unstable(feature = "allocator_api", issue = "32838")]
pub fn new_in(alloc: A) -> Weak<T, A> {
Weak {
ptr: unsafe { NonNull::new_unchecked(ptr::invalid_mut::<RcBox<T>>(usize::MAX)) },
ptr: unsafe {
NonNull::new_unchecked(ptr::without_provenance_mut::<RcBox<T>>(usize::MAX))
},
alloc,
}
}
Expand Down
8 changes: 6 additions & 2 deletions library/alloc/src/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2555,7 +2555,9 @@ impl<T> Weak<T> {
#[must_use]
pub const fn new() -> Weak<T> {
Weak {
ptr: unsafe { NonNull::new_unchecked(ptr::invalid_mut::<ArcInner<T>>(usize::MAX)) },
ptr: unsafe {
NonNull::new_unchecked(ptr::without_provenance_mut::<ArcInner<T>>(usize::MAX))
},
alloc: Global,
}
}
Expand Down Expand Up @@ -2583,7 +2585,9 @@ impl<T, A: Allocator> Weak<T, A> {
#[unstable(feature = "allocator_api", issue = "32838")]
pub fn new_in(alloc: A) -> Weak<T, A> {
Weak {
ptr: unsafe { NonNull::new_unchecked(ptr::invalid_mut::<ArcInner<T>>(usize::MAX)) },
ptr: unsafe {
NonNull::new_unchecked(ptr::without_provenance_mut::<ArcInner<T>>(usize::MAX))
},
alloc,
}
}
Expand Down
14 changes: 7 additions & 7 deletions library/alloc/tests/fmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,14 @@ fn test_format_macro_interface() {
t!(format!("{}", "foo"), "foo");
t!(format!("{}", "foo".to_string()), "foo");
if cfg!(target_pointer_width = "32") {
t!(format!("{:#p}", ptr::invalid::<isize>(0x1234)), "0x00001234");
t!(format!("{:#p}", ptr::invalid_mut::<isize>(0x1234)), "0x00001234");
t!(format!("{:#p}", ptr::without_provenance::<isize>(0x1234)), "0x00001234");
t!(format!("{:#p}", ptr::without_provenance_mut::<isize>(0x1234)), "0x00001234");
} else {
t!(format!("{:#p}", ptr::invalid::<isize>(0x1234)), "0x0000000000001234");
t!(format!("{:#p}", ptr::invalid_mut::<isize>(0x1234)), "0x0000000000001234");
t!(format!("{:#p}", ptr::without_provenance::<isize>(0x1234)), "0x0000000000001234");
t!(format!("{:#p}", ptr::without_provenance_mut::<isize>(0x1234)), "0x0000000000001234");
}
t!(format!("{:p}", ptr::invalid::<isize>(0x1234)), "0x1234");
t!(format!("{:p}", ptr::invalid_mut::<isize>(0x1234)), "0x1234");
t!(format!("{:p}", ptr::without_provenance::<isize>(0x1234)), "0x1234");
t!(format!("{:p}", ptr::without_provenance_mut::<isize>(0x1234)), "0x1234");
t!(format!("{A:x}"), "aloha");
t!(format!("{B:X}"), "adios");
t!(format!("foo {} ☃☃☃☃☃☃", "bar"), "foo bar ☃☃☃☃☃☃");
Expand Down Expand Up @@ -208,7 +208,7 @@ fn test_format_macro_interface() {
{
let val = usize::MAX;
let exp = format!("{val:#x}");
t!(format!("{:p}", std::ptr::invalid::<isize>(val)), exp);
t!(format!("{:p}", std::ptr::without_provenance::<isize>(val)), exp);
}

// Escaping
Expand Down
2 changes: 1 addition & 1 deletion library/alloc/tests/vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2575,7 +2575,7 @@ fn test_box_zero_allocator() {
assert!(state.0.insert(addr));
state.1 += 1;
std::println!("allocating {addr}");
std::ptr::invalid_mut(addr)
std::ptr::without_provenance_mut(addr)
} else {
unsafe { std::alloc::alloc(layout) }
};
Expand Down
2 changes: 1 addition & 1 deletion library/core/src/alloc/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ impl Layout {
#[inline]
pub const fn dangling(&self) -> NonNull<u8> {
// SAFETY: align is guaranteed to be non-zero
unsafe { NonNull::new_unchecked(crate::ptr::invalid_mut::<u8>(self.align())) }
unsafe { NonNull::new_unchecked(crate::ptr::without_provenance_mut::<u8>(self.align())) }
}

/// Creates a layout describing the record that can hold a value
Expand Down
2 changes: 1 addition & 1 deletion library/core/src/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1155,7 +1155,7 @@ extern "rust-intrinsic" {
///
/// Transmuting pointers *to* integers in a `const` context is [undefined behavior][ub],
/// unless the pointer was originally created *from* an integer.
/// (That includes this function specifically, integer-to-pointer casts, and helpers like [`invalid`][crate::ptr::invalid],
/// (That includes this function specifically, integer-to-pointer casts, and helpers like [`invalid`][crate::ptr::dangling],
/// but also semantically-equivalent conversions such as punning through `repr(C)` union fields.)
/// Any attempt to use the resulting value for integer operations will abort const-evaluation.
/// (And even outside `const`, such transmutation is touching on many unspecified aspects of the
Expand Down
2 changes: 1 addition & 1 deletion library/core/src/ptr/const_ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ impl<T: ?Sized> *const T {
///
/// This is similar to `self as usize`, which semantically discards *provenance* and
/// *address-space* information. However, unlike `self as usize`, casting the returned address
/// back to a pointer yields [`invalid`][], which is undefined behavior to dereference. To
/// back to a pointer yields a [pointer without provenance][without_provenance], which is undefined behavior to dereference. To
/// properly restore the lost information and obtain a dereferenceable pointer, use
/// [`with_addr`][pointer::with_addr] or [`map_addr`][pointer::map_addr].
///
Expand Down
114 changes: 71 additions & 43 deletions library/core/src/ptr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
//!
//! # Safety
//!
//! Many functions in this module take raw pointers as arguments and read from
//! or write to them. For this to be safe, these pointers must be *valid*.
//! Whether a pointer is valid depends on the operation it is used for
//! (read or write), and the extent of the memory that is accessed (i.e.,
//! how many bytes are read/written). Most functions use `*mut T` and `*const T`
//! to access only a single value, in which case the documentation omits the size
//! and implicitly assumes it to be `size_of::<T>()` bytes.
//! Many functions in this module take raw pointers as arguments and read from or write to them. For
//! this to be safe, these pointers must be *valid* for the given access. Whether a pointer is valid
//! depends on the operation it is used for (read or write), and the extent of the memory that is
//! accessed (i.e., how many bytes are read/written) -- it makes no sense to ask "is this pointer
//! valid"; one has to ask "is this pointer valid for a given access". Most functions use `*mut T`
//! and `*const T` to access only a single value, in which case the documentation omits the size and
//! implicitly assumes it to be `size_of::<T>()` bytes.
//!
//! The precise rules for validity are not determined yet. The guarantees that are
//! provided at this point are very minimal:
Expand All @@ -26,7 +26,7 @@
//! some memory happens to exist at that address and gets deallocated. This corresponds to writing
//! your own allocator: allocating zero-sized objects is not very hard. The canonical way to
//! obtain a pointer that is valid for zero-sized accesses is [`NonNull::dangling`].
//FIXME: mention `ptr::invalid` above, once it is stable.
//FIXME: mention `ptr::dangling` above, once it is stable.
//! * All accesses performed by functions in this module are *non-atomic* in the sense
//! of [atomic operations] used to synchronize between threads. This means it is
//! undefined behavior to perform two concurrent accesses to the same location from different
Expand All @@ -44,6 +44,10 @@
//! information, see the [book] as well as the section in the reference devoted
//! to [undefined behavior][ub].
//!
//! We say that a pointer is "dangling" if it is not valid for any non-zero-sized accesses. This
//! means out-of-bounds pointers, pointers to freed memory, null pointers, and pointers created with
//! [`NonNull::dangling`] are all dangling.
//!
//! ## Alignment
//!
//! Valid raw pointers as defined above are not necessarily properly aligned (where
Expand Down Expand Up @@ -167,6 +171,7 @@
//! * The **address-space** it is part of (e.g. "data" vs "code" in WASM).
//! * The **address** it points to, which can be represented by a `usize`.
//! * The **provenance** it has, defining the memory it has permission to access.
//! Provenance can be absent, in which case the pointer does not have permission to access any memory.
//!
//! Under Strict Provenance, a usize *cannot* accurately represent a pointer, and converting from
//! a pointer to a usize is generally an operation which *only* extracts the address. It is
Expand Down Expand Up @@ -270,11 +275,12 @@
//!
//! But it *is* still sound to:
//!
//! * Create an invalid pointer from just an address (see [`ptr::invalid`][]). This can
//! be used for sentinel values like `null` *or* to represent a tagged pointer that will
//! never be dereferenceable. In general, it is always sound for an integer to pretend
//! to be a pointer "for fun" as long as you don't use operations on it which require
//! it to be valid (offset, read, write, etc).
//! * Create a pointer without provenance from just an address (see [`ptr::dangling`][]). Such a
//! pointer cannot be used for memory accesses (except for zero-sized accesses). This can still be
//! useful for sentinel values like `null` *or* to represent a tagged pointer that will never be
//! dereferenceable. In general, it is always sound for an integer to pretend to be a pointer "for
//! fun" as long as you don't use operations on it which require it to be valid (non-zero-sized
//! offset, read, write, etc).
//!
//! * Forge an allocation of size zero at any sufficiently aligned non-null address.
//! i.e. the usual "ZSTs are fake, do what you want" rules apply *but* this only applies
Expand All @@ -283,7 +289,7 @@
//! that allocation and it will still get invalidated if the allocation gets deallocated.
//! In the future we may introduce an API to make such a forged allocation explicit.
//!
//! * [`wrapping_offset`][] a pointer outside its provenance. This includes invalid pointers
//! * [`wrapping_offset`][] a pointer outside its provenance. This includes pointers
//! which have "no" provenance. Unfortunately there may be practical limits on this for a
//! particular platform, and it's an open question as to how to specify this (if at all).
//! Notably, [CHERI][] relies on a compression scheme that can't handle a
Expand All @@ -294,7 +300,7 @@
//! generous (think kilobytes, not bytes).
//!
//! * Compare arbitrary pointers by address. Addresses *are* just integers and so there is
//! always a coherent answer, even if the pointers are invalid or from different
//! always a coherent answer, even if the pointers are dangling or from different
//! address-spaces/provenances. Of course, comparing addresses from different address-spaces
//! is generally going to be *meaningless*, but so is comparing Kilograms to Meters, and Rust
//! doesn't prevent that either. Similarly, if you get "lucky" and notice that a pointer
Expand Down Expand Up @@ -367,7 +373,7 @@
//! [`with_addr`]: pointer::with_addr
//! [`map_addr`]: pointer::map_addr
//! [`addr`]: pointer::addr
//! [`ptr::invalid`]: core::ptr::invalid
//! [`ptr::dangling`]: core::ptr::dangling
//! [`expose_addr`]: pointer::expose_addr
//! [`from_exposed_addr`]: from_exposed_addr
//! [Miri]: https://github.com/rust-lang/miri
Expand Down Expand Up @@ -537,7 +543,7 @@ pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
#[rustc_allow_const_fn_unstable(ptr_metadata)]
#[rustc_diagnostic_item = "ptr_null"]
pub const fn null<T: ?Sized + Thin>() -> *const T {
from_raw_parts(invalid(0), ())
from_raw_parts(without_provenance(0), ())
}

/// Creates a null mutable raw pointer.
Expand All @@ -563,32 +569,26 @@ pub const fn null<T: ?Sized + Thin>() -> *const T {
#[rustc_allow_const_fn_unstable(ptr_metadata)]
#[rustc_diagnostic_item = "ptr_null_mut"]
pub const fn null_mut<T: ?Sized + Thin>() -> *mut T {
from_raw_parts_mut(invalid_mut(0), ())
from_raw_parts_mut(without_provenance_mut(0), ())
}

/// Creates an invalid pointer with the given address.
/// Creates a pointer with the given address and no provenance.
///
/// Without provenance, this pointer is not associated with any actual allocation. Such a
/// no-provenance pointer may be used for zero-sized memory accesses (if suitably aligned), but
/// non-zero-sized memory accesses with a no-provenance pointer are UB. No-provenance pointers are
/// little more than a usize address in disguise.
///
/// This is different from `addr as *const T`, which creates a pointer that picks up a previously
/// exposed provenance. See [`from_exposed_addr`] for more details on that operation.
///
/// The module's top-level documentation discusses the precise meaning of an "invalid"
/// pointer but essentially this expresses that the pointer is not associated
/// with any actual allocation and is little more than a usize address in disguise.
///
/// This pointer will have no provenance associated with it and is therefore
/// UB to read/write/offset. This mostly exists to facilitate things
/// like `ptr::null` and `NonNull::dangling` which make invalid pointers.
///
/// (Standard "Zero-Sized-Types get to cheat and lie" caveats apply, although it
/// may be desirable to give them their own API just to make that 100% clear.)
///
/// This API and its claimed semantics are part of the Strict Provenance experiment,
/// see the [module documentation][crate::ptr] for details.
#[inline(always)]
#[must_use]
#[rustc_const_stable(feature = "stable_things_using_strict_provenance", since = "1.61.0")]
#[unstable(feature = "strict_provenance", issue = "95228")]
pub const fn invalid<T>(addr: usize) -> *const T {
pub const fn without_provenance<T>(addr: usize) -> *const T {
// FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
// We use transmute rather than a cast so tools like Miri can tell that this
// is *not* the same as from_exposed_addr.
Expand All @@ -597,29 +597,40 @@ pub const fn invalid<T>(addr: usize) -> *const T {
unsafe { mem::transmute(addr) }
}

/// Creates an invalid mutable pointer with the given address.
/// Creates a new pointer that is dangling, but well-aligned.
///
/// This is different from `addr as *mut T`, which creates a pointer that picks up a previously
/// exposed provenance. See [`from_exposed_addr_mut`] for more details on that operation.
/// This is useful for initializing types which lazily allocate, like
/// `Vec::new` does.
///
/// The module's top-level documentation discusses the precise meaning of an "invalid"
/// pointer but essentially this expresses that the pointer is not associated
/// with any actual allocation and is little more than a usize address in disguise.
/// Note that the pointer value may potentially represent a valid pointer to
/// a `T`, which means this must not be used as a "not yet initialized"
/// sentinel value. Types that lazily allocate must track initialization by
/// some other means.
#[inline(always)]
#[must_use]
#[rustc_const_stable(feature = "stable_things_using_strict_provenance", since = "1.61.0")]
#[unstable(feature = "strict_provenance", issue = "95228")]
pub const fn dangling<T>() -> *const T {
without_provenance(mem::align_of::<T>())
}

/// Creates a pointer with the given address and no provenance.
///
/// This pointer will have no provenance associated with it and is therefore
/// UB to read/write/offset. This mostly exists to facilitate things
/// like `ptr::null` and `NonNull::dangling` which make invalid pointers.
/// Without provenance, this pointer is not associated with any actual allocation. Such a
/// no-provenance pointer may be used for zero-sized memory accesses (if suitably aligned), but
/// non-zero-sized memory accesses with a no-provenance pointer are UB. No-provenance pointers are
/// little more than a usize address in disguise.
///
/// (Standard "Zero-Sized-Types get to cheat and lie" caveats apply, although it
/// may be desirable to give them their own API just to make that 100% clear.)
/// This is different from `addr as *mut T`, which creates a pointer that picks up a previously
/// exposed provenance. See [`from_exposed_addr_mut`] for more details on that operation.
///
/// This API and its claimed semantics are part of the Strict Provenance experiment,
/// see the [module documentation][crate::ptr] for details.
#[inline(always)]
#[must_use]
#[rustc_const_stable(feature = "stable_things_using_strict_provenance", since = "1.61.0")]
#[unstable(feature = "strict_provenance", issue = "95228")]
pub const fn invalid_mut<T>(addr: usize) -> *mut T {
pub const fn without_provenance_mut<T>(addr: usize) -> *mut T {
// FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
// We use transmute rather than a cast so tools like Miri can tell that this
// is *not* the same as from_exposed_addr.
Expand All @@ -628,6 +639,23 @@ pub const fn invalid_mut<T>(addr: usize) -> *mut T {
unsafe { mem::transmute(addr) }
}

/// Creates a new pointer that is dangling, but well-aligned.
///
/// This is useful for initializing types which lazily allocate, like
/// `Vec::new` does.
///
/// Note that the pointer value may potentially represent a valid pointer to
/// a `T`, which means this must not be used as a "not yet initialized"
/// sentinel value. Types that lazily allocate must track initialization by
/// some other means.
#[inline(always)]
#[must_use]
#[rustc_const_stable(feature = "stable_things_using_strict_provenance", since = "1.61.0")]
#[unstable(feature = "strict_provenance", issue = "95228")]
pub const fn dangling_mut<T>() -> *mut T {
without_provenance_mut(mem::align_of::<T>())
}

/// Convert an address back to a pointer, picking up a previously 'exposed' provenance.
///
/// This is a more rigorously specified alternative to `addr as *const T`. The provenance of the
Expand Down
7 changes: 4 additions & 3 deletions library/core/src/ptr/mut_ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,9 +188,10 @@ impl<T: ?Sized> *mut T {
///
/// This is similar to `self as usize`, which semantically discards *provenance* and
/// *address-space* information. However, unlike `self as usize`, casting the returned address
/// back to a pointer yields [`invalid`][], which is undefined behavior to dereference. To
/// properly restore the lost information and obtain a dereferenceable pointer, use
/// [`with_addr`][pointer::with_addr] or [`map_addr`][pointer::map_addr].
/// back to a pointer yields yields a [pointer without provenance][without_provenance_mut], which is undefined
/// behavior to dereference. To properly restore the lost information and obtain a
/// dereferenceable pointer, use [`with_addr`][pointer::with_addr] or
/// [`map_addr`][pointer::map_addr].
///
/// If using those APIs is not possible because there is no way to preserve a pointer with the
/// required provenance, then Strict Provenance might not be for you. Use pointer-integer casts
Expand Down
5 changes: 2 additions & 3 deletions library/core/src/ptr/non_null.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ use crate::hash;
use crate::intrinsics;
use crate::intrinsics::assert_unsafe_precondition;
use crate::marker::Unsize;
use crate::mem::SizedTypeProperties;
use crate::mem::{self, MaybeUninit};
use crate::mem::{MaybeUninit, SizedTypeProperties};
use crate::num::{NonZero, NonZeroUsize};
use crate::ops::{CoerceUnsized, DispatchFromDyn};
use crate::ptr;
Expand Down Expand Up @@ -114,7 +113,7 @@ impl<T: Sized> NonNull<T> {
// to a *mut T. Therefore, `ptr` is not null and the conditions for
// calling new_unchecked() are respected.
unsafe {
let ptr = crate::ptr::invalid_mut::<T>(mem::align_of::<T>());
let ptr = crate::ptr::dangling_mut::<T>();
NonNull::new_unchecked(ptr)
}
}
Expand Down
Loading
Loading