Skip to content

Commit

Permalink
Rollup merge of #130350 - RalfJung:strict-provenance, r=dtolnay
Browse files Browse the repository at this point in the history
stabilize Strict Provenance and Exposed Provenance APIs

Given that [RFC 3559](https://rust-lang.github.io/rfcs/3559-rust-has-provenance.html) has been accepted, t-lang has approved the concept of provenance to exist in the language. So I think it's time that we stabilize the strict provenance and exposed provenance APIs, and discuss provenance explicitly in the docs:
```rust
// core::ptr
pub const fn without_provenance<T>(addr: usize) -> *const T;
pub const fn dangling<T>() -> *const T;
pub const fn without_provenance_mut<T>(addr: usize) -> *mut T;
pub const fn dangling_mut<T>() -> *mut T;
pub fn with_exposed_provenance<T>(addr: usize) -> *const T;
pub fn with_exposed_provenance_mut<T>(addr: usize) -> *mut T;

impl<T: ?Sized> *const T {
    pub fn addr(self) -> usize;
    pub fn expose_provenance(self) -> usize;
    pub fn with_addr(self, addr: usize) -> Self;
    pub fn map_addr(self, f: impl FnOnce(usize) -> usize) -> Self;
}

impl<T: ?Sized> *mut T {
    pub fn addr(self) -> usize;
    pub fn expose_provenance(self) -> usize;
    pub fn with_addr(self, addr: usize) -> Self;
    pub fn map_addr(self, f: impl FnOnce(usize) -> usize) -> Self;
}

impl<T: ?Sized> NonNull<T> {
    pub fn addr(self) -> NonZero<usize>;
    pub fn with_addr(self, addr: NonZero<usize>) -> Self;
    pub fn map_addr(self, f: impl FnOnce(NonZero<usize>) -> NonZero<usize>) -> Self;
}
```

I also did a pass over the docs to adjust them, because this is no longer an "experiment". The `ptr` docs now discuss the concept of provenance in general, and then they go into the two families of APIs for dealing with provenance: Strict Provenance and Exposed Provenance. 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.

There are two unstable lints associated with the strict_provenance feature gate; I moved them to a new [strict_provenance_lints](#130351) feature since I didn't want this PR to have an even bigger FCP. ;)

`@rust-lang/opsem` Would be great to get some feedback on the docs here. :)
Nominating for `@rust-lang/libs-api.`

Part of #95228.

[FCP comment](#130350 (comment))
  • Loading branch information
matthiaskrgr authored Oct 21, 2024
2 parents 3ec4308 + 56ee492 commit 20b1dad
Show file tree
Hide file tree
Showing 90 changed files with 352 additions and 514 deletions.
1 change: 0 additions & 1 deletion compiler/rustc_arena/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
#![feature(maybe_uninit_slice)]
#![feature(rustc_attrs)]
#![feature(rustdoc_internals)]
#![feature(strict_provenance)]
#![warn(unreachable_pub)]
// tidy-alphabetical-end

Expand Down
1 change: 0 additions & 1 deletion compiler/rustc_codegen_ssa/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
#![feature(let_chains)]
#![feature(negative_impls)]
#![feature(rustdoc_internals)]
#![feature(strict_provenance)]
#![feature(trait_alias)]
#![feature(try_blocks)]
#![warn(unreachable_pub)]
Expand Down
6 changes: 5 additions & 1 deletion compiler/rustc_codegen_ssa/src/mir/rvalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
1 change: 0 additions & 1 deletion compiler/rustc_const_eval/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
#![feature(never_type)]
#![feature(rustdoc_internals)]
#![feature(slice_ptr_get)]
#![feature(strict_provenance)]
#![feature(trait_alias)]
#![feature(try_blocks)]
#![feature(unqualified_local_imports)]
Expand Down
1 change: 0 additions & 1 deletion compiler/rustc_data_structures/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
#![feature(ptr_alignment_type)]
#![feature(rustc_attrs)]
#![feature(rustdoc_internals)]
#![feature(strict_provenance)]
#![feature(test)]
#![feature(thread_id_value)]
#![feature(type_alias_impl_trait)]
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -595,7 +595,7 @@ declare_features! (
/// Allows attributes on expressions and non-item statements.
(unstable, stmt_expr_attributes, "1.6.0", Some(15701)),
/// Allows lints part of the strict provenance effort.
(unstable, strict_provenance, "1.61.0", Some(95228)),
(unstable, strict_provenance_lints, "1.61.0", Some(130351)),
/// Allows string patterns to dereference values to match them.
(unstable, string_deref_patterns, "1.67.0", Some(87121)),
/// Allows the use of `#[target_feature]` on safe functions.
Expand Down
6 changes: 2 additions & 4 deletions compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2667,7 +2667,6 @@ declare_lint! {
/// ### Example
///
/// ```rust
/// #![feature(strict_provenance)]
/// #![warn(fuzzy_provenance_casts)]
///
/// fn main() {
Expand Down Expand Up @@ -2701,7 +2700,7 @@ declare_lint! {
pub FUZZY_PROVENANCE_CASTS,
Allow,
"a fuzzy integer to pointer cast is used",
@feature_gate = strict_provenance;
@feature_gate = strict_provenance_lints;
}

declare_lint! {
Expand All @@ -2711,7 +2710,6 @@ declare_lint! {
/// ### Example
///
/// ```rust
/// #![feature(strict_provenance)]
/// #![warn(lossy_provenance_casts)]
///
/// fn main() {
Expand Down Expand Up @@ -2747,7 +2745,7 @@ declare_lint! {
pub LOSSY_PROVENANCE_CASTS,
Allow,
"a lossy pointer to integer cast is used",
@feature_gate = strict_provenance;
@feature_gate = strict_provenance_lints;
}

declare_lint! {
Expand Down
1 change: 0 additions & 1 deletion compiler/rustc_middle/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@
#![feature(ptr_alignment_type)]
#![feature(rustc_attrs)]
#![feature(rustdoc_internals)]
#![feature(strict_provenance)]
#![feature(trait_upcasting)]
#![feature(trusted_len)]
#![feature(try_blocks)]
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1913,7 +1913,7 @@ symbols! {
str_trim,
str_trim_end,
str_trim_start,
strict_provenance,
strict_provenance_lints,
string_as_mut_str,
string_as_str,
string_deref_patterns,
Expand Down
3 changes: 2 additions & 1 deletion library/alloc/benches/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
#![feature(iter_next_chunk)]
#![feature(repr_simd)]
#![feature(slice_partition_dedup)]
#![feature(strict_provenance)]
#![cfg_attr(bootstrap, feature(strict_provenance))]
#![cfg_attr(not(bootstrap), feature(strict_provenance_lints))]
#![feature(test)]
#![deny(fuzzy_provenance_casts)]

Expand Down
3 changes: 2 additions & 1 deletion library/alloc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,6 @@
#![feature(slice_range)]
#![feature(std_internals)]
#![feature(str_internals)]
#![feature(strict_provenance)]
#![feature(trusted_fused)]
#![feature(trusted_len)]
#![feature(trusted_random_access)]
Expand All @@ -162,6 +161,8 @@
//
// Language features:
// tidy-alphabetical-start
#![cfg_attr(bootstrap, feature(strict_provenance))]
#![cfg_attr(not(bootstrap), feature(strict_provenance_lints))]
#![cfg_attr(not(test), feature(coroutine_trait))]
#![cfg_attr(test, feature(panic_update_hook))]
#![cfg_attr(test, feature(test))]
Expand Down
3 changes: 2 additions & 1 deletion library/alloc/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
#![feature(panic_update_hook)]
#![feature(pointer_is_aligned_to)]
#![feature(thin_box)]
#![feature(strict_provenance)]
#![cfg_attr(bootstrap, feature(strict_provenance))]
#![cfg_attr(not(bootstrap), feature(strict_provenance_lints))]
#![feature(drain_keep_rest)]
#![feature(local_waker)]
#![feature(vec_pop_if)]
Expand Down
1 change: 0 additions & 1 deletion library/core/src/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2794,7 +2794,6 @@ where
/// #![feature(is_val_statically_known)]
/// #![feature(core_intrinsics)]
/// # #![allow(internal_features)]
/// #![feature(strict_provenance)]
/// use std::intrinsics::is_val_statically_known;
///
/// fn foo(x: &i32) -> bool {
Expand Down
3 changes: 2 additions & 1 deletion library/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,6 @@
#![feature(str_internals)]
#![feature(str_split_inclusive_remainder)]
#![feature(str_split_remainder)]
#![feature(strict_provenance)]
#![feature(ub_checks)]
#![feature(unchecked_neg)]
#![feature(unchecked_shifts)]
Expand All @@ -174,6 +173,8 @@
//
// Language features:
// tidy-alphabetical-start
#![cfg_attr(bootstrap, feature(strict_provenance))]
#![cfg_attr(not(bootstrap), feature(strict_provenance_lints))]
#![feature(abi_unadjusted)]
#![feature(adt_const_params)]
#![feature(allow_internal_unsafe)]
Expand Down
92 changes: 42 additions & 50 deletions library/core/src/ptr/const_ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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()))
}
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -560,7 +552,7 @@ impl<T: ?Sized> *const T {
/// ## Examples
///
/// ```
/// #![feature(ptr_mask, strict_provenance)]
/// #![feature(ptr_mask)]
/// let v = 17_u32;
/// let ptr: *const u32 = &v;
///
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down
Loading

0 comments on commit 20b1dad

Please sign in to comment.