mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-06-21 15:43:21 +02:00
499dc02cd5
Originally, when the Rust upstream `alloc` standard library crate was vendored, the SPDX License Identifiers were added to every file so that the license on those was clear. The same happened with the vendoring of `proc_macro2`, `quote` and `syn`. Please see: commit057b8d2571("rust: adapt `alloc` crate to the kernel") commit69942c0a89("rust: syn: add SPDX License Identifiers") commitddfa1b279d("rust: quote: add SPDX License Identifiers") commita9acfceb96("rust: proc-macro2: add SPDX License Identifiers") Thus do the same for the `zerocopy` crate. This makes `scripts/spdxcheck.py` pass: use parentheses like commit06e9bfc1e5("ionic: make spdxcheck.py happy") did since we have two `OR` operators in the expression (three licenses). SPDX identifiers are not added to the `benches` files because they are included in rendered documentation. Nevertheless, the `README.md` to be added by a later commit mentions the license. Finally, as requested, I filed an issue [1] with upstream about it. Cc: Joshua Liebow-Feeser <joshlf@google.com> Cc: Jack Wrenn <jswrenn@google.com> Link: https://github.com/google/zerocopy/issues/3428 [1] Link: https://patch.msgid.link/20260608141439.182634-10-ojeda@kernel.org Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
755 lines
33 KiB
Rust
755 lines
33 KiB
Rust
// SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT
|
|
|
|
// Copyright 2024 The Fuchsia Authors
|
|
//
|
|
// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
|
|
// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
|
|
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
|
|
// This file may not be copied, modified, or distributed except according to
|
|
// those terms.
|
|
|
|
use core::{marker::PhantomData, ops::Range, ptr::NonNull};
|
|
|
|
pub use _def::PtrInner;
|
|
|
|
#[allow(unused_imports)]
|
|
use crate::util::polyfills::NumExt as _;
|
|
use crate::{
|
|
layout::{CastType, MetadataCastError},
|
|
pointer::cast,
|
|
util::AsAddress,
|
|
AlignmentError, CastError, KnownLayout, MetadataOf, SizeError, SplitAt,
|
|
};
|
|
|
|
mod _def {
|
|
use super::*;
|
|
/// The inner pointer stored inside a [`Ptr`][crate::Ptr].
|
|
///
|
|
/// `PtrInner<'a, T>` is [covariant] in `'a` and invariant in `T`.
|
|
///
|
|
/// [covariant]: https://doc.rust-lang.org/reference/subtyping.html
|
|
#[allow(missing_debug_implementations)]
|
|
pub struct PtrInner<'a, T>
|
|
where
|
|
T: ?Sized,
|
|
{
|
|
/// # Invariants
|
|
///
|
|
/// 0. If `ptr`'s referent is not zero sized, then `ptr` has valid
|
|
/// provenance for its referent, which is entirely contained in some
|
|
/// Rust allocation, `A`.
|
|
/// 1. If `ptr`'s referent is not zero sized, `A` is guaranteed to live
|
|
/// for at least `'a`.
|
|
///
|
|
/// # Postconditions
|
|
///
|
|
/// By virtue of these invariants, code may assume the following, which
|
|
/// are logical implications of the invariants:
|
|
/// - `ptr`'s referent is not larger than `isize::MAX` bytes \[1\]
|
|
/// - `ptr`'s referent does not wrap around the address space \[1\]
|
|
///
|
|
/// \[1\] Per <https://doc.rust-lang.org/1.85.0/std/ptr/index.html#allocated-object>:
|
|
///
|
|
/// For any allocated object with `base` address, `size`, and a set of
|
|
/// `addresses`, the following are guaranteed:
|
|
/// ...
|
|
/// - `size <= isize::MAX`
|
|
///
|
|
/// As a consequence of these guarantees, given any address `a` within
|
|
/// the set of addresses of an allocated object:
|
|
/// ...
|
|
/// - It is guaranteed that, given `o = a - base` (i.e., the offset of
|
|
/// `a` within the allocated object), `base + o` will not wrap
|
|
/// around the address space (in other words, will not overflow
|
|
/// `usize`)
|
|
ptr: NonNull<T>,
|
|
// SAFETY: `&'a UnsafeCell<T>` is covariant in `'a` and invariant in `T`
|
|
// [1]. We use this construction rather than the equivalent `&mut T`,
|
|
// because our MSRV of 1.65 prohibits `&mut` types in const contexts.
|
|
//
|
|
// [1] https://doc.rust-lang.org/1.81.0/reference/subtyping.html#variance
|
|
_marker: PhantomData<&'a core::cell::UnsafeCell<T>>,
|
|
}
|
|
|
|
impl<'a, T: 'a + ?Sized> Copy for PtrInner<'a, T> {}
|
|
impl<'a, T: 'a + ?Sized> Clone for PtrInner<'a, T> {
|
|
#[inline(always)]
|
|
fn clone(&self) -> PtrInner<'a, T> {
|
|
// SAFETY: None of the invariants on `ptr` are affected by having
|
|
// multiple copies of a `PtrInner`.
|
|
*self
|
|
}
|
|
}
|
|
|
|
impl<'a, T: 'a + ?Sized> PtrInner<'a, T> {
|
|
/// Constructs a `Ptr` from a [`NonNull`].
|
|
///
|
|
/// # Safety
|
|
///
|
|
/// The caller promises that:
|
|
///
|
|
/// 0. If `ptr`'s referent is not zero sized, then `ptr` has valid
|
|
/// provenance for its referent, which is entirely contained in some
|
|
/// Rust allocation, `A`.
|
|
/// 1. If `ptr`'s referent is not zero sized, `A` is guaranteed to live
|
|
/// for at least `'a`.
|
|
#[inline(always)]
|
|
#[must_use]
|
|
pub const unsafe fn new(ptr: NonNull<T>) -> PtrInner<'a, T> {
|
|
// SAFETY: The caller has promised to satisfy all safety invariants
|
|
// of `PtrInner`.
|
|
Self { ptr, _marker: PhantomData }
|
|
}
|
|
|
|
/// Converts this `PtrInner<T>` to a [`NonNull<T>`].
|
|
///
|
|
/// Note that this method does not consume `self`. The caller should
|
|
/// watch out for `unsafe` code which uses the returned `NonNull` in a
|
|
/// way that violates the safety invariants of `self`.
|
|
#[inline(always)]
|
|
#[must_use]
|
|
pub const fn as_non_null(&self) -> NonNull<T> {
|
|
self.ptr
|
|
}
|
|
|
|
/// Converts this `PtrInner<T>` to a [`*mut T`].
|
|
///
|
|
/// Note that this method does not consume `self`. The caller should
|
|
/// watch out for `unsafe` code which uses the returned `*mut T` in a
|
|
/// way that violates the safety invariants of `self`.
|
|
#[inline(always)]
|
|
#[must_use]
|
|
pub const fn as_ptr(&self) -> *mut T {
|
|
self.ptr.as_ptr()
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a, T: ?Sized> PtrInner<'a, T> {
|
|
/// Constructs a `PtrInner` from a reference.
|
|
#[inline]
|
|
pub fn from_ref(ptr: &'a T) -> Self {
|
|
let ptr = NonNull::from(ptr);
|
|
// SAFETY:
|
|
// 0. If `ptr`'s referent is not zero sized, then `ptr`, by invariant on
|
|
// `&'a T` [1], has valid provenance for its referent, which is
|
|
// entirely contained in some Rust allocation, `A`.
|
|
// 1. If `ptr`'s referent is not zero sized, then `A`, by invariant on
|
|
// `&'a T`, is guaranteed to live for at least `'a`.
|
|
//
|
|
// [1] Per https://doc.rust-lang.org/1.85.0/std/primitive.reference.html#safety:
|
|
//
|
|
// For all types, `T: ?Sized`, and for all `t: &T` or `t: &mut T`,
|
|
// when such values cross an API boundary, the following invariants
|
|
// must generally be upheld:
|
|
// ...
|
|
// - if `size_of_val(t) > 0`, then `t` is dereferenceable for
|
|
// `size_of_val(t)` many bytes
|
|
//
|
|
// If `t` points at address `a`, being “dereferenceable” for N bytes
|
|
// means that the memory range `[a, a + N)` is all contained within a
|
|
// single allocated object.
|
|
unsafe { Self::new(ptr) }
|
|
}
|
|
|
|
/// Constructs a `PtrInner` from a mutable reference.
|
|
#[inline]
|
|
pub fn from_mut(ptr: &'a mut T) -> Self {
|
|
let ptr = NonNull::from(ptr);
|
|
// SAFETY:
|
|
// 0. If `ptr`'s referent is not zero sized, then `ptr`, by invariant on
|
|
// `&'a mut T` [1], has valid provenance for its referent, which is
|
|
// entirely contained in some Rust allocation, `A`.
|
|
// 1. If `ptr`'s referent is not zero sized, then `A`, by invariant on
|
|
// `&'a mut T`, is guaranteed to live for at least `'a`.
|
|
//
|
|
// [1] Per https://doc.rust-lang.org/1.85.0/std/primitive.reference.html#safety:
|
|
//
|
|
// For all types, `T: ?Sized`, and for all `t: &T` or `t: &mut T`,
|
|
// when such values cross an API boundary, the following invariants
|
|
// must generally be upheld:
|
|
// ...
|
|
// - if `size_of_val(t) > 0`, then `t` is dereferenceable for
|
|
// `size_of_val(t)` many bytes
|
|
//
|
|
// If `t` points at address `a`, being “dereferenceable” for N bytes
|
|
// means that the memory range `[a, a + N)` is all contained within a
|
|
// single allocated object.
|
|
unsafe { Self::new(ptr) }
|
|
}
|
|
|
|
/// # Safety
|
|
///
|
|
/// The caller may assume that the resulting `PtrInner` addresses the subset
|
|
/// of the bytes of `self`'s referent addressed by `C::project(self)`.
|
|
#[must_use]
|
|
#[inline(always)]
|
|
pub fn project<U: ?Sized, C: cast::Project<T, U>>(self) -> PtrInner<'a, U> {
|
|
let projected_raw = C::project(self);
|
|
|
|
// SAFETY: `self`'s referent lives at a `NonNull` address, and is either
|
|
// zero-sized or lives in an allocation. In either case, it does not
|
|
// wrap around the address space [1], and so none of the addresses
|
|
// contained in it or one-past-the-end of it are null.
|
|
//
|
|
// By invariant on `C: Project`, `C::project` is a provenance-preserving
|
|
// projection which preserves or shrinks the set of referent bytes, so
|
|
// `projected_raw` references a subset of `self`'s referent, and so it
|
|
// cannot be null.
|
|
//
|
|
// [1] https://doc.rust-lang.org/1.92.0/std/ptr/index.html#allocation
|
|
let projected_non_null = unsafe { NonNull::new_unchecked(projected_raw) };
|
|
|
|
// SAFETY: As described in the preceding safety comment, `projected_raw`,
|
|
// and thus `projected_non_null`, addresses a subset of `self`'s
|
|
// referent. Thus, `projected_non_null` either:
|
|
// - Addresses zero bytes or,
|
|
// - Addresses a subset of the referent of `self`. In this case, `self`
|
|
// has provenance for its referent, which lives in an allocation.
|
|
// Since `projected_non_null` was constructed using a sequence of
|
|
// provenance-preserving operations, it also has provenance for its
|
|
// referent and that referent lives in an allocation. By invariant on
|
|
// `self`, that allocation lives for `'a`.
|
|
unsafe { PtrInner::new(projected_non_null) }
|
|
}
|
|
}
|
|
|
|
#[allow(clippy::needless_lifetimes)]
|
|
impl<'a, T> PtrInner<'a, T>
|
|
where
|
|
T: ?Sized + KnownLayout,
|
|
{
|
|
/// Extracts the metadata of this `ptr`.
|
|
#[inline]
|
|
#[must_use]
|
|
pub fn meta(self) -> MetadataOf<T> {
|
|
let meta = T::pointer_to_metadata(self.as_ptr());
|
|
// SAFETY: By invariant on `PtrInner`, `self.as_non_null()` addresses no
|
|
// more than `isize::MAX` bytes.
|
|
unsafe { MetadataOf::new_unchecked(meta) }
|
|
}
|
|
|
|
/// Produces a `PtrInner` with the same address and provenance as `self` but
|
|
/// the given `meta`.
|
|
///
|
|
/// # Safety
|
|
///
|
|
/// The caller promises that if `self`'s referent is not zero sized, then
|
|
/// a pointer constructed from its address with the given `meta` metadata
|
|
/// will address a subset of the allocation pointed to by `self`.
|
|
#[inline]
|
|
#[must_use]
|
|
pub unsafe fn with_meta(self, meta: T::PointerMetadata) -> Self
|
|
where
|
|
T: KnownLayout,
|
|
{
|
|
let raw = T::raw_from_ptr_len(self.as_non_null().cast(), meta);
|
|
|
|
// SAFETY:
|
|
//
|
|
// Lemma 0: `raw` either addresses zero bytes, or addresses a subset of
|
|
// the allocation pointed to by `self` and has the same
|
|
// provenance as `self`. Proof: `raw` is constructed using
|
|
// provenance-preserving operations, and the caller has
|
|
// promised that, if `self`'s referent is not zero-sized, the
|
|
// resulting pointer addresses a subset of the allocation
|
|
// pointed to by `self`.
|
|
//
|
|
// 0. Per Lemma 0 and by invariant on `self`, if `ptr`'s referent is not
|
|
// zero sized, then `ptr` is derived from some valid Rust allocation,
|
|
// `A`.
|
|
// 1. Per Lemma 0 and by invariant on `self`, if `ptr`'s referent is not
|
|
// zero sized, then `ptr` has valid provenance for `A`.
|
|
// 2. Per Lemma 0 and by invariant on `self`, if `ptr`'s referent is not
|
|
// zero sized, then `ptr` addresses a byte range which is entirely
|
|
// contained in `A`.
|
|
// 3. Per Lemma 0 and by invariant on `self`, `ptr` addresses a byte
|
|
// range whose length fits in an `isize`.
|
|
// 4. Per Lemma 0 and by invariant on `self`, `ptr` addresses a byte
|
|
// range which does not wrap around the address space.
|
|
// 5. Per Lemma 0 and by invariant on `self`, if `ptr`'s referent is not
|
|
// zero sized, then `A` is guaranteed to live for at least `'a`.
|
|
unsafe { PtrInner::new(raw) }
|
|
}
|
|
}
|
|
|
|
#[allow(clippy::needless_lifetimes)]
|
|
impl<'a, T> PtrInner<'a, T>
|
|
where
|
|
T: ?Sized + KnownLayout<PointerMetadata = usize>,
|
|
{
|
|
/// Splits `T` in two.
|
|
///
|
|
/// # Safety
|
|
///
|
|
/// The caller promises that:
|
|
/// - `l_len.get() <= self.meta()`.
|
|
///
|
|
/// ## (Non-)Overlap
|
|
///
|
|
/// Given `let (left, right) = ptr.split_at(l_len)`, it is guaranteed that
|
|
/// `left` and `right` are contiguous and non-overlapping if
|
|
/// `l_len.padding_needed_for() == 0`. This is true for all `[T]`.
|
|
///
|
|
/// If `l_len.padding_needed_for() != 0`, then the left pointer will overlap
|
|
/// the right pointer to satisfy `T`'s padding requirements.
|
|
#[inline]
|
|
#[must_use]
|
|
pub unsafe fn split_at_unchecked(
|
|
self,
|
|
l_len: crate::util::MetadataOf<T>,
|
|
) -> (Self, PtrInner<'a, [T::Elem]>)
|
|
where
|
|
T: SplitAt,
|
|
{
|
|
let l_len = l_len.get();
|
|
|
|
// SAFETY: The caller promises that `l_len.get() <= self.meta()`.
|
|
// Trivially, `0 <= l_len`.
|
|
let left = unsafe { self.with_meta(l_len) };
|
|
|
|
let right = self.trailing_slice();
|
|
// SAFETY: The caller promises that `l_len <= self.meta() = slf.meta()`.
|
|
// Trivially, `slf.meta() <= slf.meta()`.
|
|
let right = unsafe { right.slice_unchecked(l_len..self.meta().get()) };
|
|
|
|
// SAFETY: If `l_len.padding_needed_for() == 0`, then `left` and `right`
|
|
// are non-overlapping. Proof: `left` is constructed `slf` with `l_len`
|
|
// as its (exclusive) upper bound. If `l_len.padding_needed_for() == 0`,
|
|
// then `left` requires no trailing padding following its final element.
|
|
// Since `right` is constructed from `slf`'s trailing slice with `l_len`
|
|
// as its (inclusive) lower bound, no byte is referred to by both
|
|
// pointers.
|
|
//
|
|
// Conversely, `l_len.padding_needed_for() == N`, where `N
|
|
// > 0`, `left` requires `N` bytes of trailing padding following its
|
|
// final element. Since `right` is constructed from the trailing slice
|
|
// of `slf` with `l_len` as its (inclusive) lower bound, the first `N`
|
|
// bytes of `right` are aliased by `left`.
|
|
(left, right)
|
|
}
|
|
|
|
/// Produces the trailing slice of `self`.
|
|
#[inline]
|
|
#[must_use]
|
|
pub fn trailing_slice(self) -> PtrInner<'a, [T::Elem]>
|
|
where
|
|
T: SplitAt,
|
|
{
|
|
let offset = crate::trailing_slice_layout::<T>().offset;
|
|
|
|
let bytes = self.as_non_null().cast::<u8>().as_ptr();
|
|
|
|
// SAFETY:
|
|
// - By invariant on `T: KnownLayout`, `T::LAYOUT` describes `T`'s
|
|
// layout. `offset` is the offset of the trailing slice within `T`,
|
|
// which is by definition in-bounds or one byte past the end of any
|
|
// `T`, regardless of metadata. By invariant on `PtrInner`, `self`
|
|
// (and thus `bytes`) points to a byte range of size `<= isize::MAX`,
|
|
// and so `offset <= isize::MAX`. Since `size_of::<u8>() == 1`,
|
|
// `offset * size_of::<u8>() <= isize::MAX`.
|
|
// - If `offset > 0`, then by invariant on `PtrInner`, `self` (and thus
|
|
// `bytes`) points to a byte range entirely contained within the same
|
|
// allocated object as `self`. As explained above, this offset results
|
|
// in a pointer to or one byte past the end of this allocated object.
|
|
let bytes = unsafe { bytes.add(offset) };
|
|
|
|
// SAFETY: By the preceding safety argument, `bytes` is within or one
|
|
// byte past the end of the same allocated object as `self`, which
|
|
// ensures that it is non-null.
|
|
let bytes = unsafe { NonNull::new_unchecked(bytes) };
|
|
|
|
let ptr = KnownLayout::raw_from_ptr_len(bytes, self.meta().get());
|
|
|
|
// SAFETY:
|
|
// 0. If `ptr`'s referent is not zero sized, then `ptr` is derived from
|
|
// some valid Rust allocation, `A`, because `ptr` is derived from
|
|
// the same allocated object as `self`.
|
|
// 1. If `ptr`'s referent is not zero sized, then `ptr` has valid
|
|
// provenance for `A` because `raw` is derived from the same
|
|
// allocated object as `self` via provenance-preserving operations.
|
|
// 2. If `ptr`'s referent is not zero sized, then `ptr` addresses a byte
|
|
// range which is entirely contained in `A`, by previous safety proof
|
|
// on `bytes`.
|
|
// 3. `ptr` addresses a byte range whose length fits in an `isize`, by
|
|
// consequence of #2.
|
|
// 4. `ptr` addresses a byte range which does not wrap around the
|
|
// address space, by consequence of #2.
|
|
// 5. If `ptr`'s referent is not zero sized, then `A` is guaranteed to
|
|
// live for at least `'a`, because `ptr` is derived from `self`.
|
|
unsafe { PtrInner::new(ptr) }
|
|
}
|
|
}
|
|
|
|
#[allow(clippy::needless_lifetimes)]
|
|
impl<'a, T> PtrInner<'a, [T]> {
|
|
/// Creates a pointer which addresses the given `range` of self.
|
|
///
|
|
/// # Safety
|
|
///
|
|
/// `range` is a valid range (`start <= end`) and `end <= self.meta()`.
|
|
#[inline]
|
|
#[must_use]
|
|
pub unsafe fn slice_unchecked(self, range: Range<usize>) -> Self {
|
|
let base = self.as_non_null().cast::<T>().as_ptr();
|
|
|
|
// SAFETY: The caller promises that `start <= end <= self.meta()`. By
|
|
// invariant, if `self`'s referent is not zero-sized, then `self` refers
|
|
// to a byte range which is contained within a single allocation, which
|
|
// is no more than `isize::MAX` bytes long, and which does not wrap
|
|
// around the address space. Thus, this pointer arithmetic remains
|
|
// in-bounds of the same allocation, and does not wrap around the
|
|
// address space. The offset (in bytes) does not overflow `isize`.
|
|
//
|
|
// If `self`'s referent is zero-sized, then these conditions are
|
|
// trivially satisfied.
|
|
let base = unsafe { base.add(range.start) };
|
|
|
|
// SAFETY: The caller promises that `start <= end`, and so this will not
|
|
// underflow.
|
|
#[allow(unstable_name_collisions)]
|
|
let len = unsafe { range.end.unchecked_sub(range.start) };
|
|
|
|
let ptr = core::ptr::slice_from_raw_parts_mut(base, len);
|
|
|
|
// SAFETY: By invariant, `self`'s referent is either a ZST or lives
|
|
// entirely in an allocation. `ptr` points inside of or one byte past
|
|
// the end of that referent. Thus, in either case, `ptr` is non-null.
|
|
let ptr = unsafe { NonNull::new_unchecked(ptr) };
|
|
|
|
// SAFETY:
|
|
//
|
|
// Lemma 0: `ptr` addresses a subset of the bytes addressed by `self`,
|
|
// and has the same provenance. Proof: The caller guarantees
|
|
// that `start <= end <= self.meta()`. Thus, `base` is
|
|
// in-bounds of `self`, and `base + (end - start)` is also
|
|
// in-bounds of self. Finally, `ptr` is constructed using
|
|
// provenance-preserving operations.
|
|
//
|
|
// 0. Per Lemma 0 and by invariant on `self`, if `ptr`'s referent is not
|
|
// zero sized, then `ptr` has valid provenance for its referent,
|
|
// which is entirely contained in some Rust allocation, `A`.
|
|
// 1. Per Lemma 0 and by invariant on `self`, if `ptr`'s referent is not
|
|
// zero sized, then `A` is guaranteed to live for at least `'a`.
|
|
unsafe { PtrInner::new(ptr) }
|
|
}
|
|
|
|
/// Iteratively projects the elements `PtrInner<T>` from `PtrInner<[T]>`.
|
|
#[inline]
|
|
pub fn iter(&self) -> impl Iterator<Item = PtrInner<'a, T>> {
|
|
// FIXME(#429): Once `NonNull::cast` documents that it preserves
|
|
// provenance, cite those docs.
|
|
let base = self.as_non_null().cast::<T>().as_ptr();
|
|
(0..self.meta().get()).map(move |i| {
|
|
// FIXME(https://github.com/rust-lang/rust/issues/74265): Use
|
|
// `NonNull::get_unchecked_mut`.
|
|
|
|
// SAFETY: If the following conditions are not satisfied
|
|
// `pointer::cast` may induce Undefined Behavior [1]:
|
|
//
|
|
// > - The computed offset, `count * size_of::<T>()` bytes, must not
|
|
// > overflow `isize``.
|
|
// > - If the computed offset is non-zero, then `self` must be
|
|
// > derived from a pointer to some allocated object, and the
|
|
// > entire memory range between `self` and the result must be in
|
|
// > bounds of that allocated object. In particular, this range
|
|
// > must not “wrap around” the edge of the address space.
|
|
//
|
|
// [1] https://doc.rust-lang.org/std/primitive.pointer.html#method.add
|
|
//
|
|
// We satisfy both of these conditions here:
|
|
// - By invariant on `Ptr`, `self` addresses a byte range whose
|
|
// length fits in an `isize`. Since `elem` is contained in `self`,
|
|
// the computed offset of `elem` must fit within `isize.`
|
|
// - If the computed offset is non-zero, then this means that the
|
|
// referent is not zero-sized. In this case, `base` points to an
|
|
// allocated object (by invariant on `self`). Thus:
|
|
// - By contract, `self.meta()` accurately reflects the number of
|
|
// elements in the slice. `i` is in bounds of `c.meta()` by
|
|
// construction, and so the result of this addition cannot
|
|
// overflow past the end of the allocation referred to by `c`.
|
|
// - By invariant on `Ptr`, `self` addresses a byte range which
|
|
// does not wrap around the address space. Since `elem` is
|
|
// contained in `self`, the computed offset of `elem` must wrap
|
|
// around the address space.
|
|
//
|
|
// FIXME(#429): Once `pointer::add` documents that it preserves
|
|
// provenance, cite those docs.
|
|
let elem = unsafe { base.add(i) };
|
|
|
|
// SAFETY: `elem` must not be null. `base` is constructed from a
|
|
// `NonNull` pointer, and the addition that produces `elem` must not
|
|
// overflow or wrap around, so `elem >= base > 0`.
|
|
//
|
|
// FIXME(#429): Once `NonNull::new_unchecked` documents that it
|
|
// preserves provenance, cite those docs.
|
|
let elem = unsafe { NonNull::new_unchecked(elem) };
|
|
|
|
// SAFETY: The safety invariants of `Ptr::new` (see definition) are
|
|
// satisfied:
|
|
// 0. If `elem`'s referent is not zero sized, then `elem` has valid
|
|
// provenance for its referent, because it derived from `self`
|
|
// using a series of provenance-preserving operations, and
|
|
// because `self` has valid provenance for its referent. By the
|
|
// same argument, `elem`'s referent is entirely contained within
|
|
// the same allocated object as `self`'s referent.
|
|
// 1. If `elem`'s referent is not zero sized, then the allocation of
|
|
// `elem` is guaranteed to live for at least `'a`, because `elem`
|
|
// is entirely contained in `self`, which lives for at least `'a`
|
|
// by invariant on `Ptr`.
|
|
unsafe { PtrInner::new(elem) }
|
|
})
|
|
}
|
|
}
|
|
|
|
impl<'a, T, const N: usize> PtrInner<'a, [T; N]> {
|
|
/// Casts this pointer-to-array into a slice.
|
|
///
|
|
/// # Safety
|
|
///
|
|
/// Callers may assume that the returned `PtrInner` references the same
|
|
/// address and length as `self`.
|
|
#[allow(clippy::wrong_self_convention)]
|
|
#[inline]
|
|
#[must_use]
|
|
pub fn as_slice(self) -> PtrInner<'a, [T]> {
|
|
let start = self.as_non_null().cast::<T>().as_ptr();
|
|
let slice = core::ptr::slice_from_raw_parts_mut(start, N);
|
|
// SAFETY: `slice` is not null, because it is derived from `start`
|
|
// which is non-null.
|
|
let slice = unsafe { NonNull::new_unchecked(slice) };
|
|
// SAFETY: Lemma: In the following safety arguments, note that `slice`
|
|
// is derived from `self` in two steps: first, by casting `self: [T; N]`
|
|
// to `start: T`, then by constructing a pointer to a slice starting at
|
|
// `start` of length `N`. As a result, `slice` references exactly the
|
|
// same allocation as `self`, if any.
|
|
//
|
|
// 0. By the above lemma, if `slice`'s referent is not zero sized, then
|
|
// `slice` has the same referent as `self`. By invariant on `self`,
|
|
// this referent is entirely contained within some allocation, `A`.
|
|
// Because `slice` was constructed using provenance-preserving
|
|
// operations, it has provenance for its entire referent.
|
|
// 1. By the above lemma, if `slice`'s referent is not zero sized, then
|
|
// `A` is guaranteed to live for at least `'a`, because it is derived
|
|
// from the same allocation as `self`, which, by invariant on
|
|
// `PtrInner`, lives for at least `'a`.
|
|
unsafe { PtrInner::new(slice) }
|
|
}
|
|
}
|
|
|
|
impl<'a> PtrInner<'a, [u8]> {
|
|
/// Attempts to cast `self` to a `U` using the given cast type.
|
|
///
|
|
/// If `U` is a slice DST and pointer metadata (`meta`) is provided, then
|
|
/// the cast will only succeed if it would produce an object with the given
|
|
/// metadata.
|
|
///
|
|
/// Returns `None` if the resulting `U` would be invalidly-aligned, if no
|
|
/// `U` can fit in `self`, or if the provided pointer metadata describes an
|
|
/// invalid instance of `U`. On success, returns a pointer to the
|
|
/// largest-possible `U` which fits in `self`.
|
|
///
|
|
/// # Safety
|
|
///
|
|
/// The caller may assume that this implementation is correct, and may rely
|
|
/// on that assumption for the soundness of their code. In particular, the
|
|
/// caller may assume that, if `try_cast_into` returns `Some((ptr,
|
|
/// remainder))`, then `ptr` and `remainder` refer to non-overlapping byte
|
|
/// ranges within `self`, and that `ptr` and `remainder` entirely cover
|
|
/// `self`. Finally:
|
|
/// - If this is a prefix cast, `ptr` has the same address as `self`.
|
|
/// - If this is a suffix cast, `remainder` has the same address as `self`.
|
|
#[inline]
|
|
pub fn try_cast_into<U>(
|
|
self,
|
|
cast_type: CastType,
|
|
meta: Option<U::PointerMetadata>,
|
|
) -> Result<(PtrInner<'a, U>, PtrInner<'a, [u8]>), CastError<Self, U>>
|
|
where
|
|
U: 'a + ?Sized + KnownLayout,
|
|
{
|
|
// PANICS: By invariant, the byte range addressed by
|
|
// `self.as_non_null()` does not wrap around the address space. This
|
|
// implies that the sum of the address (represented as a `usize`) and
|
|
// length do not overflow `usize`, as required by
|
|
// `validate_cast_and_convert_metadata`. Thus, this call to
|
|
// `validate_cast_and_convert_metadata` will only panic if `U` is a DST
|
|
// whose trailing slice element is zero-sized.
|
|
let maybe_metadata = MetadataOf::<U>::validate_cast_and_convert_metadata(
|
|
AsAddress::addr(self.as_ptr()),
|
|
self.meta(),
|
|
cast_type,
|
|
meta,
|
|
);
|
|
|
|
let (elems, split_at) = match maybe_metadata {
|
|
Ok((elems, split_at)) => (elems, split_at),
|
|
Err(MetadataCastError::Alignment) => {
|
|
// SAFETY: Since `validate_cast_and_convert_metadata` returned
|
|
// an alignment error, `U` must have an alignment requirement
|
|
// greater than one.
|
|
let err = unsafe { AlignmentError::<_, U>::new_unchecked(self) };
|
|
return Err(CastError::Alignment(err));
|
|
}
|
|
Err(MetadataCastError::Size) => return Err(CastError::Size(SizeError::new(self))),
|
|
};
|
|
|
|
// SAFETY: `validate_cast_and_convert_metadata` promises to return
|
|
// `split_at <= self.meta()`.
|
|
//
|
|
// Lemma 0: `l_slice` and `r_slice` are non-overlapping. Proof: By
|
|
// contract on `PtrInner::split_at_unchecked`, the produced `PtrInner`s
|
|
// are always non-overlapping if `self` is a `[T]`; here it is a `[u8]`.
|
|
let (l_slice, r_slice) = unsafe { self.split_at_unchecked(split_at) };
|
|
|
|
let (target, remainder) = match cast_type {
|
|
CastType::Prefix => (l_slice, r_slice),
|
|
CastType::Suffix => (r_slice, l_slice),
|
|
};
|
|
|
|
let base = target.as_non_null().cast::<u8>();
|
|
|
|
let ptr = U::raw_from_ptr_len(base, elems.get());
|
|
|
|
// SAFETY:
|
|
// 0. By invariant, if `target`'s referent is not zero sized, then
|
|
// `target` has provenance valid for some Rust allocation, `A`.
|
|
// Because `ptr` is derived from `target` via provenance-preserving
|
|
// operations, `ptr` will also have provenance valid for its entire
|
|
// referent.
|
|
// 1. `validate_cast_and_convert_metadata` promises that the object
|
|
// described by `elems` and `split_at` lives at a byte range which is
|
|
// a subset of the input byte range. Thus, by invariant, if
|
|
// `target`'s referent is not zero sized, then `target` refers to an
|
|
// allocation which is guaranteed to live for at least `'a`, and thus
|
|
// so does `ptr`.
|
|
Ok((unsafe { PtrInner::new(ptr) }, remainder))
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::*;
|
|
|
|
#[test]
|
|
fn test_meta() {
|
|
let arr = [1; 16];
|
|
let dst = <[u8]>::ref_from_bytes(&arr[..]).unwrap();
|
|
let ptr = PtrInner::from_ref(dst);
|
|
assert_eq!(ptr.meta().get(), 16);
|
|
|
|
// SAFETY: 8 is less than 16
|
|
let ptr = unsafe { ptr.with_meta(8) };
|
|
|
|
assert_eq!(ptr.meta().get(), 8);
|
|
}
|
|
|
|
#[test]
|
|
fn test_split_at() {
|
|
fn test_split_at<const OFFSET: usize, const BUFFER_SIZE: usize>() {
|
|
#[derive(FromBytes, KnownLayout, SplitAt, Immutable)]
|
|
#[repr(C)]
|
|
struct SliceDst<const OFFSET: usize> {
|
|
prefix: [u8; OFFSET],
|
|
trailing: [u8],
|
|
}
|
|
|
|
let n: usize = BUFFER_SIZE - OFFSET;
|
|
let arr = [1; BUFFER_SIZE];
|
|
let dst = SliceDst::<OFFSET>::ref_from_bytes(&arr[..]).unwrap();
|
|
let ptr = PtrInner::from_ref(dst);
|
|
for i in 0..=n {
|
|
assert_eq!(ptr.meta().get(), n);
|
|
// SAFETY: `i` is in bounds by construction.
|
|
let i = unsafe { MetadataOf::new_unchecked(i) };
|
|
// SAFETY: `i` is in bounds by construction.
|
|
let (l, r) = unsafe { ptr.split_at_unchecked(i) };
|
|
// SAFETY: Points to a valid value by construction.
|
|
#[allow(clippy::undocumented_unsafe_blocks, clippy::as_conversions)]
|
|
// Clippy false positive
|
|
let l_sum: usize = l
|
|
.trailing_slice()
|
|
.iter()
|
|
.map(
|
|
#[inline(always)]
|
|
|ptr| unsafe { core::ptr::read_unaligned(ptr.as_ptr()) } as usize,
|
|
)
|
|
.sum();
|
|
// SAFETY: Points to a valid value by construction.
|
|
#[allow(clippy::undocumented_unsafe_blocks, clippy::as_conversions)]
|
|
// Clippy false positive
|
|
let r_sum: usize = r
|
|
.iter()
|
|
.map(
|
|
#[inline(always)]
|
|
|ptr| unsafe { core::ptr::read_unaligned(ptr.as_ptr()) } as usize,
|
|
)
|
|
.sum();
|
|
assert_eq!(l_sum, i.get());
|
|
assert_eq!(r_sum, n - i.get());
|
|
assert_eq!(l_sum + r_sum, n);
|
|
}
|
|
}
|
|
|
|
test_split_at::<0, 16>();
|
|
test_split_at::<1, 17>();
|
|
test_split_at::<2, 18>();
|
|
}
|
|
|
|
#[test]
|
|
fn test_trailing_slice() {
|
|
fn test_trailing_slice<const OFFSET: usize, const BUFFER_SIZE: usize>() {
|
|
#[derive(FromBytes, KnownLayout, SplitAt, Immutable)]
|
|
#[repr(C)]
|
|
struct SliceDst<const OFFSET: usize> {
|
|
prefix: [u8; OFFSET],
|
|
trailing: [u8],
|
|
}
|
|
|
|
let n: usize = BUFFER_SIZE - OFFSET;
|
|
let arr = [1; BUFFER_SIZE];
|
|
let dst = SliceDst::<OFFSET>::ref_from_bytes(&arr[..]).unwrap();
|
|
let ptr = PtrInner::from_ref(dst);
|
|
|
|
assert_eq!(ptr.meta().get(), n);
|
|
let trailing = ptr.trailing_slice();
|
|
assert_eq!(trailing.meta().get(), n);
|
|
|
|
assert_eq!(
|
|
// SAFETY: We assume this to be sound for the sake of this test,
|
|
// which will fail, here, in miri, if the safety precondition of
|
|
// `offset_of` is not satisfied.
|
|
unsafe {
|
|
#[allow(clippy::as_conversions)]
|
|
let offset = (trailing.as_ptr() as *mut u8).offset_from(ptr.as_ptr() as *mut _);
|
|
offset
|
|
},
|
|
isize::try_from(OFFSET).unwrap(),
|
|
);
|
|
|
|
// SAFETY: Points to a valid value by construction.
|
|
#[allow(clippy::undocumented_unsafe_blocks, clippy::as_conversions)]
|
|
// Clippy false positive
|
|
let trailing: usize = trailing
|
|
.iter()
|
|
.map(|ptr| unsafe { core::ptr::read_unaligned(ptr.as_ptr()) } as usize)
|
|
.sum();
|
|
|
|
assert_eq!(trailing, n);
|
|
}
|
|
|
|
test_trailing_slice::<0, 16>();
|
|
test_trailing_slice::<1, 17>();
|
|
test_trailing_slice::<2, 18>();
|
|
}
|
|
#[test]
|
|
fn test_ptr_inner_clone() {
|
|
let mut x = 0u8;
|
|
let p = PtrInner::from_mut(&mut x);
|
|
#[allow(clippy::clone_on_copy)]
|
|
let p2 = p.clone();
|
|
assert_eq!(p.as_non_null(), p2.as_non_null());
|
|
}
|
|
}
|