zerocopy/pointer/inner.rs
1// Copyright 2024 The Fuchsia Authors
2//
3// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
4// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
5// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
6// This file may not be copied, modified, or distributed except according to
7// those terms.
8
9use core::{marker::PhantomData, ops::Range, ptr::NonNull};
10
11#[allow(unused_imports)]
12use crate::util::polyfills::NumExt as _;
13use crate::{
14 layout::{CastType, DstLayout, MetadataCastError},
15 util::AsAddress,
16 AlignmentError, CastError, KnownLayout, PointerMetadata, SizeError,
17};
18
19pub(crate) use _def::PtrInner;
20
21mod _def {
22 use super::*;
23 /// The inner pointer stored inside a [`Ptr`][crate::Ptr].
24 ///
25 /// `PtrInner<'a, T>` is [covariant] in `'a` and invariant in `T`.
26 ///
27 /// [covariant]: https://doc.rust-lang.org/reference/subtyping.html
28 pub(crate) struct PtrInner<'a, T>
29 where
30 T: ?Sized,
31 {
32 /// # Invariants
33 ///
34 /// 0. If `ptr`'s referent is not zero sized, then `ptr` has valid
35 /// provenance for its referent, which is entirely contained in some
36 /// Rust allocation, `A`.
37 /// 1. If `ptr`'s referent is not zero sized, `A` is guaranteed to live
38 /// for at least `'a`.
39 ///
40 /// # Postconditions
41 ///
42 /// By virtue of these invariants, code may assume the following, which
43 /// are logical implications of the invariants:
44 /// - `ptr`'s referent is not larger than `isize::MAX` bytes \[1\]
45 /// - `ptr`'s referent does not wrap around the address space \[1\]
46 ///
47 /// \[1\] Per <https://doc.rust-lang.org/1.85.0/std/ptr/index.html#allocated-object>:
48 ///
49 /// For any allocated object with `base` address, `size`, and a set of
50 /// `addresses`, the following are guaranteed:
51 /// ...
52 /// - `size <= isize::MAX`
53 ///
54 /// As a consequence of these guarantees, given any address `a` within
55 /// the set of addresses of an allocated object:
56 /// ...
57 /// - It is guaranteed that, given `o = a - base` (i.e., the offset of
58 /// `a` within the allocated object), `base + o` will not wrap around
59 /// the address space (in other words, will not overflow `usize`)
60 ptr: NonNull<T>,
61 // SAFETY: `&'a UnsafeCell<T>` is covariant in `'a` and invariant in `T`
62 // [1]. We use this construction rather than the equivalent `&mut T`,
63 // because our MSRV of 1.65 prohibits `&mut` types in const contexts.
64 //
65 // [1] https://doc.rust-lang.org/1.81.0/reference/subtyping.html#variance
66 _marker: PhantomData<&'a core::cell::UnsafeCell<T>>,
67 }
68
69 impl<'a, T: 'a + ?Sized> Copy for PtrInner<'a, T> {}
70 impl<'a, T: 'a + ?Sized> Clone for PtrInner<'a, T> {
71 fn clone(&self) -> PtrInner<'a, T> {
72 // SAFETY: None of the invariants on `ptr` are affected by having
73 // multiple copies of a `PtrInner`.
74 *self
75 }
76 }
77
78 impl<'a, T: 'a + ?Sized> PtrInner<'a, T> {
79 /// Constructs a `Ptr` from a [`NonNull`].
80 ///
81 /// # Safety
82 ///
83 /// The caller promises that:
84 ///
85 /// 0. If `ptr`'s referent is not zero sized, then `ptr` has valid
86 /// provenance for its referent, which is entirely contained in some
87 /// Rust allocation, `A`.
88 /// 1. If `ptr`'s referent is not zero sized, `A` is guaranteed to live
89 /// for at least `'a`.
90 pub(crate) const unsafe fn new(ptr: NonNull<T>) -> PtrInner<'a, T> {
91 // SAFETY: The caller has promised to satisfy all safety invariants
92 // of `PtrInner`.
93 Self { ptr, _marker: PhantomData }
94 }
95
96 /// Converts this `PtrInner<T>` to a [`NonNull<T>`].
97 ///
98 /// Note that this method does not consume `self`. The caller should
99 /// watch out for `unsafe` code which uses the returned `NonNull` in a
100 /// way that violates the safety invariants of `self`.
101 pub(crate) const fn as_non_null(&self) -> NonNull<T> {
102 self.ptr
103 }
104 }
105}
106
107impl<'a, T: ?Sized> PtrInner<'a, T> {
108 /// Constructs a `PtrInner` from a reference.
109 #[inline]
110 pub(crate) fn from_ref(ptr: &'a T) -> Self {
111 let ptr = NonNull::from(ptr);
112 // SAFETY:
113 // 0. If `ptr`'s referent is not zero sized, then `ptr`, by invariant on
114 // `&'a T` [1], has valid provenance for its referent, which is
115 // entirely contained in some Rust allocation, `A`.
116 // 1. If `ptr`'s referent is not zero sized, then `A`, by invariant on
117 // `&'a T`, is guaranteed to live for at least `'a`.
118 //
119 // [1] Per https://doc.rust-lang.org/1.85.0/std/primitive.reference.html#safety:
120 //
121 // For all types, `T: ?Sized`, and for all `t: &T` or `t: &mut T`,
122 // when such values cross an API boundary, the following invariants
123 // must generally be upheld:
124 // ...
125 // - if `size_of_val(t) > 0`, then `t` is dereferenceable for
126 // `size_of_val(t)` many bytes
127 //
128 // If `t` points at address `a`, being “dereferenceable” for N bytes
129 // means that the memory range `[a, a + N)` is all contained within a
130 // single allocated object.
131 unsafe { Self::new(ptr) }
132 }
133
134 /// Constructs a `PtrInner` from a mutable reference.
135 #[inline]
136 pub(crate) fn from_mut(ptr: &'a mut T) -> Self {
137 let ptr = NonNull::from(ptr);
138 // SAFETY:
139 // 0. If `ptr`'s referent is not zero sized, then `ptr`, by invariant on
140 // `&'a mut T` [1], has valid provenance for its referent, which is
141 // entirely contained in some Rust allocation, `A`.
142 // 1. If `ptr`'s referent is not zero sized, then `A`, by invariant on
143 // `&'a mut T`, is guaranteed to live for at least `'a`.
144 //
145 // [1] Per https://doc.rust-lang.org/1.85.0/std/primitive.reference.html#safety:
146 //
147 // For all types, `T: ?Sized`, and for all `t: &T` or `t: &mut T`,
148 // when such values cross an API boundary, the following invariants
149 // must generally be upheld:
150 // ...
151 // - if `size_of_val(t) > 0`, then `t` is dereferenceable for
152 // `size_of_val(t)` many bytes
153 //
154 // If `t` points at address `a`, being “dereferenceable” for N bytes
155 // means that the memory range `[a, a + N)` is all contained within a
156 // single allocated object.
157 unsafe { Self::new(ptr) }
158 }
159}
160
161#[allow(clippy::needless_lifetimes)]
162impl<'a, T> PtrInner<'a, [T]> {
163 /// Creates a pointer which addresses the given `range` of self.
164 ///
165 /// # Safety
166 ///
167 /// `range` is a valid range (`start <= end`) and `end <= self.len()`.
168 pub(crate) unsafe fn slice_unchecked(self, range: Range<usize>) -> Self {
169 let base = self.as_non_null().cast::<T>().as_ptr();
170
171 // SAFETY: The caller promises that `start <= end <= self.len()`. By
172 // invariant, if `self`'s referent is not zero-sized, then `self` refers
173 // to a byte range which is contained within a single allocation, which
174 // is no more than `isize::MAX` bytes long, and which does not wrap
175 // around the address space. Thus, this pointer arithmetic remains
176 // in-bounds of the same allocation, and does not wrap around the
177 // address space. The offset (in bytes) does not overflow `isize`.
178 //
179 // If `self`'s referent is zero-sized, then these conditions are
180 // trivially satisfied.
181 let base = unsafe { base.add(range.start) };
182
183 // SAFETY: The caller promises that `start <= end`, and so this will not
184 // underflow.
185 #[allow(unstable_name_collisions, clippy::incompatible_msrv)]
186 let len = unsafe { range.end.unchecked_sub(range.start) };
187
188 let ptr = core::ptr::slice_from_raw_parts_mut(base, len);
189
190 // SAFETY: By invariant, `self`'s referent is either a ZST or lives
191 // entirely in an allocation. `ptr` points inside of or one byte past
192 // the end of that referent. Thus, in either case, `ptr` is non-null.
193 let ptr = unsafe { NonNull::new_unchecked(ptr) };
194
195 // SAFETY:
196 //
197 // Lemma 0: `ptr` addresses a subset of the bytes addressed by `self`,
198 // and has the same provenance. Proof: The caller guarantees
199 // that `start <= end <= self.len()`. Thus, `base` is in-bounds
200 // of `self`, and `base + (end - start)` is also in-bounds of
201 // self. Finally, `ptr` is constructed using
202 // provenance-preserving operations.
203 //
204 // 0. Per Lemma 0 and by invariant on `self`, if `ptr`'s referent is not
205 // zero sized, then `ptr` has valid provenance for its referent,
206 // which is entirely contained in some Rust allocation, `A`.
207 // 1. Per Lemma 0 and by invariant on `self`, if `ptr`'s referent is not
208 // zero sized, then `A` is guaranteed to live for at least `'a`.
209 unsafe { PtrInner::new(ptr) }
210 }
211
212 /// Splits the slice in two.
213 ///
214 /// # Safety
215 ///
216 /// The caller promises that `l_len <= self.len()`.
217 ///
218 /// Given `let (left, right) = ptr.split_at(l_len)`, it is guaranteed
219 /// that `left` and `right` are contiguous and non-overlapping.
220 pub(crate) unsafe fn split_at(self, l_len: usize) -> (Self, Self) {
221 // SAFETY: The caller promises that `l_len <= self.len()`.
222 // Trivially, `0 <= l_len`.
223 let left = unsafe { self.slice_unchecked(0..l_len) };
224
225 // SAFETY: The caller promises that `l_len <= self.len() =
226 // slf.len()`. Trivially, `slf.len() <= slf.len()`.
227 let right = unsafe { self.slice_unchecked(l_len..self.len()) };
228
229 // SAFETY: `left` and `right` are non-overlapping. Proof: `left` is
230 // constructed from `slf` with `l_len` as its (exclusive) upper
231 // bound, while `right` is constructed from `slf` with `l_len` as
232 // its (inclusive) lower bound. Thus, no index is a member of both
233 // ranges.
234 (left, right)
235 }
236
237 /// Iteratively projects the elements `PtrInner<T>` from `PtrInner<[T]>`.
238 pub(crate) fn iter(&self) -> impl Iterator<Item = PtrInner<'a, T>> {
239 // TODO(#429): Once `NonNull::cast` documents that it preserves
240 // provenance, cite those docs.
241 let base = self.as_non_null().cast::<T>().as_ptr();
242 (0..self.len()).map(move |i| {
243 // TODO(https://github.com/rust-lang/rust/issues/74265): Use
244 // `NonNull::get_unchecked_mut`.
245
246 // SAFETY: If the following conditions are not satisfied
247 // `pointer::cast` may induce Undefined Behavior [1]:
248 //
249 // > - The computed offset, `count * size_of::<T>()` bytes, must not
250 // > overflow `isize``.
251 // > - If the computed offset is non-zero, then `self` must be
252 // > derived from a pointer to some allocated object, and the
253 // > entire memory range between `self` and the result must be in
254 // > bounds of that allocated object. In particular, this range
255 // > must not “wrap around” the edge of the address space.
256 //
257 // [1] https://doc.rust-lang.org/std/primitive.pointer.html#method.add
258 //
259 // We satisfy both of these conditions here:
260 // - By invariant on `Ptr`, `self` addresses a byte range whose
261 // length fits in an `isize`. Since `elem` is contained in `self`,
262 // the computed offset of `elem` must fit within `isize.`
263 // - If the computed offset is non-zero, then this means that the
264 // referent is not zero-sized. In this case, `base` points to an
265 // allocated object (by invariant on `self`). Thus:
266 // - By contract, `self.len()` accurately reflects the number of
267 // elements in the slice. `i` is in bounds of `c.len()` by
268 // construction, and so the result of this addition cannot
269 // overflow past the end of the allocation referred to by `c`.
270 // - By invariant on `Ptr`, `self` addresses a byte range which
271 // does not wrap around the address space. Since `elem` is
272 // contained in `self`, the computed offset of `elem` must wrap
273 // around the address space.
274 //
275 // TODO(#429): Once `pointer::add` documents that it preserves
276 // provenance, cite those docs.
277 let elem = unsafe { base.add(i) };
278
279 // SAFETY: `elem` must not be null. `base` is constructed from a
280 // `NonNull` pointer, and the addition that produces `elem` must not
281 // overflow or wrap around, so `elem >= base > 0`.
282 //
283 // TODO(#429): Once `NonNull::new_unchecked` documents that it
284 // preserves provenance, cite those docs.
285 let elem = unsafe { NonNull::new_unchecked(elem) };
286
287 // SAFETY: The safety invariants of `Ptr::new` (see definition) are
288 // satisfied:
289 // 0. If `elem`'s referent is not zero sized, then `elem` has valid
290 // provenance for its referent, because it derived from `self`
291 // using a series of provenance-preserving operations, and
292 // because `self` has valid provenance for its referent. By the
293 // same argument, `elem`'s referent is entirely contained within
294 // the same allocated object as `self`'s referent.
295 // 1. If `elem`'s referent is not zero sized, then the allocation of
296 // `elem` is guaranteed to live for at least `'a`, because `elem`
297 // is entirely contained in `self`, which lives for at least `'a`
298 // by invariant on `Ptr`.
299 unsafe { PtrInner::new(elem) }
300 })
301 }
302
303 /// The number of slice elements in the object referenced by `self`.
304 ///
305 /// # Safety
306 ///
307 /// Unsafe code my rely on `len` satisfying the above contract.
308 pub(crate) fn len(&self) -> usize {
309 self.trailing_slice_len()
310 }
311}
312
313#[allow(clippy::needless_lifetimes)]
314impl<'a, T> PtrInner<'a, T>
315where
316 T: ?Sized + KnownLayout<PointerMetadata = usize>,
317{
318 /// The number of trailing slice elements in the object referenced by
319 /// `self`.
320 ///
321 /// # Safety
322 ///
323 /// Unsafe code my rely on `trailing_slice_len` satisfying the above
324 /// contract.
325 pub(super) fn trailing_slice_len(&self) -> usize {
326 T::pointer_to_metadata(self.as_non_null().as_ptr())
327 }
328}
329
330impl<'a, T, const N: usize> PtrInner<'a, [T; N]> {
331 /// Casts this pointer-to-array into a slice.
332 ///
333 /// # Safety
334 ///
335 /// Callers may assume that the returned `PtrInner` references the same
336 /// address and length as `self`.
337 #[allow(clippy::wrong_self_convention)]
338 pub(crate) fn as_slice(self) -> PtrInner<'a, [T]> {
339 let start = self.as_non_null().cast::<T>().as_ptr();
340 let slice = core::ptr::slice_from_raw_parts_mut(start, N);
341 // SAFETY: `slice` is not null, because it is derived from `start`
342 // which is non-null.
343 let slice = unsafe { NonNull::new_unchecked(slice) };
344 // SAFETY: Lemma: In the following safety arguments, note that `slice`
345 // is derived from `self` in two steps: first, by casting `self: [T; N]`
346 // to `start: T`, then by constructing a pointer to a slice starting at
347 // `start` of length `N`. As a result, `slice` references exactly the
348 // same allocation as `self`, if any.
349 //
350 // 0. By the above lemma, if `slice`'s referent is not zero sized, then
351 // `slice` has the same referent as `self`. By invariant on `self`,
352 // this referent is entirely contained within some allocation, `A`.
353 // Because `slice` was constructed using provenance-preserving
354 // operations, it has provenance for its entire referent.
355 // 1. By the above lemma, if `slice`'s referent is not zero sized, then
356 // `A` is guaranteed to live for at least `'a`, because it is derived
357 // from the same allocation as `self`, which, by invariant on `Ptr`,
358 // lives for at least `'a`.
359 unsafe { PtrInner::new(slice) }
360 }
361}
362
363impl<'a> PtrInner<'a, [u8]> {
364 /// Attempts to cast `self` to a `U` using the given cast type.
365 ///
366 /// If `U` is a slice DST and pointer metadata (`meta`) is provided, then
367 /// the cast will only succeed if it would produce an object with the given
368 /// metadata.
369 ///
370 /// Returns `None` if the resulting `U` would be invalidly-aligned, if no
371 /// `U` can fit in `self`, or if the provided pointer metadata describes an
372 /// invalid instance of `U`. On success, returns a pointer to the
373 /// largest-possible `U` which fits in `self`.
374 ///
375 /// # Safety
376 ///
377 /// The caller may assume that this implementation is correct, and may rely
378 /// on that assumption for the soundness of their code. In particular, the
379 /// caller may assume that, if `try_cast_into` returns `Some((ptr,
380 /// remainder))`, then `ptr` and `remainder` refer to non-overlapping byte
381 /// ranges within `self`, and that `ptr` and `remainder` entirely cover
382 /// `self`. Finally:
383 /// - If this is a prefix cast, `ptr` has the same address as `self`.
384 /// - If this is a suffix cast, `remainder` has the same address as `self`.
385 #[inline]
386 pub(crate) fn try_cast_into<U>(
387 self,
388 cast_type: CastType,
389 meta: Option<U::PointerMetadata>,
390 ) -> Result<(PtrInner<'a, U>, PtrInner<'a, [u8]>), CastError<Self, U>>
391 where
392 U: 'a + ?Sized + KnownLayout,
393 {
394 let layout = match meta {
395 None => U::LAYOUT,
396 // This can return `None` if the metadata describes an object
397 // which can't fit in an `isize`.
398 Some(meta) => {
399 let size = match meta.size_for_metadata(U::LAYOUT) {
400 Some(size) => size,
401 None => return Err(CastError::Size(SizeError::new(self))),
402 };
403 DstLayout { align: U::LAYOUT.align, size_info: crate::SizeInfo::Sized { size } }
404 }
405 };
406 // PANICS: By invariant, the byte range addressed by
407 // `self.as_non_null()` does not wrap around the address space. This
408 // implies that the sum of the address (represented as a `usize`) and
409 // length do not overflow `usize`, as required by
410 // `validate_cast_and_convert_metadata`. Thus, this call to
411 // `validate_cast_and_convert_metadata` will only panic if `U` is a DST
412 // whose trailing slice element is zero-sized.
413 let maybe_metadata = layout.validate_cast_and_convert_metadata(
414 AsAddress::addr(self.as_non_null().as_ptr()),
415 self.len(),
416 cast_type,
417 );
418
419 let (elems, split_at) = match maybe_metadata {
420 Ok((elems, split_at)) => (elems, split_at),
421 Err(MetadataCastError::Alignment) => {
422 // SAFETY: Since `validate_cast_and_convert_metadata` returned
423 // an alignment error, `U` must have an alignment requirement
424 // greater than one.
425 let err = unsafe { AlignmentError::<_, U>::new_unchecked(self) };
426 return Err(CastError::Alignment(err));
427 }
428 Err(MetadataCastError::Size) => return Err(CastError::Size(SizeError::new(self))),
429 };
430
431 // SAFETY: `validate_cast_and_convert_metadata` promises to return
432 // `split_at <= self.len()`.
433 let (l_slice, r_slice) = unsafe { self.split_at(split_at) };
434
435 let (target, remainder) = match cast_type {
436 CastType::Prefix => (l_slice, r_slice),
437 CastType::Suffix => (r_slice, l_slice),
438 };
439
440 let base = target.as_non_null().cast::<u8>();
441
442 let elems = <U as KnownLayout>::PointerMetadata::from_elem_count(elems);
443 // For a slice DST type, if `meta` is `Some(elems)`, then we synthesize
444 // `layout` to describe a sized type whose size is equal to the size of
445 // the instance that we are asked to cast. For sized types,
446 // `validate_cast_and_convert_metadata` returns `elems == 0`. Thus, in
447 // this case, we need to use the `elems` passed by the caller, not the
448 // one returned by `validate_cast_and_convert_metadata`.
449 let elems = meta.unwrap_or(elems);
450
451 let ptr = U::raw_from_ptr_len(base, elems);
452
453 // SAFETY:
454 // 0. By invariant, if `target`'s referent is not zero sized, then
455 // `target` has provenance valid for some Rust allocation, `A`.
456 // Because `ptr` is derived from `target` via provenance-preserving
457 // operations, `ptr` will also have provenance valid for its entire
458 // referent.
459 // 1. `validate_cast_and_convert_metadata` promises that the object
460 // described by `elems` and `split_at` lives at a byte range which is
461 // a subset of the input byte range. Thus, by invariant, if
462 // `target`'s referent is not zero sized, then `target` refers to an
463 // allocation which is guaranteed to live for at least `'a`, and thus
464 // so does `ptr`.
465 Ok((unsafe { PtrInner::new(ptr) }, remainder))
466 }
467}
468
469#[cfg(test)]
470mod tests {
471 use super::*;
472
473 #[test]
474 fn test_split_at() {
475 const N: usize = 16;
476 let arr = [1; N];
477 let ptr = PtrInner::from_ref(&arr).as_slice();
478 for i in 0..=N {
479 assert_eq!(ptr.len(), N);
480 // SAFETY: `i` is in bounds by construction.
481 let (l, r) = unsafe { ptr.split_at(i) };
482 // SAFETY: Points to a valid value by construction.
483 #[allow(clippy::undocumented_unsafe_blocks)] // Clippy false positive
484 let l_sum: usize = l
485 .iter()
486 .map(|ptr| unsafe { core::ptr::read_unaligned(ptr.as_non_null().as_ptr()) })
487 .sum();
488 // SAFETY: Points to a valid value by construction.
489 #[allow(clippy::undocumented_unsafe_blocks)] // Clippy false positive
490 let r_sum: usize = r
491 .iter()
492 .map(|ptr| unsafe { core::ptr::read_unaligned(ptr.as_non_null().as_ptr()) })
493 .sum();
494 assert_eq!(l_sum, i);
495 assert_eq!(r_sum, N - i);
496 assert_eq!(l_sum + r_sum, N);
497 }
498 }
499}