Skip to content

Commit 5167b68

Browse files
committed
Introduce experimental APIs for conforming to "strict provenance".
This patch series examines the question: how bad would it be if we adopted an extremely strict pointer provenance model that completely banished all int<->ptr casts. The key insight to making this approach even *vaguely* pallatable is the ptr.with_addr(addr) -> ptr function, which takes a pointer and an address and creates a new pointer with that address and the provenance of the input pointer. In this way the "chain of custody" is completely and dynamically restored, making the model suitable even for dynamic checkers like CHERI and Miri. This is not a formal model, but lots of the docs discussing the model have been updated to try to the *concept* of this design in the hopes that it can be iterated on.
1 parent 9c06e1b commit 5167b68

File tree

3 files changed

+400
-10
lines changed

3 files changed

+400
-10
lines changed

library/core/src/ptr/const_ptr.rs

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,75 @@ impl<T: ?Sized> *const T {
150150
bits as Self
151151
}
152152

153+
/// Gets the "address" portion of the pointer.
154+
///
155+
/// This is equivalent to `self as usize`, which semantically discards
156+
/// *provenance* and *address-space* information. To properly restore that information,
157+
/// use [`with_addr`][pointer::with_addr] or [`map_addr`][pointer::map_addr].
158+
///
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.
163+
///
164+
/// This API and its claimed semantics are part of the Strict Provenance experiment,
165+
/// see the [module documentation][crate::ptr] for details.
166+
#[must_use]
167+
#[unstable(feature = "strict_provenance", issue = "95228")]
168+
pub fn addr(self) -> usize
169+
where
170+
T: Sized,
171+
{
172+
// FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
173+
self as usize
174+
}
175+
176+
/// Creates a new pointer with the given address.
177+
///
178+
/// This performs the same operation as an `addr as ptr` cast, but copies
179+
/// the *address-space* and *provenance* of `self` to the new pointer.
180+
/// This allows us to dynamically preserve and propagate this important
181+
/// information in a way that is otherwise impossible with a unary cast.
182+
///
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+
///
186+
/// This API and its claimed semantics are part of the Strict Provenance experiment,
187+
/// see the [module documentation][crate::ptr] for details.
188+
#[must_use]
189+
#[unstable(feature = "strict_provenance", issue = "95228")]
190+
pub fn with_addr(self, addr: usize) -> Self
191+
where
192+
T: Sized,
193+
{
194+
// FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
195+
//
196+
// In the mean-time, this operation is defined to be "as if" it was
197+
// a wrapping_offset, so we can emulate it as such. This should properly
198+
// restore pointer provenance even under today's compiler.
199+
let self_addr = self.addr() as isize;
200+
let dest_addr = addr as isize;
201+
let offset = dest_addr.wrapping_sub(self_addr);
202+
203+
// This is the canonical desugarring of this operation
204+
self.cast::<u8>().wrapping_offset(offset).cast::<T>()
205+
}
206+
207+
/// Creates a new pointer by mapping `self`'s address to a new one.
208+
///
209+
/// This is a convenience for [`with_addr`][pointer::with_addr], see that method for details.
210+
///
211+
/// This API and its claimed semantics are part of the Strict Provenance experiment,
212+
/// see the [module documentation][crate::ptr] for details.
213+
#[must_use]
214+
#[unstable(feature = "strict_provenance", issue = "95228")]
215+
pub fn map_addr(self, f: impl FnOnce(usize) -> usize) -> Self
216+
where
217+
T: Sized,
218+
{
219+
self.with_addr(f(self.addr()))
220+
}
221+
153222
/// Decompose a (possibly wide) pointer into its address and metadata components.
154223
///
155224
/// The pointer can be later reconstructed with [`from_raw_parts`].
@@ -1006,7 +1075,7 @@ impl<T> *const [T] {
10061075
/// use std::ptr;
10071076
///
10081077
/// let slice: *const [i8] = ptr::slice_from_raw_parts(ptr::null(), 3);
1009-
/// assert_eq!(slice.as_ptr(), 0 as *const i8);
1078+
/// assert_eq!(slice.as_ptr(), ptr::null());
10101079
/// ```
10111080
#[inline]
10121081
#[unstable(feature = "slice_ptr_get", issue = "74265")]

0 commit comments

Comments
 (0)