Skip to content

Commit 31e1cde

Browse files
committed
clean up pointer docs
1 parent b608df8 commit 31e1cde

File tree

3 files changed

+84
-37
lines changed

3 files changed

+84
-37
lines changed

library/core/src/ptr/const_ptr.rs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -156,14 +156,16 @@ impl<T: ?Sized> *const T {
156156
/// *provenance* and *address-space* information. To properly restore that information,
157157
/// use [`with_addr`][pointer::with_addr] or [`map_addr`][pointer::map_addr].
158158
///
159-
/// On most platforms this information isn't represented at runtime, and so the loss
160-
/// of information is "only" semantic. On more complicated platforms like miri, CHERI,
161-
/// and segmented architectures, this may result in an actual change of representation
162-
/// and the loss of information.
159+
/// On most platforms this will produce a value with the same bytes as the original
160+
/// pointer, because all the bytes are dedicated to describing the address.
161+
/// Platforms which need to store additional information in the pointer may
162+
/// perform a change of representation to produce a value containing only the address
163+
/// portion of the pointer. What that means is up to the platform to define.
163164
///
164165
/// This API and its claimed semantics are part of the Strict Provenance experiment,
165166
/// see the [module documentation][crate::ptr] for details.
166167
#[must_use]
168+
#[inline]
167169
#[unstable(feature = "strict_provenance", issue = "95228")]
168170
pub fn addr(self) -> usize
169171
where
@@ -180,12 +182,13 @@ impl<T: ?Sized> *const T {
180182
/// This allows us to dynamically preserve and propagate this important
181183
/// information in a way that is otherwise impossible with a unary cast.
182184
///
183-
/// This is equivalent to using [`wrapping_offset`][pointer::wrapping_offset] to offset `self` to the
184-
/// given address, and therefore has all the same capabilities and restrictions.
185+
/// This is equivalent to using [`wrapping_offset`][pointer::wrapping_offset] to offset
186+
/// `self` to the given address, and therefore has all the same capabilities and restrictions.
185187
///
186188
/// This API and its claimed semantics are part of the Strict Provenance experiment,
187189
/// see the [module documentation][crate::ptr] for details.
188190
#[must_use]
191+
#[inline]
189192
#[unstable(feature = "strict_provenance", issue = "95228")]
190193
pub fn with_addr(self, addr: usize) -> Self
191194
where
@@ -211,6 +214,7 @@ impl<T: ?Sized> *const T {
211214
/// This API and its claimed semantics are part of the Strict Provenance experiment,
212215
/// see the [module documentation][crate::ptr] for details.
213216
#[must_use]
217+
#[inline]
214218
#[unstable(feature = "strict_provenance", issue = "95228")]
215219
pub fn map_addr(self, f: impl FnOnce(usize) -> usize) -> Self
216220
where

library/core/src/ptr/mod.rs

Lines changed: 64 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -66,22 +66,30 @@
6666
//!
6767
//! # Strict Provenance
6868
//!
69-
//! This section is *non-normative* and describes an experimental set of APIs that help tools
70-
//! that validate the memory-safety of your program's execution. Notably this includes [miri][]
69+
//! **The following text is non-normative, insufficiently formal, and is an extremely strict
70+
//! interpretation of provenance. It's ok if your code doesn't strictly conform to it.**
71+
//!
72+
//! [Strict Provenance][] is an experimental set of APIs that help tools that try
73+
//! to validate the memory-safety of your program's execution. Notably this includes [miri][]
7174
//! and [CHERI][], which can detect when you access out of bounds memory or otherwise violate
7275
//! Rust's memory model.
7376
//!
74-
//! **The following text is overly strict and insufficiently formal, and is an extremely
75-
//! strict interpretation of provenance.** Provenance must exist in some form for any language
76-
//! higher-level than an assembler, but to our knowledge no one has ever been able to square
77-
//! the notion of provenance with all the operations that programmers believe should be permitted.
77+
//! Provenance must exist in some form for any programming
78+
//! language compiled for modern computer architectures, but specifying a model for provenance
79+
//! in a way that is useful to both compilers and programmers is an ongoing challenge.
7880
//! The [Strict Provenance][] experiment seeks to explore the question: *what if we just said you
7981
//! couldn't do all the nasty operations that make provenance so messy?*
8082
//!
8183
//! What APIs would have to be removed? What APIs would have to be added? How much would code
8284
//! have to change, and is it worse or better now? Would any patterns become truly inexpressible?
83-
//! Could we carve out special exceptions for those patterns?
85+
//! Could we carve out special exceptions for those patterns? Should we?
8486
//!
87+
//! A secondary goal of this project is to see if we can disamiguate the many functions of
88+
//! pointer<->integer casts enough for the definition of `usize` to be loosened so that it
89+
//! isn't *pointer*-sized but address-space/offset/allocation-sized (we'll probably continue
90+
//! to conflate these notions). This would potentially make it possible to more efficiently
91+
//! target platforms where pointers are larger than offsets, such as CHERI and maybe some
92+
//! segmented architecures.
8593
//!
8694
//! ## Provenance
8795
//!
@@ -102,26 +110,37 @@
102110
//! The Original Pointer for an allocation is guaranteed to have unique access to the entire
103111
//! allocation and *only* that allocation. In this sense, an allocation can be thought of
104112
//! as a "sandbox" that cannot be broken into or out of. *Provenance* is the permission
105-
//! to access an allocation's sandbox and consists of:
113+
//! to access an allocation's sandbox and has both a *spatial* and *temporal* component:
114+
//!
115+
//! * Spatial: A range of bytes in the allocation that the pointer is allowed to access.
116+
//! * Temporal: Some kind of globally unique identifier tied to the allocation itself.
106117
//!
107-
//! * Some kind of globally unique identifier tied to the allocation itself.
108-
//! * A range of bytes in the allocation that the pointer is allowed to access.
118+
//! Spatial provenance makes sure you don't go beyond your sandbox, while temporal provenance
119+
//! makes sure that you can't "get lucky" after your permission to access some memory
120+
//! has been revoked (either through deallocations or borrows expiring).
109121
//!
110122
//! Provenance is implicitly shared with all pointers transitively derived from
111123
//! The Original Pointer through operations like [`offset`], borrowing, and pointer casts.
112-
//! Some operations may produce a pointer whose provenance has a smaller range
113-
//! than the one it's derived from (i.e. borrowing a subfield and subslicing).
124+
//! Some operations may *shrink* the derived provenance, limiting how much memory it can
125+
//! access or how long it's valid for (i.e. borrowing a subfield and subslicing).
114126
//!
115127
//! Shrinking provenance cannot be undone: even if you "know" there is a larger allocation, you
116128
//! can't derive a pointer with a larger provenance. Similarly, you cannot "recombine"
117129
//! two contiguous provenances back into one (i.e. with a `fn merge(&[T], &[T]) -> &[T]`).
118130
//!
119131
//! A reference to a value always has provenance over exactly the memory that field occupies.
120-
//! A reference slice always has provenance over exactly the range that slice describes.
132+
//! A reference to a slice always has provenance over exactly the range that slice describes.
121133
//!
122134
//! If an allocation is deallocated, all pointers with provenance to that allocation become
123135
//! invalidated, and effectively lose their provenance.
124136
//!
137+
//! The strict provenance experiment is mostly only interested in exploring stricter *spatial*
138+
//! provenance. In this sense it can be thought of as a subset of the more ambitious and
139+
//! formal [Stacked Borrows][] research project, which is what tools like [miri][] are based on.
140+
//! In particular, Stacked Borrows is necessary to properly describe what borrows are allowed
141+
//! to do and when they become invalidated. This necessarily involves much more complex
142+
//! *temporal* reasoning than simply identifying allocations.
143+
//!
125144
//!
126145
//! ## Pointer Vs Addresses
127146
//!
@@ -241,20 +260,33 @@
241260
//! be used for sentinel values like `null` *or* to represent a tagged pointer that will
242261
//! never be dereferencable.
243262
//!
244-
//! * [`wrapping_offset`][] a pointer outside its provenance. This includes invalid pointers
245-
//! which have no provenance. Unfortunately there may be practical limits on this for a
246-
//! particular platform ([CHERI][] may mark your pointers as invalid, but it has a pretty
247-
//! generour buffer that is *at least* 1KB on each side of the provenance). Note that
248-
//! least-significant-bit tagging is generally pretty robust, and often doesn't even
249-
//! go out of bounds because types have a size >= align.
250-
//!
251263
//! * Forge an allocation of size zero at any sufficiently aligned non-null address.
252264
//! i.e. the usual "ZSTs are fake, do what you want" rules apply *but* this only applies
253265
//! for actual forgery (integers cast to pointers). If you borrow some structs subfield
254266
//! that *happens* to be zero-sized, the resulting pointer will have provenance tied to
255267
//! that allocation and it will still get invalidated if the allocation gets deallocated.
256268
//! In the future we may introduce an API to make such a forged allocation explicit.
257269
//!
270+
//! * [`wrapping_offset`][] a pointer outside its provenance. This includes invalid pointers
271+
//! which have "no" provenance. Unfortunately there may be practical limits on this for a
272+
//! particular platform, and it's an open question as to how to specify this (if at all).
273+
//! Notably, [CHERI][] relies on a compression scheme that can't handle a
274+
//! pointer getting offset "too far" out of bounds. If this happens, the address
275+
//! returned by `addr` will be the value you expect, but the provenance will get invalidated
276+
//! and using it to read/write will fault. The details of this are architecture-specific
277+
//! and based on alignment, but the buffer on either side of the pointer's range is pretty
278+
//! generous (think kilobytes, not bytes).
279+
//!
280+
//! * Perform pointer tagging tricks. This falls out of [`wrapping_offset`] but is worth
281+
//! mentioning in more detail because of the limitations of [CHERI][]. Low-bit tagging
282+
//! is very robust, and often doesn't even go out of bounds because types have a
283+
//! size >= align (and over-aligning actually gives CHERI more flexibility). Anything
284+
//! more complex than this rapidly enters "extremely platform-specific" territory as
285+
//! certain things may or may not be allowed based on specific supported operations.
286+
//! For instance, ARM explicitly supports high-bit tagging, and so CHERI on ARM inherits
287+
//! that and should support it.
288+
//!
289+
//!
258290
//! [aliasing]: ../../nomicon/aliasing.html
259291
//! [book]: ../../book/ch19-01-unsafe-rust.html#dereferencing-a-raw-pointer
260292
//! [ub]: ../../reference/behavior-considered-undefined.html
@@ -269,6 +301,7 @@
269301
//! [miri]: https://github.com/rust-lang/miri
270302
//! [CHERI]: https://www.cl.cam.ac.uk/research/security/ctsrd/cheri/
271303
//! [Strict Provenance]: https://github.com/rust-lang/rust/issues/95228
304+
//! [Stacked Borrows]: https://plv.mpi-sws.org/rustbelt/stacked-borrows/
272305
273306
#![stable(feature = "rust1", since = "1.0.0")]
274307

@@ -410,7 +443,7 @@ pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
410443
#[rustc_const_stable(feature = "const_ptr_null", since = "1.24.0")]
411444
#[rustc_diagnostic_item = "ptr_null"]
412445
pub const fn null<T>() -> *const T {
413-
invalid::<T>(0)
446+
invalid(0)
414447
}
415448

416449
/// Creates a null mutable raw pointer.
@@ -430,22 +463,25 @@ pub const fn null<T>() -> *const T {
430463
#[rustc_const_stable(feature = "const_ptr_null", since = "1.24.0")]
431464
#[rustc_diagnostic_item = "ptr_null_mut"]
432465
pub const fn null_mut<T>() -> *mut T {
433-
invalid_mut::<T>(0)
466+
invalid_mut(0)
434467
}
435468

436469
/// Creates an invalid pointer with the given address.
437470
///
438471
/// This is *currently* equivalent to `addr as *const T` but it expresses the intended semantic
439472
/// more clearly, and may become important under future memory models.
440473
///
441-
/// The module's to-level documentation discusses the precise meaning of an "invalid"
474+
/// The module's top-level documentation discusses the precise meaning of an "invalid"
442475
/// pointer but essentially this expresses that the pointer is not associated
443476
/// with any actual allocation and is little more than a usize address in disguise.
444477
///
445478
/// This pointer will have no provenance associated with it and is therefore
446479
/// UB to read/write/offset. This mostly exists to facilitate things
447480
/// like ptr::null and NonNull::dangling which make invalid pointers.
448481
///
482+
/// (Standard "Zero-Sized-Types get to cheat and lie" caveats apply, although it
483+
/// may be desirable to give them their own API just to make that 100% clear.)
484+
///
449485
/// This API and its claimed semantics are part of the Strict Provenance experiment,
450486
/// see the [module documentation][crate::ptr] for details.
451487
#[inline(always)]
@@ -459,17 +495,20 @@ pub const fn invalid<T>(addr: usize) -> *const T {
459495

460496
/// Creates an invalid mutable pointer with the given address.
461497
///
462-
/// This is *currently* equivalent to `addr as *const T` but it expresses the intended semantic
498+
/// This is *currently* equivalent to `addr as *mut T` but it expresses the intended semantic
463499
/// more clearly, and may become important under future memory models.
464500
///
465-
/// The module's to-level documentation discusses the precise meaning of an "invalid"
501+
/// The module's top-level documentation discusses the precise meaning of an "invalid"
466502
/// pointer but essentially this expresses that the pointer is not associated
467503
/// with any actual allocation and is little more than a usize address in disguise.
468504
///
469505
/// This pointer will have no provenance associated with it and is therefore
470506
/// UB to read/write/offset. This mostly exists to facilitate things
471507
/// like ptr::null and NonNull::dangling which make invalid pointers.
472508
///
509+
/// (Standard "Zero-Sized-Types get to cheat and lie" caveats apply, although it
510+
/// may be desirable to give them their own API just to make that 100% clear.)
511+
///
473512
/// This API and its claimed semantics are part of the Strict Provenance experiment,
474513
/// see the [module documentation][crate::ptr] for details.
475514
#[inline(always)]

library/core/src/ptr/mut_ptr.rs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -160,14 +160,16 @@ impl<T: ?Sized> *mut T {
160160
/// *provenance* and *address-space* information. To properly restore that information,
161161
/// use [`with_addr`][pointer::with_addr] or [`map_addr`][pointer::map_addr].
162162
///
163-
/// On most platforms this information isn't represented at runtime, and so the loss
164-
/// of information is "only" semantic. On more complicated platforms like miri, CHERI,
165-
/// and segmented architectures, this may result in an actual change of representation
166-
/// and the loss of information.
163+
/// On most platforms this will produce a value with the same bytes as the original
164+
/// pointer, because all the bytes are dedicated to describing the address.
165+
/// Platforms which need to store additional information in the pointer may
166+
/// perform a change of representation to produce a value containing only the address
167+
/// portion of the pointer. What that means is up to the platform to define.
167168
///
168169
/// This API and its claimed semantics are part of the Strict Provenance experiment,
169170
/// see the [module documentation][crate::ptr] for details.
170171
#[must_use]
172+
#[inline]
171173
#[unstable(feature = "strict_provenance", issue = "95228")]
172174
pub fn addr(self) -> usize
173175
where
@@ -184,12 +186,13 @@ impl<T: ?Sized> *mut T {
184186
/// This allows us to dynamically preserve and propagate this important
185187
/// information in a way that is otherwise impossible with a unary cast.
186188
///
187-
/// This is equivalent to using [`wrapping_offset`][pointer::wrapping_offset] to offset `self` to the
188-
/// given address, and therefore has all the same capabilities and restrictions.
189+
/// This is equivalent to using [`wrapping_offset`][pointer::wrapping_offset] to offset
190+
/// `self` to the given address, and therefore has all the same capabilities and restrictions.
189191
///
190192
/// This API and its claimed semantics are part of the Strict Provenance experiment,
191193
/// see the [module documentation][crate::ptr] for details.
192194
#[must_use]
195+
#[inline]
193196
#[unstable(feature = "strict_provenance", issue = "95228")]
194197
pub fn with_addr(self, addr: usize) -> Self
195198
where
@@ -215,6 +218,7 @@ impl<T: ?Sized> *mut T {
215218
/// This API and its claimed semantics are part of the Strict Provenance experiment,
216219
/// see the [module documentation][crate::ptr] for details.
217220
#[must_use]
221+
#[inline]
218222
#[unstable(feature = "strict_provenance", issue = "95228")]
219223
pub fn map_addr(self, f: impl FnOnce(usize) -> usize) -> Self
220224
where

0 commit comments

Comments
 (0)