66
66
//!
67
67
//! # Strict Provenance
68
68
//!
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][]
71
74
//! and [CHERI][], which can detect when you access out of bounds memory or otherwise violate
72
75
//! Rust's memory model.
73
76
//!
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.
78
80
//! The [Strict Provenance][] experiment seeks to explore the question: *what if we just said you
79
81
//! couldn't do all the nasty operations that make provenance so messy?*
80
82
//!
81
83
//! What APIs would have to be removed? What APIs would have to be added? How much would code
82
84
//! 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?
84
86
//!
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.
85
93
//!
86
94
//! ## Provenance
87
95
//!
102
110
//! The Original Pointer for an allocation is guaranteed to have unique access to the entire
103
111
//! allocation and *only* that allocation. In this sense, an allocation can be thought of
104
112
//! 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.
106
117
//!
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).
109
121
//!
110
122
//! Provenance is implicitly shared with all pointers transitively derived from
111
123
//! 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).
114
126
//!
115
127
//! Shrinking provenance cannot be undone: even if you "know" there is a larger allocation, you
116
128
//! can't derive a pointer with a larger provenance. Similarly, you cannot "recombine"
117
129
//! two contiguous provenances back into one (i.e. with a `fn merge(&[T], &[T]) -> &[T]`).
118
130
//!
119
131
//! 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.
121
133
//!
122
134
//! If an allocation is deallocated, all pointers with provenance to that allocation become
123
135
//! invalidated, and effectively lose their provenance.
124
136
//!
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
+ //!
125
144
//!
126
145
//! ## Pointer Vs Addresses
127
146
//!
241
260
//! be used for sentinel values like `null` *or* to represent a tagged pointer that will
242
261
//! never be dereferencable.
243
262
//!
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
- //!
251
263
//! * Forge an allocation of size zero at any sufficiently aligned non-null address.
252
264
//! i.e. the usual "ZSTs are fake, do what you want" rules apply *but* this only applies
253
265
//! for actual forgery (integers cast to pointers). If you borrow some structs subfield
254
266
//! that *happens* to be zero-sized, the resulting pointer will have provenance tied to
255
267
//! that allocation and it will still get invalidated if the allocation gets deallocated.
256
268
//! In the future we may introduce an API to make such a forged allocation explicit.
257
269
//!
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
+ //!
258
290
//! [aliasing]: ../../nomicon/aliasing.html
259
291
//! [book]: ../../book/ch19-01-unsafe-rust.html#dereferencing-a-raw-pointer
260
292
//! [ub]: ../../reference/behavior-considered-undefined.html
269
301
//! [miri]: https://github.com/rust-lang/miri
270
302
//! [CHERI]: https://www.cl.cam.ac.uk/research/security/ctsrd/cheri/
271
303
//! [Strict Provenance]: https://github.com/rust-lang/rust/issues/95228
304
+ //! [Stacked Borrows]: https://plv.mpi-sws.org/rustbelt/stacked-borrows/
272
305
273
306
#![ stable( feature = "rust1" , since = "1.0.0" ) ]
274
307
@@ -410,7 +443,7 @@ pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
410
443
#[ rustc_const_stable( feature = "const_ptr_null" , since = "1.24.0" ) ]
411
444
#[ rustc_diagnostic_item = "ptr_null" ]
412
445
pub const fn null < T > ( ) -> * const T {
413
- invalid :: < T > ( 0 )
446
+ invalid ( 0 )
414
447
}
415
448
416
449
/// Creates a null mutable raw pointer.
@@ -430,22 +463,25 @@ pub const fn null<T>() -> *const T {
430
463
#[ rustc_const_stable( feature = "const_ptr_null" , since = "1.24.0" ) ]
431
464
#[ rustc_diagnostic_item = "ptr_null_mut" ]
432
465
pub const fn null_mut < T > ( ) -> * mut T {
433
- invalid_mut :: < T > ( 0 )
466
+ invalid_mut ( 0 )
434
467
}
435
468
436
469
/// Creates an invalid pointer with the given address.
437
470
///
438
471
/// This is *currently* equivalent to `addr as *const T` but it expresses the intended semantic
439
472
/// more clearly, and may become important under future memory models.
440
473
///
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"
442
475
/// pointer but essentially this expresses that the pointer is not associated
443
476
/// with any actual allocation and is little more than a usize address in disguise.
444
477
///
445
478
/// This pointer will have no provenance associated with it and is therefore
446
479
/// UB to read/write/offset. This mostly exists to facilitate things
447
480
/// like ptr::null and NonNull::dangling which make invalid pointers.
448
481
///
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
+ ///
449
485
/// This API and its claimed semantics are part of the Strict Provenance experiment,
450
486
/// see the [module documentation][crate::ptr] for details.
451
487
#[ inline( always) ]
@@ -459,17 +495,20 @@ pub const fn invalid<T>(addr: usize) -> *const T {
459
495
460
496
/// Creates an invalid mutable pointer with the given address.
461
497
///
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
463
499
/// more clearly, and may become important under future memory models.
464
500
///
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"
466
502
/// pointer but essentially this expresses that the pointer is not associated
467
503
/// with any actual allocation and is little more than a usize address in disguise.
468
504
///
469
505
/// This pointer will have no provenance associated with it and is therefore
470
506
/// UB to read/write/offset. This mostly exists to facilitate things
471
507
/// like ptr::null and NonNull::dangling which make invalid pointers.
472
508
///
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
+ ///
473
512
/// This API and its claimed semantics are part of the Strict Provenance experiment,
474
513
/// see the [module documentation][crate::ptr] for details.
475
514
#[ inline( always) ]
0 commit comments