Skip to content

Commit

Permalink
stabilize Strict Provenance and Exposed Provenance
Browse files Browse the repository at this point in the history
This comes with a big docs rewrite. It's mostly just rewording, except:

- I removed the discussion of how pointers also have an associated "address
  space" -- that is not actually tracked in the pointer value, it is tracked in
  the type, so IMO it just distracts from the core point of provenance.
- I also adjusted the docs for with_exposed_provenance to make it clear that we
  cannot guarantee much about this function, it's all best-effort.
  • Loading branch information
RalfJung committed Sep 14, 2024
1 parent f9567d0 commit 18c72d0
Show file tree
Hide file tree
Showing 4 changed files with 237 additions and 309 deletions.
79 changes: 37 additions & 42 deletions library/core/src/ptr/const_ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,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
Expand All @@ -134,67 +135,61 @@ 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 has exactly the right semantics: it returns
// the address without exposing the provenance.
// 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.
//
Expand All @@ -209,15 +204,15 @@ impl<T: ?Sized> *const T {
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()))
}
Expand Down Expand Up @@ -357,7 +352,7 @@ impl<T: ?Sized> *const T {
///
/// * The computed offset, `count * size_of::<T>()` bytes, must not overflow `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.
Expand Down Expand Up @@ -561,7 +556,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
Expand Down Expand Up @@ -818,7 +813,7 @@ impl<T: ?Sized> *const T {
///
/// * The computed offset, `count * size_of::<T>()` bytes, must not overflow `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.
Expand Down Expand Up @@ -892,7 +887,7 @@ impl<T: ?Sized> *const T {
///
/// * The computed offset, `count * size_of::<T>()` bytes, must not overflow `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.
Expand Down
Loading

0 comments on commit 18c72d0

Please sign in to comment.