about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2024-09-14 13:30:37 +0200
committerRalf Jung <post@ralfj.de>2024-10-21 15:05:35 +0100
commitc3e928d8ddc73f5681778208f6c44cf958694e8b (patch)
tree94c237b0210e5e7d0f0409c6e5785f237dc5d288
parentbfab34af4c5bfbcc9168064857bbec826a60a0b9 (diff)
downloadrust-c3e928d8ddc73f5681778208f6c44cf958694e8b.tar.gz
rust-c3e928d8ddc73f5681778208f6c44cf958694e8b.zip
stabilize Strict Provenance and Exposed Provenance
This comes with a big docs rewrite.
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/rvalue.rs6
-rw-r--r--library/core/src/ptr/const_ptr.rs90
-rw-r--r--library/core/src/ptr/mod.rs489
-rw-r--r--library/core/src/ptr/mut_ptr.rs93
-rw-r--r--library/core/src/ptr/non_null.rs21
5 files changed, 312 insertions, 387 deletions
diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
index a132ca69540..82fea4c58e1 100644
--- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
@@ -361,12 +361,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             (Int(..) | Float(_), Int(..) | Float(_)) => bx.bitcast(imm, to_backend_ty),
             (Pointer(..), Pointer(..)) => bx.pointercast(imm, to_backend_ty),
             (Int(..), Pointer(..)) => bx.ptradd(bx.const_null(bx.type_ptr()), imm),
-            (Pointer(..), Int(..)) => bx.ptrtoint(imm, to_backend_ty),
+            (Pointer(..), Int(..)) => {
+                // FIXME: this exposes the provenance, which shouldn't be necessary.
+                bx.ptrtoint(imm, to_backend_ty)
+            }
             (Float(_), Pointer(..)) => {
                 let int_imm = bx.bitcast(imm, bx.cx().type_isize());
                 bx.ptradd(bx.const_null(bx.type_ptr()), int_imm)
             }
             (Pointer(..), Float(_)) => {
+                // FIXME: this exposes the provenance, which shouldn't be necessary.
                 let int_imm = bx.ptrtoint(imm, bx.cx().type_isize());
                 bx.bitcast(int_imm, to_backend_ty)
             }
diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs
index 72f4bc2c9da..f530954516b 100644
--- a/library/core/src/ptr/const_ptr.rs
+++ b/library/core/src/ptr/const_ptr.rs
@@ -137,10 +137,11 @@ impl<T: ?Sized> *const T {
 
     /// Gets the "address" portion of the pointer.
     ///
-    /// 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 a [pointer without provenance][without_provenance], which is undefined behavior to dereference. To
-    /// properly restore the lost information and obtain a dereferenceable pointer, use
+    /// This is similar to `self as usize`, except that the [provenance][crate::ptr#provenance] of
+    /// the pointer is discarded and not [exposed][crate::ptr#exposed-provenance]. This means that
+    /// casting the returned address 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].
     ///
     /// If using those APIs is not possible because there is no way to preserve a pointer with the
@@ -155,90 +156,81 @@ impl<T: ?Sized> *const T {
     /// perform a change of representation to produce a value containing only the address
     /// portion of the pointer. What that means is up to the platform to define.
     ///
-    /// This API and its claimed semantics are part of the Strict Provenance experiment, and as such
-    /// might change in the future (including possibly weakening this so it becomes wholly
-    /// equivalent to `self as usize`). See the [module documentation][crate::ptr] for details.
+    /// This is a [Strict Provenance][crate::ptr#strict-provenance] API.
     #[must_use]
     #[inline(always)]
-    #[unstable(feature = "strict_provenance", issue = "95228")]
+    #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")]
     pub fn addr(self) -> usize {
-        // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
+        // A pointer-to-integer transmute currently has exactly the right semantics: it returns the
+        // address without exposing the provenance. Note that this is *not* a stable guarantee about
+        // transmute semantics, it relies on sysroot crates having special status.
         // SAFETY: Pointer-to-integer transmutes are valid (if you are okay with losing the
         // provenance).
         unsafe { mem::transmute(self.cast::<()>()) }
     }
 
-    /// Exposes the "provenance" part of the pointer for future use in
-    /// [`with_exposed_provenance`][] and returns the "address" portion.
+    /// Exposes the ["provenance"][crate::ptr#provenance] part of the pointer for future use in
+    /// [`with_exposed_provenance`] and returns the "address" portion.
     ///
-    /// This is equivalent to `self as usize`, which semantically discards *provenance* and
-    /// *address-space* information. Furthermore, this (like the `as` cast) has the implicit
-    /// side-effect of marking the provenance as 'exposed', so on platforms that support it you can
-    /// later call [`with_exposed_provenance`][] to reconstitute the original pointer including its
-    /// provenance. (Reconstructing address space information, if required, is your responsibility.)
+    /// This is equivalent to `self as usize`, which semantically discards provenance information.
+    /// Furthermore, this (like the `as` cast) has the implicit side-effect of marking the
+    /// provenance as 'exposed', so on platforms that support it you can later call
+    /// [`with_exposed_provenance`] to reconstitute the original pointer including its provenance.
     ///
-    /// Using this method means that code is *not* following [Strict
-    /// Provenance][super#strict-provenance] rules. Supporting
-    /// [`with_exposed_provenance`][] complicates specification and reasoning and may not be supported by
-    /// tools that help you to stay conformant with the Rust memory model, so it is recommended to
-    /// use [`addr`][pointer::addr] wherever possible.
+    /// Due to its inherent ambiguity, [`with_exposed_provenance`] may not be supported by tools
+    /// that help you to stay conformant with the Rust memory model. It is recommended to use
+    /// [Strict Provenance][crate::ptr#strict-provenance] APIs such as [`with_addr`][pointer::with_addr]
+    /// wherever possible, in which case [`addr`][pointer::addr] should be used instead of `expose_provenance`.
     ///
     /// On most platforms this will produce a value with the same bytes as the original pointer,
     /// because all the bytes are dedicated to describing the address. Platforms which need to store
     /// additional information in the pointer may not support this operation, since the 'expose'
-    /// side-effect which is required for [`with_exposed_provenance`][] to work is typically not
+    /// side-effect which is required for [`with_exposed_provenance`] to work is typically not
     /// available.
     ///
-    /// It is unclear whether this method can be given a satisfying unambiguous specification. This
-    /// API and its claimed semantics are part of [Exposed Provenance][super#exposed-provenance].
+    /// This is an [Exposed Provenance][crate::ptr#exposed-provenance] API.
     ///
     /// [`with_exposed_provenance`]: with_exposed_provenance
     #[must_use]
     #[inline(always)]
-    #[unstable(feature = "exposed_provenance", issue = "95228")]
+    #[stable(feature = "exposed_provenance", since = "CURRENT_RUSTC_VERSION")]
     pub fn expose_provenance(self) -> usize {
-        // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
         self.cast::<()>() as usize
     }
 
-    /// Creates a new pointer with the given address.
+    /// Creates a new pointer with the given address and the [provenance][crate::ptr#provenance] of
+    /// `self`.
     ///
-    /// This performs the same operation as an `addr as ptr` cast, but copies
-    /// the *address-space* and *provenance* of `self` to the new pointer.
-    /// This allows us to dynamically preserve and propagate this important
-    /// information in a way that is otherwise impossible with a unary cast.
+    /// This is similar to a `addr as *const T` cast, but copies
+    /// the *provenance* of `self` to the new pointer.
+    /// This avoids the inherent ambiguity of the unary cast.
     ///
     /// This is equivalent to using [`wrapping_offset`][pointer::wrapping_offset] to offset
     /// `self` to the given address, and therefore has all the same capabilities and restrictions.
     ///
-    /// This API and its claimed semantics are part of the Strict Provenance experiment,
-    /// see the [module documentation][crate::ptr] for details.
+    /// This is a [Strict Provenance][crate::ptr#strict-provenance] API.
     #[must_use]
     #[inline]
-    #[unstable(feature = "strict_provenance", issue = "95228")]
+    #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")]
     pub fn with_addr(self, addr: usize) -> Self {
-        // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
-        //
-        // In the mean-time, this operation is defined to be "as if" it was
-        // a wrapping_offset, so we can emulate it as such. This should properly
-        // restore pointer provenance even under today's compiler.
+        // This should probably be an intrinsic to avoid doing any sort of arithmetic, but
+        // meanwhile, we can implement it with `wrapping_offset`, which preserves the pointer's
+        // provenance.
         let self_addr = self.addr() as isize;
         let dest_addr = addr as isize;
         let offset = dest_addr.wrapping_sub(self_addr);
-
-        // This is the canonical desugaring of this operation
         self.wrapping_byte_offset(offset)
     }
 
-    /// Creates a new pointer by mapping `self`'s address to a new one.
+    /// Creates a new pointer by mapping `self`'s address to a new one, preserving the
+    /// [provenance][crate::ptr#provenance] of `self`.
     ///
     /// This is a convenience for [`with_addr`][pointer::with_addr], see that method for details.
     ///
-    /// This API and its claimed semantics are part of the Strict Provenance experiment,
-    /// see the [module documentation][crate::ptr] for details.
+    /// This is a [Strict Provenance][crate::ptr#strict-provenance] API.
     #[must_use]
     #[inline]
-    #[unstable(feature = "strict_provenance", issue = "95228")]
+    #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")]
     pub fn map_addr(self, f: impl FnOnce(usize) -> usize) -> Self {
         self.with_addr(f(self.addr()))
     }
@@ -379,7 +371,7 @@ impl<T: ?Sized> *const T {
     /// * The offset in bytes, `count * size_of::<T>()`, computed on mathematical integers (without
     ///   "wrapping around"), must fit in an `isize`.
     ///
-    /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some
+    /// * If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] 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.
@@ -611,7 +603,7 @@ impl<T: ?Sized> *const T {
     /// * `self` and `origin` must either
     ///
     ///   * point to the same address, or
-    ///   * both be *derived from* a pointer to the same [allocated object], and the memory range between
+    ///   * both be [derived from][crate::ptr#provenance] a pointer to the same [allocated object], and the memory range between
     ///     the two pointers must be in bounds of that object. (See below for an example.)
     ///
     /// * The distance between the pointers, in bytes, must be an exact multiple
@@ -871,7 +863,7 @@ impl<T: ?Sized> *const T {
     /// * The offset in bytes, `count * size_of::<T>()`, computed on mathematical integers (without
     ///   "wrapping around"), must fit in an `isize`.
     ///
-    /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some
+    /// * If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] 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.
@@ -978,7 +970,7 @@ impl<T: ?Sized> *const T {
     /// * The offset in bytes, `count * size_of::<T>()`, computed on mathematical integers (without
     ///   "wrapping around"), must fit in an `isize`.
     ///
-    /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some
+    /// * If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] 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.
diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs
index f7036f30a99..72a77ea6b18 100644
--- a/library/core/src/ptr/mod.rs
+++ b/library/core/src/ptr/mod.rs
@@ -18,10 +18,11 @@
 //! * For operations of [size zero][zst], *every* pointer is valid, including the [null] pointer.
 //!   The following points are only concerned with non-zero-sized accesses.
 //! * A [null] pointer is *never* valid.
-//! * For a pointer to be valid, it is necessary, but not always sufficient, that the pointer
-//!   be *dereferenceable*: the memory range of the given size starting at the pointer must all be
-//!   within the bounds of a single allocated object. Note that in Rust,
-//!   every (stack-allocated) variable is considered a separate allocated object.
+//! * For a pointer to be valid, it is necessary, but not always sufficient, that the pointer be
+//!   *dereferenceable*. The [provenance] of the pointer is used to determine which [allocated
+//!   object] it is derived from; a pointer is dereferenceable if the memory range of the given size
+//!   starting at the pointer is entirely contained within the bounds of that allocated object. Note
+//!   that in Rust, every (stack-allocated) variable is considered a separate allocated object.
 //! * 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
@@ -130,123 +131,130 @@
 //!
 //! [`null()`]: null
 //!
-//! # Strict Provenance
-//!
-//! **The following text is non-normative, insufficiently formal, and is an extremely strict
-//! interpretation of provenance. It's ok if your code doesn't strictly conform to it.**
-//!
-//! [Strict Provenance][] is an experimental set of APIs that help tools that try
-//! to validate the memory-safety of your program's execution. Notably this includes [Miri][]
-//! and [CHERI][], which can detect when you access out of bounds memory or otherwise violate
-//! Rust's memory model.
-//!
-//! Provenance must exist in some form for any programming
-//! language compiled for modern computer architectures, but specifying a model for provenance
-//! in a way that is useful to both compilers and programmers is an ongoing challenge.
-//! The [Strict Provenance][] experiment seeks to explore the question: *what if we just said you
-//! couldn't do all the nasty operations that make provenance so messy?*
-//!
-//! What APIs would have to be removed? What APIs would have to be added? How much would code
-//! have to change, and is it worse or better now? Would any patterns become truly inexpressible?
-//! Could we carve out special exceptions for those patterns? Should we?
-//!
-//! A secondary goal of this project is to see if we can disambiguate the many functions of
-//! pointer<->integer casts enough for the definition of `usize` to be loosened so that it
-//! isn't *pointer*-sized but address-space/offset/allocation-sized (we'll probably continue
-//! to conflate these notions). This would potentially make it possible to more efficiently
-//! target platforms where pointers are larger than offsets, such as CHERI and maybe some
-//! segmented architectures.
-//!
-//! ## Provenance
-//!
-//! **This section is *non-normative* and is part of the [Strict Provenance][] experiment.**
+//! # Provenance
 //!
 //! Pointers are not *simply* an "integer" or "address". For instance, it's uncontroversial
 //! to say that a Use After Free is clearly Undefined Behaviour, even if you "get lucky"
 //! and the freed memory gets reallocated before your read/write (in fact this is the
 //! worst-case scenario, UAFs would be much less concerning if this didn't happen!).
-//! To rationalize this claim, pointers need to somehow be *more* than just their addresses:
-//! they must have provenance.
+//! As another example, consider that [`wrapping_offset`] is documented to "remember"
+//! the allocated object that the original pointer points to, even if it is offset far
+//! outside the memory range occupied by that allocated object.
+//! To rationalize claims like this, pointers need to somehow be *more* than just their addresses:
+//! they must have **provenance**.
 //!
-//! When an allocation is created, that allocation has a unique Original Pointer. For alloc
-//! APIs this is literally the pointer the call returns, and for local variables and statics,
-//! this is the name of the variable/static. This is mildly overloading the term "pointer"
-//! for the sake of brevity/exposition.
+//! A pointer value in Rust semantically contains the following information:
 //!
-//! The Original Pointer for an allocation is guaranteed to have unique access to the entire
-//! allocation and *only* that allocation. In this sense, an allocation can be thought of
-//! as a "sandbox" that cannot be broken into or out of. *Provenance* is the permission
-//! to access an allocation's sandbox and has both a *spatial* and *temporal* component:
+//! * 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.
 //!
-//! * Spatial: A range of bytes that the pointer is allowed to access.
-//! * Temporal: The lifetime (of the allocation) that access to these bytes is tied to.
+//! The exact structure of provenance is not yet specified, but the permission defined by a
+//! pointer's provenance have a *spatial* component, a *temporal* component, and a *mutability*
+//! component:
 //!
-//! Spatial provenance makes sure you don't go beyond your sandbox, while temporal provenance
-//! makes sure that you can't "get lucky" after your permission to access some memory
-//! has been revoked (either through deallocations or borrows expiring).
+//! * Spatial: The set of memory addresses that the pointer is allowed to access.
+//! * Temporal: The timespan during which the pointer is allowed to access those memory addresses.
+//! * Mutability: Whether the pointer may only access the memory for reads, or also access it for
+//!   writes. Note that this can interact with the other components, e.g. a pointer might permit
+//!   mutation only for a subset of addresses, or only for a subset of its maximal timespan.
 //!
-//! Provenance is implicitly shared with all pointers transitively derived from
-//! The Original Pointer through operations like [`offset`], borrowing, and pointer casts.
-//! Some operations may *shrink* the derived provenance, limiting how much memory it can
-//! access or how long it's valid for (i.e. borrowing a subfield and subslicing).
+//! When an [allocated object] is created, it has a unique Original Pointer. For alloc
+//! APIs this is literally the pointer the call returns, and for local variables and statics,
+//! this is the name of the variable/static. (This is mildly overloading the term "pointer"
+//! for the sake of brevity/exposition.)
+//!
+//! The Original Pointer for an allocated object has provenance that constrains the *spatial*
+//! permissions of this pointer to the memory range of the allocation, and the *temporal*
+//! permissions to the lifetime of the allocation. Provenance is implicitly inherited by all
+//! pointers transitively derived from the Original Pointer through operations like [`offset`],
+//! borrowing, and pointer casts. Some operations may *shrink* the permissions of the derived
+//! provenance, limiting how much memory it can access or how long it's valid for (i.e. borrowing a
+//! subfield and subslicing can shrink the spatial component of provenance, and all borrowing can
+//! shrink the temporal component of provenance). However, no operation can ever *grow* the
+//! permissions of the derived provenance: even if you "know" there is a larger allocation, you
+//! can't derive a pointer with a larger provenance. Similarly, you cannot "recombine" two
+//! contiguous provenances back into one (i.e. with a `fn merge(&[T], &[T]) -> &[T]`).
+//!
+//! A reference to a place always has provenance over at least the memory that place occupies.
+//! A reference to a slice always has provenance over at least the range that slice describes.
+//! Whether and when exactly the provenance of a reference gets "shrunk" to *exactly* fit
+//! the memory it points to is not yet determined.
+//!
+//! A *shared* reference only ever has provenance that permits reading from memory,
+//! and never permits writes, except inside [`UnsafeCell`].
+//!
+//! Provenance can affect whether a program has undefined behavior:
+//!
+//! * It is undefined behavior to access memory through a pointer that does not have provenance over
+//!   that memory. Note that a pointer "at the end" of its provenance is not actually outside its
+//!   provenance, it just has 0 bytes it can load/store. Zero-sized accesses do not require any
+//!   provenance since they access an empty range of memory.
+//!
+//! * It is undefined behavior to [`offset`] a pointer across a memory range that is not contained
+//!   in the allocated object it is derived from, or to [`offset_from`] two pointers not derived
+//!   from the same allocated object. Provenance is used to say what exactly "derived from" even
+//!   means: the lineage of a pointer is traced back to the Original Pointer it descends from, and
+//!   that identifies the relevant allocated object. In particular, it's always UB to offset a
+//!   pointer derived from something that is now deallocated, except if the offset is 0.
 //!
-//! Shrinking provenance cannot be undone: even if you "know" there is a larger allocation, you
-//! can't derive a pointer with a larger provenance. Similarly, you cannot "recombine"
-//! two contiguous provenances back into one (i.e. with a `fn merge(&[T], &[T]) -> &[T]`).
+//! But it *is* still sound to:
 //!
-//! A reference to a value always has provenance over exactly the memory that field occupies.
-//! A reference to a slice always has provenance over exactly the range that slice describes.
+//! * 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.
 //!
-//! If an allocation is deallocated, all pointers with provenance to that allocation become
-//! invalidated, and effectively lose their provenance.
+//! * [`wrapping_offset`] a pointer outside its provenance. This includes pointers
+//!   which have "no" provenance. In particular, this makes it sound to do pointer tagging tricks.
 //!
-//! The strict provenance experiment is mostly only interested in exploring stricter *spatial*
-//! provenance. In this sense it can be thought of as a subset of the more ambitious and
-//! formal [Stacked Borrows][] research project, which is what tools like [Miri][] are based on.
-//! In particular, Stacked Borrows is necessary to properly describe what borrows are allowed
-//! to do and when they become invalidated. This necessarily involves much more complex
-//! *temporal* reasoning than simply identifying allocations. Adjusting APIs and code
-//! for the strict provenance experiment will also greatly help Stacked Borrows.
+//! * Compare arbitrary pointers by address. Pointer comparison ignores provenance and addresses
+//!   *are* just integers, so there is always a coherent answer, even if the pointers are dangling
+//!   or from different provenances. Note that if you get "lucky" and notice that a pointer at the
+//!   end of one allocated object is the "same" address as the start of another allocated object,
+//!   anything you do with that fact is *probably* going to be gibberish. The scope of that
+//!   gibberish is kept under control by the fact that the two pointers *still* aren't allowed to
+//!   access the other's allocation (bytes), because they still have different provenance.
 //!
+//! Note that the full definition of provenance in Rust is not decided yet, as this interacts
+//! with the as-yet undecided [aliasing] rules.
 //!
-//! ## Pointer Vs Addresses
+//! ## Pointers Vs Integers
 //!
-//! **This section is *non-normative* and is part of the [Strict Provenance][] experiment.**
+//! From this discussion, it becomes very clear that a `usize` *cannot* accurately represent a pointer,
+//! and converting from a pointer to a `usize` is generally an operation which *only* extracts the
+//! address. Converting this address back into pointer requires somehow answering the question:
+//! which provenance should the resulting pointer have?
 //!
-//! One of the largest historical issues with trying to define provenance is that programmers
-//! freely convert between pointers and integers. Once you allow for this, it generally becomes
-//! impossible to accurately track and preserve provenance information, and you need to appeal
-//! to very complex and unreliable heuristics. But of course, converting between pointers and
-//! integers is very useful, so what can we do?
+//! Rust provides two ways of dealing with this situation: *Strict Provenance* and *Exposed Provenance*.
 //!
-//! Also did you know WASM is actually a "Harvard Architecture"? As in function pointers are
-//! handled completely differently from data pointers? And we kind of just shipped Rust on WASM
-//! without really addressing the fact that we let you freely convert between function pointers
-//! and data pointers, because it mostly Just Works? Let's just put that on the "pointer casts
-//! are dubious" pile.
+//! Note that a pointer *can* represent a `usize` (via [`without_provenance`]), so the right type to
+//! use in situations where a value is "sometimes a pointer and sometimes a bare `usize`" is a
+//! pointer type.
 //!
-//! Strict Provenance attempts to square these circles by decoupling Rust's traditional conflation
-//! of pointers and `usize` (and `isize`), and defining a pointer to semantically contain the
-//! following information:
+//! ## Strict Provenance
 //!
-//! * 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.
+//! "Strict Provenance" refers to a set of APIs designed to make working with provenance more
+//! explicit. They are intended as substitutes for casting a pointer to an integer and back.
 //!
-//! 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
-//! therefore *impossible* to construct a valid pointer from a `usize` because there is no way
-//! to restore the address-space and provenance. In other words, pointer-integer-pointer
-//! roundtrips are not possible (in the sense that the resulting pointer is not dereferenceable).
+//! Entirely avoiding integer-to-pointer casts successfully side-steps the inherent ambiguity of
+//! that operation. This benefits compiler optimizations, and it is pretty much a requirement for
+//! using tools like [Miri] and architectures like [CHERI] that aim to detect and diagnose pointer
+//! misuse.
 //!
-//! The key insight to making this model *at all* viable is the [`with_addr`][] method:
+//! The key insight to making programming without integer-to-pointer casts *at all* viable is the
+//! [`with_addr`] method:
 //!
 //! ```text
 //!     /// Creates a new pointer with the given address.
 //!     ///
 //!     /// This performs the same operation as an `addr as ptr` cast, but copies
-//!     /// the *address-space* and *provenance* of `self` to the new pointer.
+//!     /// the *provenance* of `self` to the new pointer.
 //!     /// This allows us to dynamically preserve and propagate this important
 //!     /// information in a way that is otherwise impossible with a unary cast.
 //!     ///
@@ -257,23 +265,21 @@
 //!
 //! So you're still able to drop down to the address representation and do whatever
 //! clever bit tricks you want *as long as* you're able to keep around a pointer
-//! into the allocation you care about that can "reconstitute" the other parts of the pointer.
+//! into the allocation you care about that can "reconstitute" the provenance.
 //! Usually this is very easy, because you only are taking a pointer, messing with the address,
 //! and then immediately converting back to a pointer. To make this use case more ergonomic,
-//! we provide the [`map_addr`][] method.
+//! we provide the [`map_addr`] method.
 //!
 //! To help make it clear that code is "following" Strict Provenance semantics, we also provide an
-//! [`addr`][] method which promises that the returned address is not part of a
-//! pointer-usize-pointer roundtrip. In the future we may provide a lint for pointer<->integer
+//! [`addr`] method which promises that the returned address is not part of a
+//! pointer-integer-pointer roundtrip. In the future we may provide a lint for pointer<->integer
 //! casts to help you audit if your code conforms to strict provenance.
 //!
-//!
-//! ## Using Strict Provenance
+//! ### Using Strict Provenance
 //!
 //! Most code needs no changes to conform to strict provenance, as the only really concerning
-//! operation that *wasn't* obviously already Undefined Behaviour is casts from usize to a
-//! pointer. For code which *does* cast a `usize` to a pointer, the scope of the change depends
-//! on exactly what you're doing.
+//! operation is casts from usize to a pointer. For code which *does* cast a `usize` to a pointer,
+//! the scope of the change depends on exactly what you're doing.
 //!
 //! In general, you just need to make sure that if you want to convert a `usize` address to a
 //! pointer and then use that pointer to read/write memory, you need to keep around a pointer
@@ -314,122 +320,65 @@
 //! be using AtomicPtr instead. If that messes up the way you atomically manipulate pointers,
 //! we would like to know why, and what needs to be done to fix it.)
 //!
-//! Something more complicated and just generally *evil* like an XOR-List requires more significant
-//! changes like allocating all nodes in a pre-allocated Vec or Arena and using a pointer
-//! to the whole allocation to reconstitute the XORed addresses.
-//!
 //! Situations where a valid pointer *must* be created from just an address, such as baremetal code
-//! accessing a memory-mapped interface at a fixed address, are an open question on how to support.
-//! These situations *will* still be allowed, but we might require some kind of "I know what I'm
-//! doing" annotation to explain the situation to the compiler. It's also possible they need no
-//! special attention at all, because they're generally accessing memory outside the scope of
-//! "the abstract machine", or already using "I know what I'm doing" annotations like "volatile".
-//!
-//! Under [Strict Provenance] it is Undefined Behaviour to:
-//!
-//! * Access memory through a pointer that does not have provenance over that memory.
-//!
-//! * [`offset`] a pointer to or from an address it doesn't have provenance over.
-//!   This means it's always UB to offset a pointer derived from something deallocated,
-//!   even if the offset is 0. Note that a pointer "one past the end" of its provenance
-//!   is not actually outside its provenance, it just has 0 bytes it can load/store.
-//!
-//! But it *is* still sound to:
-//!
-//! * 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
-//!   for actual forgery (integers cast to pointers). If you borrow some struct's field
-//!   that *happens* to be zero-sized, the resulting pointer will have provenance tied to
-//!   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 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
-//!   pointer getting offset "too far" out of bounds. If this happens, the address
-//!   returned by `addr` will be the value you expect, but the provenance will get invalidated
-//!   and using it to read/write will fault. The details of this are architecture-specific
-//!   and based on alignment, but the buffer on either side of the pointer's range is pretty
-//!   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 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
-//!   one-past-the-end is the "same" address as the start of an unrelated allocation, anything
-//!   you do with that fact is *probably* going to be gibberish. The scope of that gibberish
-//!   is kept under control by the fact that the two pointers *still* aren't allowed to access
-//!   the other's allocation (bytes), because they still have different provenance.
-//!
-//! * Perform pointer tagging tricks. This falls out of [`wrapping_offset`] but is worth
-//!   mentioning in more detail because of the limitations of [CHERI][]. Low-bit tagging
-//!   is very robust, and often doesn't even go out of bounds because types ensure
-//!   size >= align (and over-aligning actually gives CHERI more flexibility). Anything
-//!   more complex than this rapidly enters "extremely platform-specific" territory as
-//!   certain things may or may not be allowed based on specific supported operations.
-//!   For instance, ARM explicitly supports high-bit tagging, and so CHERI on ARM inherits
-//!   that and should support it.
+//! accessing a memory-mapped interface at a fixed address, cannot currently be handled with strict
+//! provenance APIs and should use [exposed provenance](#exposed-provenance).
 //!
 //! ## Exposed Provenance
 //!
-//! **This section is *non-normative* and is an extension to the [Strict Provenance] experiment.**
-//!
-//! As discussed above, pointer-usize-pointer roundtrips are not possible under [Strict Provenance].
+//! As discussed above, integer-to-pointer casts are not possible with Strict Provenance APIs.
 //! This is by design: the goal of Strict Provenance is to provide a clear specification that we are
-//! confident can be formalized unambiguously and can be subject to  precise formal reasoning.
+//! confident can be formalized unambiguously and can be subject to precise formal reasoning.
+//! Integer-to-pointer casts do not (currently) have such a clear specification.
 //!
-//! However, there exist situations where pointer-usize-pointer roundtrips cannot be avoided, or
+//! However, there exist situations where integer-to-pointer casts cannot be avoided, or
 //! where avoiding them would require major refactoring. Legacy platform APIs also regularly assume
-//! that `usize` can capture all the information that makes up a pointer. The goal of Strict
-//! Provenance is not to rule out such code; the goal is to put all the *other* pointer-manipulating
-//! code onto a more solid foundation. Strict Provenance is about improving the situation where
-//! possible (all the code that can be written with Strict Provenance) without making things worse
-//! for situations where Strict Provenance is insufficient.
-//!
-//! For these situations, there is a highly experimental extension to Strict Provenance called
-//! *Exposed Provenance*. This extension permits pointer-usize-pointer roundtrips. However, its
-//! semantics are on much less solid footing than Strict Provenance, and at this point it is not yet
-//! clear where a satisfying unambiguous semantics can be defined for Exposed Provenance.
-//! Furthermore, Exposed Provenance will not work (well) with tools like [Miri] and [CHERI].
+//! that `usize` can capture all the information that makes up a pointer.
+//! Bare-metal platforms can also require the synthesis of a pointer "out of thin air" without
+//! anywhere to obtain proper provenance from.
+//!
+//! Rust's model for dealing with integer-to-pointer casts is called *Exposed Provenance*. However,
+//! the semantics of Exposed Provenance are on much less solid footing than Strict Provenance, and
+//! at this point it is not yet clear whether a satisfying unambiguous semantics can be defined for
+//! Exposed Provenance. (If that sounds bad, be reassured that other popular languages that provide
+//! integer-to-pointer casts are not faring any better.) Furthermore, Exposed Provenance will not
+//! work (well) with tools like [Miri] and [CHERI].
 //!
 //! Exposed Provenance is provided by the [`expose_provenance`] and [`with_exposed_provenance`] methods,
-//! which are meant to replace `as` casts between pointers and integers. [`expose_provenance`] is a lot like
-//! [`addr`], but additionally adds the provenance of the pointer to a global list of 'exposed'
-//! provenances. (This list is purely conceptual, it exists for the purpose of specifying Rust but
-//! is not materialized in actual executions, except in tools like [Miri].) [`with_exposed_provenance`]
-//! can be used to construct a pointer with one of these previously 'exposed' provenances.
-//! [`with_exposed_provenance`] takes only `addr: usize` as arguments, so unlike in [`with_addr`] there is
-//! no indication of what the correct provenance for the returned pointer is -- and that is exactly
-//! what makes pointer-usize-pointer roundtrips so tricky to rigorously specify! There is no
-//! algorithm that decides which provenance will be used. You can think of this as "guessing" the
-//! right provenance, and the guess will be "maximally in your favor", in the sense that if there is
-//! any way to avoid undefined behavior, then that is the guess that will be taken. However, if
-//! there is *no* previously 'exposed' provenance that justifies the way the returned pointer will
-//! be used, the program has undefined behavior.
-//!
-//! Using [`expose_provenance`] or [`with_exposed_provenance`] (or the `as` casts) means that code is
-//! *not* following Strict Provenance rules. The goal of the Strict Provenance experiment is to
-//! determine how far one can get in Rust without the use of [`expose_provenance`] and
-//! [`with_exposed_provenance`], and to encourage code to be written with Strict Provenance APIs only.
-//! Maximizing the amount of such code is a major win for avoiding specification complexity and to
-//! facilitate adoption of tools like [CHERI] and [Miri] that can be a big help in increasing the
-//! confidence in (unsafe) Rust code.
+//! which are equivalent to `as` casts between pointers and integers.
+//! - [`expose_provenance`] is a lot like [`addr`], but additionally adds the provenance of the
+//!   pointer to a global list of 'exposed' provenances. (This list is purely conceptual, it exists
+//!   for the purpose of specifying Rust but is not materialized in actual executions, except in
+//!   tools like [Miri].)
+//!   Memory which is outside the control of the Rust abstract machine (MMIO registers, for example)
+//!   is always considered to be exposed, so long as this memory is disjoint from memory that will
+//!   be used by the abstract machine such as the stack, heap, and statics.
+//! - [`with_exposed_provenance`] can be used to construct a pointer with one of these previously
+//!   'exposed' provenances. [`with_exposed_provenance`] takes only `addr: usize` as arguments, so
+//!   unlike in [`with_addr`] there is no indication of what the correct provenance for the returned
+//!   pointer is -- and that is exactly what makes integer-to-pointer casts so tricky to rigorously
+//!   specify! The compiler will do its best to pick the right provenance for you, but currently we
+//!   cannot provide any guarantees about which provenance the resulting pointer will have. Only one
+//!   thing is clear: if there is *no* previously 'exposed' provenance that justifies the way the
+//!   returned pointer will be used, the program has undefined behavior.
+//!
+//! If at all possible, we encourage code to be ported to [Strict Provenance] APIs, thus avoiding
+//! the need for Exposed Provenance. Maximizing the amount of such code is a major win for avoiding
+//! specification complexity and to facilitate adoption of tools like [CHERI] and [Miri] that can be
+//! a big help in increasing the confidence in (unsafe) Rust code. However, we acknowledge that this
+//! is not always possible, and offer Exposed Provenance as a way to explicit "opt out" of the
+//! well-defined semantics of Strict Provenance, and "opt in" to the unclear semantics of
+//! integer-to-pointer casts.
 //!
 //! [aliasing]: ../../nomicon/aliasing.html
+//! [allocated object]: #allocated-object
+//! [provenance]: #provenance
 //! [book]: ../../book/ch19-01-unsafe-rust.html#dereferencing-a-raw-pointer
 //! [ub]: ../../reference/behavior-considered-undefined.html
 //! [zst]: ../../nomicon/exotic-sizes.html#zero-sized-types-zsts
 //! [atomic operations]: crate::sync::atomic
 //! [`offset`]: pointer::offset
+//! [`offset_from`]: pointer::offset_from
 //! [`wrapping_offset`]: pointer::wrapping_offset
 //! [`with_addr`]: pointer::with_addr
 //! [`map_addr`]: pointer::map_addr
@@ -439,8 +388,8 @@
 //! [`with_exposed_provenance`]: with_exposed_provenance
 //! [Miri]: https://github.com/rust-lang/miri
 //! [CHERI]: https://www.cl.cam.ac.uk/research/security/ctsrd/cheri/
-//! [Strict Provenance]: https://github.com/rust-lang/rust/issues/95228
-//! [Stacked Borrows]: https://plv.mpi-sws.org/rustbelt/stacked-borrows/
+//! [Strict Provenance]: #strict-provenance
+//! [`UnsafeCell`]: core::cell::UnsafeCell
 
 #![stable(feature = "rust1", since = "1.0.0")]
 // There are many unsafe functions taking pointers that don't dereference them.
@@ -629,7 +578,7 @@ pub const fn null_mut<T: ?Sized + Thin>() -> *mut T {
     from_raw_parts_mut(without_provenance_mut::<()>(0), ())
 }
 
-/// Creates a pointer with the given address and no provenance.
+/// Creates a pointer with the given address and no [provenance][crate::ptr#provenance].
 ///
 /// This is equivalent to `ptr::null().with_addr(addr)`.
 ///
@@ -641,16 +590,15 @@ pub const fn null_mut<T: ?Sized + Thin>() -> *mut T {
 /// This is different from `addr as *const T`, which creates a pointer that picks up a previously
 /// exposed provenance. See [`with_exposed_provenance`] 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.
+/// This is a [Strict Provenance][crate::ptr#strict-provenance] API.
 #[inline(always)]
 #[must_use]
 #[rustc_const_stable(feature = "stable_things_using_strict_provenance", since = "1.61.0")]
-#[unstable(feature = "strict_provenance", issue = "95228")]
+#[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")]
 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 with_exposed_provenance.
+    // An int-to-pointer transmute currently has exactly the intended semantics: it creates a
+    // pointer without provenance. Note that this is *not* a stable guarantee about transmute
+    // semantics, it relies on sysroot crates having special status.
     // SAFETY: every valid integer is also a valid pointer (as long as you don't dereference that
     // pointer).
     unsafe { mem::transmute(addr) }
@@ -668,12 +616,12 @@ pub const fn without_provenance<T>(addr: usize) -> *const T {
 #[inline(always)]
 #[must_use]
 #[rustc_const_stable(feature = "stable_things_using_strict_provenance", since = "1.61.0")]
-#[unstable(feature = "strict_provenance", issue = "95228")]
+#[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")]
 pub const fn dangling<T>() -> *const T {
     without_provenance(mem::align_of::<T>())
 }
 
-/// Creates a pointer with the given address and no provenance.
+/// Creates a pointer with the given address and no [provenance][crate::ptr#provenance].
 ///
 /// This is equivalent to `ptr::null_mut().with_addr(addr)`.
 ///
@@ -685,16 +633,15 @@ pub const fn dangling<T>() -> *const T {
 /// This is different from `addr as *mut T`, which creates a pointer that picks up a previously
 /// exposed provenance. See [`with_exposed_provenance_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.
+/// This is a [Strict Provenance][crate::ptr#strict-provenance] API.
 #[inline(always)]
 #[must_use]
 #[rustc_const_stable(feature = "stable_things_using_strict_provenance", since = "1.61.0")]
-#[unstable(feature = "strict_provenance", issue = "95228")]
+#[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")]
 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 with_exposed_provenance.
+    // An int-to-pointer transmute currently has exactly the intended semantics: it creates a
+    // pointer without provenance. Note that this is *not* a stable guarantee about transmute
+    // semantics, it relies on sysroot crates having special status.
     // SAFETY: every valid integer is also a valid pointer (as long as you don't dereference that
     // pointer).
     unsafe { mem::transmute(addr) }
@@ -712,96 +659,88 @@ pub const fn without_provenance_mut<T>(addr: usize) -> *mut T {
 #[inline(always)]
 #[must_use]
 #[rustc_const_stable(feature = "stable_things_using_strict_provenance", since = "1.61.0")]
-#[unstable(feature = "strict_provenance", issue = "95228")]
+#[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")]
 pub const fn dangling_mut<T>() -> *mut T {
     without_provenance_mut(mem::align_of::<T>())
 }
 
-/// Converts an address back to a pointer, picking up a previously 'exposed' provenance.
+/// Converts an address back to a pointer, picking up some previously 'exposed'
+/// [provenance][crate::ptr#provenance].
 ///
-/// This is a more rigorously specified alternative to `addr as *const T`. The provenance of the
-/// returned pointer is that of *any* pointer that was previously exposed by passing it to
-/// [`expose_provenance`][pointer::expose_provenance], or a `ptr as usize` cast. In addition, memory which is
-/// outside the control of the Rust abstract machine (MMIO registers, for example) is always
-/// considered to be exposed, so long as this memory is disjoint from memory that will be used by
-/// the abstract machine such as the stack, heap, and statics.
+/// This is fully equivalent to `addr as *const T`. The provenance of the returned pointer is that
+/// of *some* pointer that was previously exposed by passing it to
+/// [`expose_provenance`][pointer::expose_provenance], or a `ptr as usize` cast. In addition, memory
+/// which is outside the control of the Rust abstract machine (MMIO registers, for example) is
+/// always considered to be accessible with an exposed provenance, so long as this memory is disjoint
+/// from memory that will be used by the abstract machine such as the stack, heap, and statics.
 ///
-/// If there is no 'exposed' provenance that justifies the way this pointer will be used,
-/// the program has undefined behavior. In particular, the aliasing rules still apply: pointers
-/// and references that have been invalidated due to aliasing accesses cannot be used anymore,
-/// even if they have been exposed!
+/// The exact provenance that gets picked is not specified. The compiler will do its best to pick
+/// the "right" provenance for you (whatever that may be), but currently we cannot provide any
+/// guarantees about which provenance the resulting pointer will have -- and therefore there
+/// is no definite specification for which memory the resulting pointer may access.
 ///
-/// Note that there is no algorithm that decides which provenance will be used. You can think of this
-/// as "guessing" the right provenance, and the guess will be "maximally in your favor", in the sense
-/// that if there is any way to avoid undefined behavior (while upholding all aliasing requirements),
-/// then that is the guess that will be taken.
+/// If there is *no* previously 'exposed' provenance that justifies the way the returned pointer
+/// will be used, the program has undefined behavior. In particular, the aliasing rules still apply:
+/// pointers and references that have been invalidated due to aliasing accesses cannot be used
+/// anymore, even if they have been exposed!
 ///
-/// On platforms with multiple address spaces, it is your responsibility to ensure that the
-/// address makes sense in the address space that this pointer will be used with.
-///
-/// Using this function means that code is *not* following [Strict
-/// Provenance][self#strict-provenance] rules. "Guessing" a
-/// suitable provenance complicates specification and reasoning and may not be supported by
-/// tools that help you to stay conformant with the Rust memory model, so it is recommended to
-/// use [`with_addr`][pointer::with_addr] wherever possible.
+/// Due to its inherent ambiguity, this operation may not be supported by tools that help you to
+/// stay conformant with the Rust memory model. It is recommended to use [Strict
+/// Provenance][self#strict-provenance] APIs such as [`with_addr`][pointer::with_addr] wherever
+/// possible.
 ///
 /// On most platforms this will produce a value with the same bytes as the address. Platforms
 /// which need to store additional information in a pointer may not support this operation,
 /// since it is generally not possible to actually *compute* which provenance the returned
 /// pointer has to pick up.
 ///
-/// It is unclear whether this function can be given a satisfying unambiguous specification. This
-/// API and its claimed semantics are part of [Exposed Provenance][self#exposed-provenance].
+/// This is an [Exposed Provenance][crate::ptr#exposed-provenance] API.
 #[must_use]
 #[inline(always)]
-#[unstable(feature = "exposed_provenance", issue = "95228")]
+#[stable(feature = "exposed_provenance", since = "CURRENT_RUSTC_VERSION")]
 #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
 #[allow(fuzzy_provenance_casts)] // this *is* the explicit provenance API one should use instead
-pub fn with_exposed_provenance<T>(addr: usize) -> *const T
-where
-    T: Sized,
-{
-    // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
+pub fn with_exposed_provenance<T>(addr: usize) -> *const T {
     addr as *const T
 }
 
-/// Converts an address back to a mutable pointer, picking up a previously 'exposed' provenance.
+/// Converts an address back to a mutable pointer, picking up some previously 'exposed'
+/// [provenance][crate::ptr#provenance].
+///
+/// This is fully equivalent to `addr as *mut T`. The provenance of the returned pointer is that
+/// of *some* pointer that was previously exposed by passing it to
+/// [`expose_provenance`][pointer::expose_provenance], or a `ptr as usize` cast. In addition, memory
+/// which is outside the control of the Rust abstract machine (MMIO registers, for example) is
+/// always considered to be accessible with an exposed provenance, so long as this memory is disjoint
+/// from memory that will be used by the abstract machine such as the stack, heap, and statics.
 ///
-/// This is a more rigorously specified alternative to `addr as *mut T`. The provenance of the
-/// returned pointer is that of *any* pointer that was previously passed to
-/// [`expose_provenance`][pointer::expose_provenance] or a `ptr as usize` cast. If there is no previously
-/// 'exposed' provenance that justifies the way this pointer will be used, the program has undefined
-/// behavior. Note that there is no algorithm that decides which provenance will be used. You can
-/// think of this as "guessing" the right provenance, and the guess will be "maximally in your
-/// favor", in the sense that if there is any way to avoid undefined behavior, then that is the
-/// guess that will be taken.
+/// The exact provenance that gets picked is not specified. The compiler will do its best to pick
+/// the "right" provenance for you (whatever that may be), but currently we cannot provide any
+/// guarantees about which provenance the resulting pointer will have -- and therefore there
+/// is no definite specification for which memory the resulting pointer may access.
 ///
-/// On platforms with multiple address spaces, it is your responsibility to ensure that the
-/// address makes sense in the address space that this pointer will be used with.
+/// If there is *no* previously 'exposed' provenance that justifies the way the returned pointer
+/// will be used, the program has undefined behavior. In particular, the aliasing rules still apply:
+/// pointers and references that have been invalidated due to aliasing accesses cannot be used
+/// anymore, even if they have been exposed!
 ///
-/// Using this function means that code is *not* following [Strict
-/// Provenance][self#strict-provenance] rules. "Guessing" a
-/// suitable provenance complicates specification and reasoning and may not be supported by
-/// tools that help you to stay conformant with the Rust memory model, so it is recommended to
-/// use [`with_addr`][pointer::with_addr] wherever possible.
+/// Due to its inherent ambiguity, this operation may not be supported by tools that help you to
+/// stay conformant with the Rust memory model. It is recommended to use [Strict
+/// Provenance][self#strict-provenance] APIs such as [`with_addr`][pointer::with_addr] wherever
+/// possible.
 ///
 /// On most platforms this will produce a value with the same bytes as the address. Platforms
 /// which need to store additional information in a pointer may not support this operation,
 /// since it is generally not possible to actually *compute* which provenance the returned
 /// pointer has to pick up.
 ///
-/// It is unclear whether this function can be given a satisfying unambiguous specification. This
-/// API and its claimed semantics are part of [Exposed Provenance][self#exposed-provenance].
+/// This is an [Exposed Provenance][crate::ptr#exposed-provenance] API.
 #[must_use]
 #[inline(always)]
-#[unstable(feature = "exposed_provenance", issue = "95228")]
+#[stable(feature = "exposed_provenance", since = "CURRENT_RUSTC_VERSION")]
 #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
 #[allow(fuzzy_provenance_casts)] // this *is* the explicit provenance API one should use instead
-pub fn with_exposed_provenance_mut<T>(addr: usize) -> *mut T
-where
-    T: Sized,
-{
-    // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
+pub fn with_exposed_provenance_mut<T>(addr: usize) -> *mut T {
     addr as *mut T
 }
 
diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs
index 613d2c91ac6..aac42f517ef 100644
--- a/library/core/src/ptr/mut_ptr.rs
+++ b/library/core/src/ptr/mut_ptr.rs
@@ -124,12 +124,12 @@ impl<T: ?Sized> *mut T {
 
     /// Gets the "address" portion of the pointer.
     ///
-    /// 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 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].
+    /// This is similar to `self as usize`, except that the [provenance][crate::ptr#provenance] of
+    /// the pointer is discarded and not [exposed][crate::ptr#exposed-provenance]. This means that
+    /// casting the returned address back to a pointer 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
@@ -143,89 +143,80 @@ impl<T: ?Sized> *mut T {
     /// perform a change of representation to produce a value containing only the address
     /// portion of the pointer. What that means is up to the platform to define.
     ///
-    /// This API and its claimed semantics are part of the Strict Provenance experiment, and as such
-    /// might change in the future (including possibly weakening this so it becomes wholly
-    /// equivalent to `self as usize`). See the [module documentation][crate::ptr] for details.
+    /// This is a [Strict Provenance][crate::ptr#strict-provenance] API.
     #[must_use]
     #[inline(always)]
-    #[unstable(feature = "strict_provenance", issue = "95228")]
+    #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")]
     pub fn addr(self) -> usize {
-        // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
+        // A pointer-to-integer transmute currently has exactly the right semantics: it returns the
+        // address without exposing the provenance. Note that this is *not* a stable guarantee about
+        // transmute semantics, it relies on sysroot crates having special status.
         // SAFETY: Pointer-to-integer transmutes are valid (if you are okay with losing the
         // provenance).
         unsafe { mem::transmute(self.cast::<()>()) }
     }
 
-    /// Exposes the "provenance" part of the pointer for future use in
-    /// [`with_exposed_provenance`][] and returns the "address" portion.
+    /// Exposes the ["provenance"][crate::ptr#provenance] part of the pointer for future use in
+    /// [`with_exposed_provenance_mut`] and returns the "address" portion.
     ///
-    /// This is equivalent to `self as usize`, which semantically discards *provenance* and
-    /// *address-space* information. Furthermore, this (like the `as` cast) has the implicit
-    /// side-effect of marking the provenance as 'exposed', so on platforms that support it you can
-    /// later call [`with_exposed_provenance_mut`][] to reconstitute the original pointer including its
-    /// provenance. (Reconstructing address space information, if required, is your responsibility.)
+    /// This is equivalent to `self as usize`, which semantically discards provenance information.
+    /// Furthermore, this (like the `as` cast) has the implicit side-effect of marking the
+    /// provenance as 'exposed', so on platforms that support it you can later call
+    /// [`with_exposed_provenance_mut`] to reconstitute the original pointer including its provenance.
     ///
-    /// Using this method means that code is *not* following [Strict
-    /// Provenance][super#strict-provenance] rules. Supporting
-    /// [`with_exposed_provenance_mut`][] complicates specification and reasoning and may not be supported
-    /// by tools that help you to stay conformant with the Rust memory model, so it is recommended
-    /// to use [`addr`][pointer::addr] wherever possible.
+    /// Due to its inherent ambiguity, [`with_exposed_provenance_mut`] may not be supported by tools
+    /// that help you to stay conformant with the Rust memory model. It is recommended to use
+    /// [Strict Provenance][crate::ptr#strict-provenance] APIs such as [`with_addr`][pointer::with_addr]
+    /// wherever possible, in which case [`addr`][pointer::addr] should be used instead of `expose_provenance`.
     ///
     /// On most platforms this will produce a value with the same bytes as the original pointer,
     /// because all the bytes are dedicated to describing the address. Platforms which need to store
     /// additional information in the pointer may not support this operation, since the 'expose'
-    /// side-effect which is required for [`with_exposed_provenance_mut`][] to work is typically not
+    /// side-effect which is required for [`with_exposed_provenance_mut`] to work is typically not
     /// available.
     ///
-    /// It is unclear whether this method can be given a satisfying unambiguous specification. This
-    /// API and its claimed semantics are part of [Exposed Provenance][super#exposed-provenance].
+    /// This is an [Exposed Provenance][crate::ptr#exposed-provenance] API.
     ///
     /// [`with_exposed_provenance_mut`]: with_exposed_provenance_mut
     #[inline(always)]
-    #[unstable(feature = "exposed_provenance", issue = "95228")]
+    #[stable(feature = "exposed_provenance", since = "CURRENT_RUSTC_VERSION")]
     pub fn expose_provenance(self) -> usize {
-        // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
         self.cast::<()>() as usize
     }
 
-    /// Creates a new pointer with the given address.
+    /// Creates a new pointer with the given address and the [provenance][crate::ptr#provenance] of
+    /// `self`.
     ///
-    /// This performs the same operation as an `addr as ptr` cast, but copies
-    /// the *address-space* and *provenance* of `self` to the new pointer.
-    /// This allows us to dynamically preserve and propagate this important
-    /// information in a way that is otherwise impossible with a unary cast.
+    /// This is similar to a `addr as *mut T` cast, but copies
+    /// the *provenance* of `self` to the new pointer.
+    /// This avoids the inherent ambiguity of the unary cast.
     ///
     /// This is equivalent to using [`wrapping_offset`][pointer::wrapping_offset] to offset
     /// `self` to the given address, and therefore has all the same capabilities and restrictions.
     ///
-    /// This API and its claimed semantics are an extension to the Strict Provenance experiment,
-    /// see the [module documentation][crate::ptr] for details.
+    /// This is a [Strict Provenance][crate::ptr#strict-provenance] API.
     #[must_use]
     #[inline]
-    #[unstable(feature = "strict_provenance", issue = "95228")]
+    #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")]
     pub fn with_addr(self, addr: usize) -> Self {
-        // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
-        //
-        // In the mean-time, this operation is defined to be "as if" it was
-        // a wrapping_offset, so we can emulate it as such. This should properly
-        // restore pointer provenance even under today's compiler.
+        // This should probably be an intrinsic to avoid doing any sort of arithmetic, but
+        // meanwhile, we can implement it with `wrapping_offset`, which preserves the pointer's
+        // provenance.
         let self_addr = self.addr() as isize;
         let dest_addr = addr as isize;
         let offset = dest_addr.wrapping_sub(self_addr);
-
-        // This is the canonical desugaring of this operation
         self.wrapping_byte_offset(offset)
     }
 
-    /// Creates a new pointer by mapping `self`'s address to a new one.
+    /// Creates a new pointer by mapping `self`'s address to a new one, preserving the original
+    /// pointer's [provenance][crate::ptr#provenance].
     ///
     /// This is a convenience for [`with_addr`][pointer::with_addr], see that method for details.
     ///
-    /// This API and its claimed semantics are part of the Strict Provenance experiment,
-    /// see the [module documentation][crate::ptr] for details.
+    /// This is a [Strict Provenance][crate::ptr#strict-provenance] API.
     #[must_use]
     #[inline]
-    #[unstable(feature = "strict_provenance", issue = "95228")]
+    #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")]
     pub fn map_addr(self, f: impl FnOnce(usize) -> usize) -> Self {
         self.with_addr(f(self.addr()))
     }
@@ -376,7 +367,7 @@ impl<T: ?Sized> *mut T {
     /// * The offset in bytes, `count * size_of::<T>()`, computed on mathematical integers (without
     ///   "wrapping around"), must fit in an `isize`.
     ///
-    /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some
+    /// * If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] 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.
@@ -777,7 +768,7 @@ impl<T: ?Sized> *mut T {
     /// * `self` and `origin` must either
     ///
     ///   * point to the same address, or
-    ///   * both be *derived from* a pointer to the same [allocated object], and the memory range between
+    ///   * both be [derived from][crate::ptr#provenance] a pointer to the same [allocated object], and the memory range between
     ///     the two pointers must be in bounds of that object. (See below for an example.)
     ///
     /// * The distance between the pointers, in bytes, must be an exact multiple
@@ -954,7 +945,7 @@ impl<T: ?Sized> *mut T {
     /// * The offset in bytes, `count * size_of::<T>()`, computed on mathematical integers (without
     ///   "wrapping around"), must fit in an `isize`.
     ///
-    /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some
+    /// * If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] 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.
@@ -1061,7 +1052,7 @@ impl<T: ?Sized> *mut T {
     /// * The offset in bytes, `count * size_of::<T>()`, computed on mathematical integers (without
     ///   "wrapping around"), must fit in an `isize`.
     ///
-    /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some
+    /// * If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] 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.
diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs
index 3e4cae2b3ca..0477147d87f 100644
--- a/library/core/src/ptr/non_null.rs
+++ b/library/core/src/ptr/non_null.rs
@@ -283,40 +283,39 @@ impl<T: ?Sized> NonNull<T> {
     ///
     /// For more details see the equivalent method on a raw pointer, [`pointer::addr`].
     ///
-    /// This API and its claimed semantics are part of the Strict Provenance experiment,
-    /// see the [`ptr` module documentation][crate::ptr].
+    /// This is a [Strict Provenance][crate::ptr#strict-provenance] API.
     #[must_use]
     #[inline]
-    #[unstable(feature = "strict_provenance", issue = "95228")]
+    #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")]
     pub fn addr(self) -> NonZero<usize> {
         // SAFETY: The pointer is guaranteed by the type to be non-null,
         // meaning that the address will be non-zero.
         unsafe { NonZero::new_unchecked(self.pointer.addr()) }
     }
 
-    /// Creates a new pointer with the given address.
+    /// Creates a new pointer with the given address and the [provenance][crate::ptr#provenance] of
+    /// `self`.
     ///
     /// For more details see the equivalent method on a raw pointer, [`pointer::with_addr`].
     ///
-    /// This API and its claimed semantics are part of the Strict Provenance experiment,
-    /// see the [`ptr` module documentation][crate::ptr].
+    /// This is a [Strict Provenance][crate::ptr#strict-provenance] API.
     #[must_use]
     #[inline]
-    #[unstable(feature = "strict_provenance", issue = "95228")]
+    #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")]
     pub fn with_addr(self, addr: NonZero<usize>) -> Self {
         // SAFETY: The result of `ptr::from::with_addr` is non-null because `addr` is guaranteed to be non-zero.
         unsafe { NonNull::new_unchecked(self.pointer.with_addr(addr.get()) as *mut _) }
     }
 
-    /// Creates a new pointer by mapping `self`'s address to a new one.
+    /// Creates a new pointer by mapping `self`'s address to a new one, preserving the
+    /// [provenance][crate::ptr#provenance] of `self`.
     ///
     /// For more details see the equivalent method on a raw pointer, [`pointer::map_addr`].
     ///
-    /// This API and its claimed semantics are part of the Strict Provenance experiment,
-    /// see the [`ptr` module documentation][crate::ptr].
+    /// This is a [Strict Provenance][crate::ptr#strict-provenance] API.
     #[must_use]
     #[inline]
-    #[unstable(feature = "strict_provenance", issue = "95228")]
+    #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")]
     pub fn map_addr(self, f: impl FnOnce(NonZero<usize>) -> NonZero<usize>) -> Self {
         self.with_addr(f(self.addr()))
     }