move exposed-provenance APIs into separate feature gate and explain the relationship of Exposed Provenance and Strict Provenance

This commit is contained in:
Ralf Jung 2023-11-30 21:45:57 +01:00
parent 00796255c2
commit dbea549d80
11 changed files with 68 additions and 53 deletions

View File

@ -219,7 +219,8 @@ impl<T: ?Sized> *const T {
/// later call [`from_exposed_addr`][] to reconstitute the original pointer including its /// later call [`from_exposed_addr`][] to reconstitute the original pointer including its
/// provenance. (Reconstructing address space information, if required, is your responsibility.) /// provenance. (Reconstructing address space information, if required, is your responsibility.)
/// ///
/// Using this method means that code is *not* following Strict Provenance rules. Supporting /// Using this method means that code is *not* following [Strict
/// Provenance][../index.html#strict-provenance] rules. Supporting
/// [`from_exposed_addr`][] complicates specification and reasoning and may not be supported by /// [`from_exposed_addr`][] 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 /// tools that help you to stay conformant with the Rust memory model, so it is recommended to
/// use [`addr`][pointer::addr] wherever possible. /// use [`addr`][pointer::addr] wherever possible.
@ -230,13 +231,13 @@ impl<T: ?Sized> *const T {
/// side-effect which is required for [`from_exposed_addr`][] to work is typically not /// side-effect which is required for [`from_exposed_addr`][] to work is typically not
/// available. /// available.
/// ///
/// This API and its claimed semantics are part of the Strict Provenance experiment, see the /// It is unclear whether this method can be given a satisfying unambiguous specification. This
/// [module documentation][crate::ptr] for details. /// API and its claimed semantics are part of [Exposed Provenance][../index.html#exposed-provenance].
/// ///
/// [`from_exposed_addr`]: from_exposed_addr /// [`from_exposed_addr`]: from_exposed_addr
#[must_use] #[must_use]
#[inline(always)] #[inline(always)]
#[unstable(feature = "strict_provenance", issue = "95228")] #[unstable(feature = "exposed_provenance", issue = "95228")]
pub fn expose_addr(self) -> usize { pub fn expose_addr(self) -> usize {
// FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
self.cast::<()>() as usize self.cast::<()>() as usize

View File

@ -312,22 +312,30 @@
//! For instance, ARM explicitly supports high-bit tagging, and so CHERI on ARM inherits //! For instance, ARM explicitly supports high-bit tagging, and so CHERI on ARM inherits
//! that and should support it. //! that and should support it.
//! //!
//! ## Pointer-usize-pointer roundtrips and 'exposed' provenance //! ## Exposed Provenance
//! //!
//! **This section is *non-normative* and is part of the [Strict Provenance] experiment.** //! **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, pointer-usize-pointer roundtrips are not possible under [Strict Provenance].
//! However, there exists legacy Rust code that is full of such roundtrips, and legacy platform APIs //! This is by design: the goal of Strict Provenance is to provide a clear specification that we are
//! regularly assume that `usize` can capture all the information that makes up a pointer. There //! confident can be formalized unambiguously and can be subject to precise formal reasoning.
//! also might be code that cannot be ported to Strict Provenance (which is something we would [like
//! to hear about][Strict Provenance]).
//! //!
//! For situations like this, there is a fallback plan, a way to 'opt out' of Strict Provenance. //! However, there exist situations where pointer-usize-pointer roundtrips cannot be avoided, or
//! However, note that this makes your code a lot harder to specify, and the code will not work //! where avoiding them would require major refactoring. Legacy platform APIs also regularly assume
//! (well) with tools like [Miri] and [CHERI]. //! 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.
//! //!
//! This fallback plan is provided by the [`expose_addr`] and [`from_exposed_addr`] methods (which //! For these situations, there is a highly experimental extension to Strict Provenance called
//! are equivalent to `as` casts between pointers and integers). [`expose_addr`] is a lot like //! *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].
//!
//! Exposed Provenance is provided by the [`expose_addr`] and [`from_exposed_addr`] methods, which
//! are meant to replace `as` casts between pointers and integers. [`expose_addr`] is a lot like
//! [`addr`], but additionally adds the provenance of the pointer to a global list of 'exposed' //! [`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 //! 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].) [`from_exposed_addr`] //! is not materialized in actual executions, except in tools like [Miri].) [`from_exposed_addr`]
@ -341,10 +349,11 @@
//! there is *no* previously 'exposed' provenance that justifies the way the returned pointer will //! there is *no* previously 'exposed' provenance that justifies the way the returned pointer will
//! be used, the program has undefined behavior. //! be used, the program has undefined behavior.
//! //!
//! Using [`expose_addr`] or [`from_exposed_addr`] (or the equivalent `as` casts) means that code is //! Using [`expose_addr`] or [`from_exposed_addr`] (or the `as` casts) means that code is
//! *not* following Strict Provenance rules. The goal of the Strict Provenance experiment is to //! *not* following Strict Provenance rules. The goal of the Strict Provenance experiment is to
//! determine whether it is possible to use Rust without [`expose_addr`] and [`from_exposed_addr`]. //! determine how far one can get in Rust without the use of [`expose_addr`] and
//! If this is successful, it would be a major win for avoiding specification complexity and to //! [`from_exposed_addr`], 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 //! facilitate adoption of tools like [CHERI] and [Miri] that can be a big help in increasing the
//! confidence in (unsafe) Rust code. //! confidence in (unsafe) Rust code.
//! //!
@ -619,12 +628,12 @@ pub const fn invalid_mut<T>(addr: usize) -> *mut T {
/// Convert an address back to a pointer, picking up a previously 'exposed' provenance. /// Convert an address back to a pointer, picking up a previously 'exposed' provenance.
/// ///
/// This is equivalent to `addr as *const T`. The provenance of the returned pointer is that of *any* /// This is a more rigorously specified alternative to `addr as *const T`. The provenance of the
/// pointer that was previously exposed by passing it to [`expose_addr`][pointer::expose_addr], /// returned pointer is that of *any* pointer that was previously exposed by passing it to
/// or a `ptr as usize` cast. In addition, memory which is outside the control of the Rust abstract /// [`expose_addr`][pointer::expose_addr], or a `ptr as usize` cast. In addition, memory which is
/// machine (MMIO registers, for example) is always considered to be exposed, so long as this memory /// outside the control of the Rust abstract machine (MMIO registers, for example) is always
/// is disjoint from memory that will be used by the abstract machine such as the stack, heap, /// considered to be exposed, so long as this memory is disjoint from memory that will be used by
/// and statics. /// 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, /// 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 /// the program has undefined behavior. In particular, the aliasing rules still apply: pointers
@ -639,7 +648,8 @@ pub const fn invalid_mut<T>(addr: usize) -> *mut T {
/// On platforms with multiple address spaces, it is your responsibility to ensure that the /// 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. /// address makes sense in the address space that this pointer will be used with.
/// ///
/// Using this method means that code is *not* following strict provenance rules. "Guessing" a /// Using this function means that code is *not* following [Strict
/// Provenance][../index.html#strict-provenance] rules. "Guessing" a
/// suitable provenance complicates specification and reasoning and may not be supported by /// 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 /// 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. /// use [`with_addr`][pointer::with_addr] wherever possible.
@ -649,13 +659,13 @@ pub const fn invalid_mut<T>(addr: usize) -> *mut T {
/// since it is generally not possible to actually *compute* which provenance the returned /// since it is generally not possible to actually *compute* which provenance the returned
/// pointer has to pick up. /// pointer has to pick up.
/// ///
/// This API and its claimed semantics are part of the Strict Provenance experiment, see the /// It is unclear whether this function can be given a satisfying unambiguous specification. This
/// [module documentation][crate::ptr] for details. /// API and its claimed semantics are part of [Exposed Provenance][../index.html#exposed-provenance].
#[must_use] #[must_use]
#[inline(always)] #[inline(always)]
#[unstable(feature = "strict_provenance", issue = "95228")] #[unstable(feature = "exposed_provenance", issue = "95228")]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
#[allow(fuzzy_provenance_casts)] // this *is* the strict provenance API one should use instead #[allow(fuzzy_provenance_casts)] // this *is* the explicit provenance API one should use instead
pub fn from_exposed_addr<T>(addr: usize) -> *const T pub fn from_exposed_addr<T>(addr: usize) -> *const T
where where
T: Sized, T: Sized,
@ -666,18 +676,20 @@ where
/// Convert an address back to a mutable pointer, picking up a previously 'exposed' provenance. /// Convert an address back to a mutable pointer, picking up a previously 'exposed' provenance.
/// ///
/// This is equivalent to `addr as *mut T`. The provenance of the returned pointer is that of *any* /// This is a more rigorously specified alternative to `addr as *mut T`. The provenance of the
/// pointer that was previously passed to [`expose_addr`][pointer::expose_addr] or a `ptr as usize` /// returned pointer is that of *any* pointer that was previously passed to
/// cast. If there is no previously 'exposed' provenance that justifies the way this pointer will be /// [`expose_addr`][pointer::expose_addr] or a `ptr as usize` cast. If there is no previously
/// used, the program has undefined behavior. Note that there is no algorithm that decides which /// 'exposed' provenance that justifies the way this pointer will be used, the program has undefined
/// provenance will be used. You can think of this as "guessing" the right provenance, and the guess /// behavior. Note that there is no algorithm that decides which provenance will be used. You can
/// will be "maximally in your favor", in the sense that if there is any way to avoid undefined /// think of this as "guessing" the right provenance, and the guess will be "maximally in your
/// behavior, then that is the guess that will be taken. /// favor", in the sense that if there is any way to avoid undefined behavior, then that is the
/// guess that will be taken.
/// ///
/// On platforms with multiple address spaces, it is your responsibility to ensure that the /// 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. /// address makes sense in the address space that this pointer will be used with.
/// ///
/// Using this method means that code is *not* following strict provenance rules. "Guessing" a /// Using this function means that code is *not* following [Strict
/// Provenance][../index.html#strict-provenance] rules. "Guessing" a
/// suitable provenance complicates specification and reasoning and may not be supported by /// 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 /// 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. /// use [`with_addr`][pointer::with_addr] wherever possible.
@ -687,13 +699,13 @@ where
/// since it is generally not possible to actually *compute* which provenance the returned /// since it is generally not possible to actually *compute* which provenance the returned
/// pointer has to pick up. /// pointer has to pick up.
/// ///
/// This API and its claimed semantics are part of the Strict Provenance experiment, see the /// It is unclear whether this function can be given a satisfying unambiguous specification. This
/// [module documentation][crate::ptr] for details. /// API and its claimed semantics are part of [Exposed Provenance][../index.html#exposed-provenance].
#[must_use] #[must_use]
#[inline(always)] #[inline(always)]
#[unstable(feature = "strict_provenance", issue = "95228")] #[unstable(feature = "exposed_provenance", issue = "95228")]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
#[allow(fuzzy_provenance_casts)] // this *is* the strict provenance API one should use instead #[allow(fuzzy_provenance_casts)] // this *is* the explicit provenance API one should use instead
pub fn from_exposed_addr_mut<T>(addr: usize) -> *mut T pub fn from_exposed_addr_mut<T>(addr: usize) -> *mut T
where where
T: Sized, T: Sized,

View File

@ -226,7 +226,8 @@ impl<T: ?Sized> *mut T {
/// later call [`from_exposed_addr_mut`][] to reconstitute the original pointer including its /// later call [`from_exposed_addr_mut`][] to reconstitute the original pointer including its
/// provenance. (Reconstructing address space information, if required, is your responsibility.) /// provenance. (Reconstructing address space information, if required, is your responsibility.)
/// ///
/// Using this method means that code is *not* following Strict Provenance rules. Supporting /// Using this method means that code is *not* following [Strict
/// Provenance][../index.html#strict-provenance] rules. Supporting
/// [`from_exposed_addr_mut`][] complicates specification and reasoning and may not be supported /// [`from_exposed_addr_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 /// by tools that help you to stay conformant with the Rust memory model, so it is recommended
/// to use [`addr`][pointer::addr] wherever possible. /// to use [`addr`][pointer::addr] wherever possible.
@ -237,13 +238,13 @@ impl<T: ?Sized> *mut T {
/// side-effect which is required for [`from_exposed_addr_mut`][] to work is typically not /// side-effect which is required for [`from_exposed_addr_mut`][] to work is typically not
/// available. /// available.
/// ///
/// This API and its claimed semantics are part of the Strict Provenance experiment, see the /// It is unclear whether this method can be given a satisfying unambiguous specification. This
/// [module documentation][crate::ptr] for details. /// API and its claimed semantics are part of [Exposed Provenance][../index.html#exposed-provenance].
/// ///
/// [`from_exposed_addr_mut`]: from_exposed_addr_mut /// [`from_exposed_addr_mut`]: from_exposed_addr_mut
#[must_use] #[must_use]
#[inline(always)] #[inline(always)]
#[unstable(feature = "strict_provenance", issue = "95228")] #[unstable(feature = "exposed_provenance", issue = "95228")]
pub fn expose_addr(self) -> usize { pub fn expose_addr(self) -> usize {
// FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
self.cast::<()>() as usize self.cast::<()>() as usize
@ -259,7 +260,7 @@ impl<T: ?Sized> *mut T {
/// This is equivalent to using [`wrapping_offset`][pointer::wrapping_offset] to offset /// 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. /// `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, /// This API and its claimed semantics are an extension to the Strict Provenance experiment,
/// see the [module documentation][crate::ptr] for details. /// see the [module documentation][crate::ptr] for details.
#[must_use] #[must_use]
#[inline] #[inline]

View File

@ -317,6 +317,7 @@
#![feature(error_iter)] #![feature(error_iter)]
#![feature(exact_size_is_empty)] #![feature(exact_size_is_empty)]
#![feature(exclusive_wrapper)] #![feature(exclusive_wrapper)]
#![feature(exposed_provenance)]
#![feature(extend_one)] #![feature(extend_one)]
#![feature(float_gamma)] #![feature(float_gamma)]
#![feature(float_minimum_maximum)] #![feature(float_minimum_maximum)]

View File

@ -1,5 +1,5 @@
//@compile-flags: -Zmiri-permissive-provenance //@compile-flags: -Zmiri-permissive-provenance
#![feature(strict_provenance)] #![feature(strict_provenance, exposed_provenance)]
fn main() { fn main() {
let x: i32 = 3; let x: i32 = 3;

View File

@ -1,4 +1,4 @@
#![feature(strict_provenance)] #![feature(strict_provenance, exposed_provenance)]
// Ensure that a `ptr::invalid` ptr is truly invalid. // Ensure that a `ptr::invalid` ptr is truly invalid.
fn main() { fn main() {

View File

@ -1,5 +1,5 @@
//@compile-flags: -Zmiri-strict-provenance //@compile-flags: -Zmiri-strict-provenance
#![feature(strict_provenance)] #![feature(exposed_provenance)]
fn main() { fn main() {
let addr = &0 as *const i32 as usize; let addr = &0 as *const i32 as usize;

View File

@ -1,5 +1,5 @@
//@compile-flags: -Zmiri-permissive-provenance //@compile-flags: -Zmiri-permissive-provenance
#![feature(strict_provenance)] #![feature(exposed_provenance)]
// If we have only exposed read-only pointers, doing a write through a wildcard ptr should fail. // If we have only exposed read-only pointers, doing a write through a wildcard ptr should fail.

View File

@ -1,7 +1,7 @@
//@revisions: stack tree //@revisions: stack tree
//@[tree]compile-flags: -Zmiri-tree-borrows //@[tree]compile-flags: -Zmiri-tree-borrows
//@compile-flags: -Zmiri-permissive-provenance //@compile-flags: -Zmiri-permissive-provenance
#![feature(strict_provenance)] #![feature(strict_provenance, exposed_provenance)]
use std::ptr; use std::ptr;

View File

@ -1,5 +1,5 @@
//@compile-flags: -Zmiri-permissive-provenance //@compile-flags: -Zmiri-permissive-provenance
#![feature(strict_provenance)] #![feature(exposed_provenance)]
use std::ptr; use std::ptr;
// Just to make sure that casting a ref to raw, to int and back to raw // Just to make sure that casting a ref to raw, to int and back to raw

View File

@ -1,5 +1,5 @@
//@compile-flags: -Zmiri-permissive-provenance //@compile-flags: -Zmiri-permissive-provenance
#![feature(strict_provenance)] #![feature(exposed_provenance)]
use std::ptr; use std::ptr;