|
103 | 103 | //! they must have provenance.
|
104 | 104 | //!
|
105 | 105 | //! When an allocation is created, that allocation has a unique Original Pointer. For alloc
|
106 |
| -//! APIs this is literally the pointer the call returns, and for variables declarations this |
107 |
| -//! is the name of the variable. This is mildly overloading the term "pointer" for the sake |
108 |
| -//! of brevity/exposition. |
| 106 | +//! APIs this is literally the pointer the call returns, and for local variables and statics, |
| 107 | +//! this is the name of the variable/static. This is mildly overloading the term "pointer" |
| 108 | +//! for the sake of brevity/exposition. |
109 | 109 | //!
|
110 | 110 | //! The Original Pointer for an allocation is guaranteed to have unique access to the entire
|
111 | 111 | //! allocation and *only* that allocation. In this sense, an allocation can be thought of
|
112 | 112 | //! as a "sandbox" that cannot be broken into or out of. *Provenance* is the permission
|
113 | 113 | //! to access an allocation's sandbox and has both a *spatial* and *temporal* component:
|
114 | 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. |
| 115 | +//! * Spatial: A range of bytes that the pointer is allowed to access. |
| 116 | +//! * Temporal: The allocation that access to these bytes is tied to. |
117 | 117 | //!
|
118 | 118 | //! Spatial provenance makes sure you don't go beyond your sandbox, while temporal provenance
|
119 | 119 | //! makes sure that you can't "get lucky" after your permission to access some memory
|
|
139 | 139 | //! formal [Stacked Borrows][] research project, which is what tools like [miri][] are based on.
|
140 | 140 | //! In particular, Stacked Borrows is necessary to properly describe what borrows are allowed
|
141 | 141 | //! to do and when they become invalidated. This necessarily involves much more complex
|
142 |
| -//! *temporal* reasoning than simply identifying allocations. |
| 142 | +//! *temporal* reasoning than simply identifying allocations. Adjusting APIs and code |
| 143 | +//! for the strict provenance experiment will also greatly help Stacked Borrows. |
143 | 144 | //!
|
144 | 145 | //!
|
145 | 146 | //! ## Pointer Vs Addresses
|
|
152 | 153 | //! to very complex and unreliable heuristics. But of course, converting between pointers and
|
153 | 154 | //! integers is very useful, so what can we do?
|
154 | 155 | //!
|
155 |
| -//! Strict Provenance attempts to square this circle by decoupling Rust's traditional conflation |
| 156 | +//! Also did you know WASM is actually a "Harvard Architecture"? As in function pointers are |
| 157 | +//! handled completely differently from data pointers? And we kind of just shipped Rust on WASM |
| 158 | +//! without really addressing the fact that we let you freely convert between function pointers |
| 159 | +//! and data pointers, because it mostly Just Works? Let's just put that on the "pointer casts |
| 160 | +//! are dubious" pile. |
| 161 | +//! |
| 162 | +//! Strict Provenance attempts to square these circles by decoupling Rust's traditional conflation |
156 | 163 | //! of pointers and `usize` (and `isize`), and defining a pointer to semantically contain the
|
157 | 164 | //! following information:
|
158 | 165 | //!
|
|
246 | 253 | //! Situations where a valid pointer *must* be created from just an address, such as baremetal code
|
247 | 254 | //! accessing a memory-mapped interface at a fixed address, are an open question on how to support.
|
248 | 255 | //! These situations *will* still be allowed, but we might require some kind of "I know what I'm
|
249 |
| -//! doing" annotation to explain the situation to the compiler. Because those situations require |
250 |
| -//! `volatile` accesses anyway, it should be possible to carve out exceptions for them. |
| 256 | +//! doing" annotation to explain the situation to the compiler. It's also possible they need no |
| 257 | +//! special attention at all, because they're generally accessing memory outside the scope of |
| 258 | +//! "the abstract machine", or already using "I know what I'm doing" annotations like "volatile". |
251 | 259 | //!
|
252 | 260 | //! Under [Strict Provenance] is is Undefined Behaviour to:
|
253 | 261 | //!
|
254 | 262 | //! * Access memory through a pointer that does not have provenance over that memory.
|
255 | 263 | //!
|
256 |
| -//! * [`offset`] a pointer to an address it doesn't have provenance over. |
| 264 | +//! * [`offset`] a pointer to or from an address it doesn't have provenance over. |
257 | 265 | //! This means it's always UB to offset a pointer derived from something deallocated,
|
258 | 266 | //! even if the offset is 0. Note that a pointer "one past the end" of its provenance
|
259 | 267 | //! is not actually outside its provenance, it just has 0 bytes it can load/store.
|
|
295 | 303 | //!
|
296 | 304 | //! * Perform pointer tagging tricks. This falls out of [`wrapping_offset`] but is worth
|
297 | 305 | //! mentioning in more detail because of the limitations of [CHERI][]. Low-bit tagging
|
298 |
| -//! is very robust, and often doesn't even go out of bounds because types have a |
| 306 | +//! is very robust, and often doesn't even go out of bounds because types ensure |
299 | 307 | //! size >= align (and over-aligning actually gives CHERI more flexibility). Anything
|
300 | 308 | //! more complex than this rapidly enters "extremely platform-specific" territory as
|
301 | 309 | //! certain things may or may not be allowed based on specific supported operations.
|
|
0 commit comments