Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
431 changes: 344 additions & 87 deletions src/impls.rs

Large diffs are not rendered by default.

69 changes: 48 additions & 21 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ use core::alloc::Layout;

// Used by `TryFromBytes::is_bit_valid`.
#[doc(hidden)]
pub use crate::pointer::{invariant::BecauseImmutable, Maybe, MaybeAligned, Ptr};
pub use crate::pointer::{invariant::BecauseImmutable, Maybe, Ptr};
// Used by `KnownLayout`.
#[doc(hidden)]
pub use crate::layout::*;
Expand Down Expand Up @@ -805,6 +805,17 @@ pub unsafe trait KnownLayout {
// resulting size would not fit in a `usize`.
meta.size_for_metadata(Self::LAYOUT)
}

#[doc(hidden)]
#[must_use]
#[inline(always)]
fn cast_from_raw<P: KnownLayout<PointerMetadata = Self::PointerMetadata> + ?Sized>(
ptr: NonNull<P>,
) -> NonNull<Self> {
let data = ptr.cast::<u8>();
let meta = P::pointer_to_metadata(ptr.as_ptr());
Self::raw_from_ptr_len(data, meta)
}
}

/// The metadata associated with a [`KnownLayout`] type.
Expand Down Expand Up @@ -2843,29 +2854,43 @@ unsafe fn try_read_from<S, T: TryFromBytes>(
// We use `from_mut` despite not mutating via `c_ptr` so that we don't need
// to add a `T: Immutable` bound.
let c_ptr = Ptr::from_mut(&mut candidate);
let c_ptr = c_ptr.transparent_wrapper_into_inner();
// SAFETY: `c_ptr` has no uninitialized sub-ranges because it derived from
// `candidate`, which the caller promises is entirely initialized. Since
// `candidate` is a `MaybeUninit`, it has no validity requirements, and so
// no values written to `c_ptr` can violate its validity. Since `c_ptr` has
// `Exclusive` aliasing, no mutations may happen except via `c_ptr` so long
// as it is live, so we don't need to worry about the fact that `c_ptr` may
// have more restricted validity than `candidate`.
// no values written to an `Initialized` `c_ptr` can violate its validity.
// Since `c_ptr` has `Exclusive` aliasing, no mutations may happen except
// via `c_ptr` so long as it is live, so we don't need to worry about the
// fact that `c_ptr` may have more restricted validity than `candidate`.
let c_ptr = unsafe { c_ptr.assume_validity::<invariant::Initialized>() };
let c_ptr = c_ptr.transmute();

// Since we don't have `T: KnownLayout`, we hack around that by using
// `Wrapping<T>`, which implements `KnownLayout` even if `T` doesn't.
//
// This call may panic. If that happens, it doesn't cause any soundness
// issues, as we have not generated any invalid state which we need to
// fix before returning.
// issues, as we have not generated any invalid state which we need to fix
// before returning.
//
// Note that one panic or post-monomorphization error condition is
// calling `try_into_valid` (and thus `is_bit_valid`) with a shared
// pointer when `Self: !Immutable`. Since `Self: Immutable`, this panic
// condition will not happen.
if !T::is_bit_valid(c_ptr.forget_aligned()) {
// Note that one panic or post-monomorphization error condition is calling
// `try_into_valid` (and thus `is_bit_valid`) with a shared pointer when
// `Self: !Immutable`. Since `Self: Immutable`, this panic condition will
// not happen.
if !Wrapping::<T>::is_bit_valid(c_ptr.forget_aligned()) {
return Err(ValidityError::new(source).into());
}

// SAFETY: We just validated that `candidate` contains a valid `T`.
fn _assert_same_size_and_validity<T>()
where
Wrapping<T>: pointer::TransmuteFrom<T, invariant::Valid, invariant::Valid>,
T: pointer::TransmuteFrom<Wrapping<T>, invariant::Valid, invariant::Valid>,
{
}

_assert_same_size_and_validity::<T>();

// SAFETY: We just validated that `candidate` contains a valid
// `Wrapping<T>`, which has the same size and bit validity as `T`, as
// guaranteed by the preceding type assertion.
Ok(unsafe { candidate.assume_init() })
}

Expand Down Expand Up @@ -3552,7 +3577,7 @@ pub unsafe trait FromBytes: FromZeros {
{
static_assert_dst_is_not_zst!(Self);
match Ptr::from_ref(source).try_cast_into_no_leftover::<_, BecauseImmutable>(None) {
Ok(ptr) => Ok(ptr.bikeshed_recall_valid().as_ref()),
Ok(ptr) => Ok(ptr.recall_validity().as_ref()),
Err(err) => Err(err.map_src(|src| src.as_ref())),
}
}
Expand Down Expand Up @@ -3788,7 +3813,7 @@ pub unsafe trait FromBytes: FromZeros {
{
static_assert_dst_is_not_zst!(Self);
match Ptr::from_mut(source).try_cast_into_no_leftover::<_, BecauseExclusive>(None) {
Ok(ptr) => Ok(ptr.bikeshed_recall_valid().as_mut()),
Ok(ptr) => Ok(ptr.recall_validity().as_mut()),
Err(err) => Err(err.map_src(|src| src.as_mut())),
}
}
Expand Down Expand Up @@ -4027,7 +4052,7 @@ pub unsafe trait FromBytes: FromZeros {
let source = Ptr::from_ref(source);
let maybe_slf = source.try_cast_into_no_leftover::<_, BecauseImmutable>(Some(count));
match maybe_slf {
Ok(slf) => Ok(slf.bikeshed_recall_valid().as_ref()),
Ok(slf) => Ok(slf.recall_validity().as_ref()),
Err(err) => Err(err.map_src(|s| s.as_ref())),
}
}
Expand Down Expand Up @@ -4258,7 +4283,9 @@ pub unsafe trait FromBytes: FromZeros {
let source = Ptr::from_mut(source);
let maybe_slf = source.try_cast_into_no_leftover::<_, BecauseImmutable>(Some(count));
match maybe_slf {
Ok(slf) => Ok(slf.bikeshed_recall_valid().as_mut()),
Ok(slf) => Ok(slf
.recall_validity::<_, (_, (_, (BecauseExclusive, BecauseExclusive)))>()
.as_mut()),
Err(err) => Err(err.map_src(|s| s.as_mut())),
}
}
Expand Down Expand Up @@ -4716,7 +4743,7 @@ fn ref_from_prefix_suffix<T: FromBytes + KnownLayout + Immutable + ?Sized>(
let (slf, prefix_suffix) = Ptr::from_ref(source)
.try_cast_into::<_, BecauseImmutable>(cast_type, meta)
.map_err(|err| err.map_src(|s| s.as_ref()))?;
Ok((slf.bikeshed_recall_valid().as_ref(), prefix_suffix.as_ref()))
Ok((slf.recall_validity().as_ref(), prefix_suffix.as_ref()))
}

/// Interprets the given affix of the given bytes as a `&mut Self` without
Expand All @@ -4728,15 +4755,15 @@ fn ref_from_prefix_suffix<T: FromBytes + KnownLayout + Immutable + ?Sized>(
/// If there are insufficient bytes, or if that affix of `source` is not
/// appropriately aligned, this returns `Err`.
#[inline(always)]
fn mut_from_prefix_suffix<T: FromBytes + KnownLayout + ?Sized>(
fn mut_from_prefix_suffix<T: FromBytes + IntoBytes + KnownLayout + ?Sized>(
source: &mut [u8],
meta: Option<T::PointerMetadata>,
cast_type: CastType,
) -> Result<(&mut T, &mut [u8]), CastError<&mut [u8], T>> {
let (slf, prefix_suffix) = Ptr::from_mut(source)
.try_cast_into::<_, BecauseExclusive>(cast_type, meta)
.map_err(|err| err.map_src(|s| s.as_mut()))?;
Ok((slf.bikeshed_recall_valid().as_mut(), prefix_suffix.as_mut()))
Ok((slf.recall_validity().as_mut(), prefix_suffix.as_mut()))
}

/// Analyzes whether a type is [`IntoBytes`].
Expand Down
40 changes: 10 additions & 30 deletions src/pointer/inner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -488,34 +488,6 @@ impl<'a> PtrInner<'a, [u8]> {
}
}

#[allow(clippy::needless_lifetimes)]
impl<'a, T> PtrInner<'a, T> {
/// Performs an unaligned read of `self`'s referent.
///
/// # Safety
///
/// `self` must point to a properly initialized value of type `T`, and
/// reading a copy of `T` must not violate `T`'s safety invariants.
///
/// `self`'s referent must not be concurrently modified during this call.
pub(crate) unsafe fn read_unaligned(self) -> T {
let raw = self.as_non_null().as_ptr();
// SAFETY: The caller promises that `self` points to a bit-valid `T` and
// that reading a copy of it won't violate `T`'s safety invariants. The
// caller promises that `self`'s referent won't be concurrently modified
// during this operation.
//
// `raw` is valid for reads:
// - `self.as_non_null()` returns a `NonNull`, which is guaranteed to be
// non-null.
// - By invariant on `PtrInner`, `raw` is is either zero-sized or:
// - ...is within bounds of a single allocated object which lives for
// at least `'a`.
// - ...has valid provenance for that object.
unsafe { core::ptr::read_unaligned(raw) }
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand All @@ -530,9 +502,17 @@ mod tests {
// SAFETY: `i` is in bounds by construction.
let (l, r) = unsafe { ptr.split_at(i) };
// SAFETY: Points to a valid value by construction.
let l_sum: usize = l.iter().map(|ptr| unsafe { ptr.read_unaligned() }).sum();
#[allow(clippy::undocumented_unsafe_blocks)] // Clippy false positive
let l_sum: usize = l
.iter()
.map(|ptr| unsafe { core::ptr::read_unaligned(ptr.as_non_null().as_ptr()) })
.sum();
// SAFETY: Points to a valid value by construction.
let r_sum: usize = r.iter().map(|ptr| unsafe { ptr.read_unaligned() }).sum();
#[allow(clippy::undocumented_unsafe_blocks)] // Clippy false positive
let r_sum: usize = r
.iter()
.map(|ptr| unsafe { core::ptr::read_unaligned(ptr.as_non_null().as_ptr()) })
.sum();
assert_eq!(l_sum, i);
assert_eq!(r_sum, N - i);
assert_eq!(l_sum + r_sum, N);
Expand Down
11 changes: 7 additions & 4 deletions src/pointer/invariant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,11 @@ pub trait Reference: Aliasing + Sealed {}
/// The `Ptr<'a, T>` adheres to the aliasing rules of a `&'a T`.
///
/// The referent of a shared-aliased `Ptr` may be concurrently referenced by any
/// number of shared-aliased `Ptr` or `&T` references, and may not be
/// concurrently referenced by any exclusively-aliased `Ptr`s or `&mut T`
/// references. The referent must not be mutated, except via [`UnsafeCell`]s.
/// number of shared-aliased `Ptr` or `&T` references, or by any number of
/// `Ptr<U>` or `&U` references as permitted by `T`'s library safety invariants,
/// and may not be concurrently referenced by any exclusively-aliased `Ptr`s or
/// `&mut` references. The referent must not be mutated, except via
/// [`UnsafeCell`]s, and only when permitted by `T`'s library safety invariants.
///
/// [`UnsafeCell`]: core::cell::UnsafeCell
pub enum Shared {}
Expand Down Expand Up @@ -178,7 +180,8 @@ pub enum Initialized {}
// required to uphold).
unsafe impl Validity for Initialized {}

/// The referent of a `Ptr<T>` is bit-valid for `T`.
/// The referent of a `Ptr<T>` is valid for `T`, upholding bit validity and any
/// library safety invariants.
pub enum Valid {}
// SAFETY: `Valid`'s validity is well-defined for all `T: ?Sized`, and is not a
// function of any property of `T` other than its bit validity.
Expand Down
59 changes: 6 additions & 53 deletions src/pointer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ mod inner;
#[doc(hidden)]
pub mod invariant;
mod ptr;
mod transmute;

#[doc(hidden)]
pub use invariant::{BecauseExclusive, BecauseImmutable, Read};
pub(crate) use transmute::*;
#[doc(hidden)]
pub use ptr::Ptr;

use crate::Unaligned;
pub use {
invariant::{BecauseExclusive, BecauseImmutable, Read},
ptr::Ptr,
};

/// A shorthand for a maybe-valid, maybe-aligned reference. Used as the argument
/// to [`TryFromBytes::is_bit_valid`].
Expand All @@ -27,55 +29,6 @@ use crate::Unaligned;
pub type Maybe<'a, T, Aliasing = invariant::Shared, Alignment = invariant::Unaligned> =
Ptr<'a, T, (Aliasing, Alignment, invariant::Initialized)>;

/// A semi-user-facing wrapper type representing a maybe-aligned reference, for
/// use in [`TryFromBytes::is_bit_valid`].
///
/// [`TryFromBytes::is_bit_valid`]: crate::TryFromBytes::is_bit_valid
pub type MaybeAligned<'a, T, Aliasing = invariant::Shared, Alignment = invariant::Unaligned> =
Ptr<'a, T, (Aliasing, Alignment, invariant::Valid)>;

// These methods are defined on the type alias, `MaybeAligned`, so as to bring
// them to the forefront of the rendered rustdoc for that type alias.
impl<'a, T, Aliasing, Alignment> MaybeAligned<'a, T, Aliasing, Alignment>
where
T: 'a + ?Sized,
Aliasing: invariant::Aliasing,
Alignment: invariant::Alignment,
{
/// Reads the value from `MaybeAligned`.
#[must_use]
#[inline]
pub fn read_unaligned<R>(self) -> T
where
T: Copy,
T: invariant::Read<Aliasing, R>,
{
// SAFETY: By invariant on `MaybeAligned`, `self` contains
// validly-initialized data for `T`. By `T: Read<Aliasing>`, we are
// permitted to perform a read of `self`'s referent.
unsafe { self.as_inner().read_unaligned() }
}
}

impl<'a, T, Aliasing, Alignment> MaybeAligned<'a, T, Aliasing, Alignment>
where
T: 'a + ?Sized,
Aliasing: invariant::Reference,
Alignment: invariant::Alignment,
{
/// Views the value as an aligned reference.
///
/// This is only available if `T` is [`Unaligned`].
#[must_use]
#[inline]
pub fn unaligned_as_ref(self) -> &'a T
where
T: Unaligned,
{
self.bikeshed_recall_aligned().as_ref()
}
}

/// Checks if the referent is zeroed.
pub(crate) fn is_zeroed<T, I>(ptr: Ptr<'_, T, I>) -> bool
where
Expand Down
Loading