From 02f59828316a55c99188c4bcda6ed4f566b85fe5 Mon Sep 17 00:00:00 2001 From: Joshua Liebow-Feeser Date: Fri, 7 Feb 2025 14:56:24 -0800 Subject: [PATCH] [WIP] Transmute Co-authored-by: Jack Wrenn gherrit-pr-id: I8d5d162c1b6fe43e3dcb90a6dc5bf58a7a203bf8 --- src/error.rs | 14 ++ src/lib.rs | 30 ++- src/pointer/mod.rs | 1 + src/pointer/ptr.rs | 151 +++++++++++- src/pointer/transmute.rs | 505 +++++++++++++++++++++++++++++++++++++++ src/ref.rs | 8 +- src/util/macro_util.rs | 197 +++++++-------- src/util/macros.rs | 2 +- 8 files changed, 784 insertions(+), 124 deletions(-) create mode 100644 src/pointer/transmute.rs diff --git a/src/error.rs b/src/error.rs index 39b2a864bf..8bfd4dd9d2 100644 --- a/src/error.rs +++ b/src/error.rs @@ -594,6 +594,20 @@ impl ValidityError { ValidityError { src: f(self.src), dst: SendSyncPhantomData::default() } } + /// Changes the destination type. + /// + /// # Safety + /// + /// `NewDst` must have the same validity - as implemented by + /// [`TryFromBytes::is_bit_valid`] - as `Dst`. + pub(crate) unsafe fn with_dst(self) -> ValidityError { + // SAFETY: There is currently no invariant required of `dst`, so this + // method's safety precondition is unnecessary. However, we require it + // to be forwards-compatible with a point in time where we add an + // invariant to the `Dst` type. + ValidityError { src: self.src, dst: SendSyncPhantomData::default() } + } + /// Converts the error into a general [`ConvertError`]. pub(crate) const fn into(self) -> ConvertError { ConvertError::Validity(self) diff --git a/src/lib.rs b/src/lib.rs index ee28450019..81d62fb070 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -375,7 +375,10 @@ use core::{ #[cfg(feature = "std")] use std::io; -use crate::pointer::invariant::{self, BecauseExclusive}; +use crate::pointer::{ + invariant::{self, BecauseExclusive}, + transmute::BecauseRead, +}; #[cfg(any(feature = "alloc", test))] extern crate alloc; @@ -1790,7 +1793,7 @@ pub unsafe trait TryFromBytes { // 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. - match source.try_into_valid() { + match source.try_into_valid::<(pointer::transmute::BecauseRead, _)>() { Ok(source) => Ok(source.as_mut()), Err(e) => { Err(e.map_src(|src| src.as_bytes::().as_mut()).into()) @@ -2372,7 +2375,7 @@ pub unsafe trait TryFromBytes { // 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. - match source.try_into_valid() { + match source.try_into_valid::<(BecauseRead, _)>() { Ok(source) => Ok(source.as_mut()), Err(e) => { Err(e.map_src(|src| src.as_bytes::().as_mut()).into()) @@ -2779,7 +2782,7 @@ fn try_ref_from_prefix_suffix( +fn try_mut_from_prefix_suffix( candidate: &mut [u8], cast_type: CastType, meta: Option, @@ -2794,7 +2797,7 @@ fn try_mut_from_prefix_suffix() { Ok(valid) => Ok((valid.as_mut(), prefix_suffix.as_mut())), Err(e) => Err(e.map_src(|src| src.as_bytes::().as_mut()).into()), } @@ -3508,7 +3511,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.transmute().as_ref()), Err(err) => Err(err.map_src(|src| src.as_ref())), } } @@ -3744,7 +3747,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.bikeshed_recall_valid::<(BecauseRead, BecauseExclusive)>().as_mut()), Err(err) => Err(err.map_src(|src| src.as_mut())), } } @@ -3983,7 +3986,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.transmute().as_ref()), Err(err) => Err(err.map_src(|s| s.as_ref())), } } @@ -4214,7 +4217,7 @@ 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.bikeshed_recall_valid::<(BecauseRead, BecauseExclusive)>().as_mut()), Err(err) => Err(err.map_src(|s| s.as_mut())), } } @@ -4590,7 +4593,7 @@ fn ref_from_prefix_suffix( 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.transmute().as_ref(), prefix_suffix.as_ref())) } /// Interprets the given affix of the given bytes as a `&mut Self` without @@ -4602,7 +4605,7 @@ fn ref_from_prefix_suffix( /// 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( +fn mut_from_prefix_suffix( source: &mut [u8], meta: Option, cast_type: CastType, @@ -4610,7 +4613,10 @@ fn mut_from_prefix_suffix( 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.transmute::, _, (BecauseRead, _), _>().as_mut(), + prefix_suffix.as_mut(), + )) } /// Analyzes whether a type is [`IntoBytes`]. diff --git a/src/pointer/mod.rs b/src/pointer/mod.rs index 5d38a92b90..ddaf1600f6 100644 --- a/src/pointer/mod.rs +++ b/src/pointer/mod.rs @@ -12,6 +12,7 @@ mod inner; #[doc(hidden)] pub mod invariant; mod ptr; +pub(crate) mod transmute; #[doc(hidden)] pub use invariant::{BecauseExclusive, BecauseImmutable, Read}; diff --git a/src/pointer/ptr.rs b/src/pointer/ptr.rs index 59ec627479..b676b2b6b9 100644 --- a/src/pointer/ptr.rs +++ b/src/pointer/ptr.rs @@ -171,7 +171,10 @@ mod _external { /// Methods for converting to and from `Ptr` and Rust's safe reference types. mod _conversions { use super::*; - use crate::util::{AlignmentVariance, Covariant, TransparentWrapper, ValidityVariance}; + use crate::{ + pointer::transmute::{CastFrom, TransmuteFromAlignment, TransmuteFromPtr}, + util::{AlignmentVariance, Covariant, TransparentWrapper, ValidityVariance}, + }; /// `&'a T` → `Ptr<'a, T>` impl<'a, T: ?Sized> Ptr<'a, Valid, (Shared, Aligned)> { @@ -337,6 +340,54 @@ mod _conversions { } } + /// `Ptr<'a, T>` → `Ptr<'a, U>` + impl<'a, V, I> Ptr<'a, V, I> + where + V: Validity, + I: Invariants, + { + /// # Safety + /// - TODO: `UnsafeCell` agreement + /// - The caller promises that the returned `Ptr` satisfies alignment + /// `A` + /// - The caller promises that the returned `Ptr` satisfies validity `V` + pub(crate) unsafe fn transmute_unchecked(self) -> Ptr<'a, U, (I::Aliasing, A)> + where + A: Alignment, + U: Validity, + U::Inner: CastFrom, + { + // SAFETY: + // - By invariant on `CastFrom::cast_from`: + // - This cast preserves address and referent size, and thus the + // returned pointer addresses the same bytes as `p` + // - This cast preserves provenance + // - TODO: `UnsafeCell` agreement + let ptr = + unsafe { self.cast_unsized_unchecked::(|p| U::Inner::cast_from(p)) }; + // SAFETY: The caller promises that alignment is satisfied. + let ptr = unsafe { ptr.assume_alignment() }; + // SAFETY: The caller promises that validity is satisfied. + let ptr = unsafe { ptr.assume_validity::() }; + ptr.unify_validity() + } + + pub(crate) fn transmute(self) -> Ptr<'a, U, (I::Aliasing, A)> + where + A: Alignment, + U: TransmuteFromPtr, + U::Inner: TransmuteFromAlignment + CastFrom, + { + // SAFETY: + // - TODO: `UnsafeCell` agreement + // - By invariant on `TransmuteFromPtr`, it is sound to treat the + // resulting pointer as having alignment `A` + // - By invariant on `TransmuteFromPtr`, it is sound to treat the + // resulting pointer as having validity `V` + unsafe { self.transmute_unchecked() } + } + } + /// `Ptr<'a, T = Wrapper>` → `Ptr<'a, U>` impl<'a, V, I> Ptr<'a, V, I> where @@ -428,7 +479,10 @@ mod _conversions { /// State transitions between invariants. mod _transitions { use super::*; - use crate::{AlignmentError, TryFromBytes, ValidityError}; + use crate::{ + pointer::transmute::{CastFrom, TransmuteFromPtr, TryTransmuteFromPtr}, + AlignmentError, TryFromBytes, ValidityError, + }; impl<'a, V, I> Ptr<'a, V, I> where @@ -649,6 +703,22 @@ mod _transitions { } } + impl<'a, V, I> Ptr<'a, V, I> + where + V: Validity, + I: Invariants, + I::Aliasing: Reference, + { + /// Forgets that `self` is an `Exclusive` pointer, downgrading it to a + /// `Shared` pointer. + #[doc(hidden)] + #[must_use] + #[inline] + pub const fn forget_exclusive(self) -> Ptr<'a, V, I::WithAliasing> { + unsafe { self.assume_invariants() } + } + } + impl<'a, T, I> Ptr<'a, Initialized, I> where T: ?Sized, @@ -660,14 +730,12 @@ mod _transitions { #[inline] // TODO(#859): Reconsider the name of this method before making it // public. - pub const fn bikeshed_recall_valid(self) -> Ptr<'a, Valid, I> + pub fn bikeshed_recall_valid(self) -> Ptr<'a, Valid, I> where - T: crate::FromBytes, + Valid: TransmuteFromPtr, I::Aliasing, R>, + as Validity>::Inner: CastFrom, { - // SAFETY: The bound `T: FromBytes` ensures that any initialized - // sequence of bytes is bit-valid for `T`. `V = Initialized` - // ensures that all of the referent bytes are initialized. - unsafe { self.assume_valid() } + self.transmute().unify_invariants() } /// Checks that `self`'s referent is validly initialized for `T`, @@ -687,7 +755,12 @@ mod _transitions { mut self, ) -> Result, I>, ValidityError> where - T: TryFromBytes + Read, + T: TryFromBytes, // + Read, + Valid: TryTransmuteFromPtr, I::Aliasing, R>, + // NOTE: This bound ought to be implied, but leaving it out causes + // Rust to infinite loop during trait solving. + as Validity>::Inner: + crate::pointer::transmute::CastFrom< as Validity>::Inner>, I::Aliasing: Reference, { // This call may panic. If that happens, it doesn't cause any soundness @@ -702,12 +775,63 @@ mod _transitions { } } } + + impl<'a, V, I> Ptr<'a, V, I> + where + V: Validity, + I: Invariants, + { + /// Attempts to transmute a `Ptr` into a `Ptr`. + /// + /// # Panics + /// + /// This method will panic if + /// [`U::is_bit_valid`][TryFromBytes::is_bit_valid] panics. + /// + /// # Safety + /// + /// On success, the returned `Ptr` addresses the same bytes as `self`. + /// + /// On error, unsafe code may rely on this method's returned + /// `ValidityError` containing `self`. + #[inline] + pub(crate) fn try_transmute( + mut self, + ) -> Result>, ValidityError> + where + U: Validity + TryTransmuteFromPtr, + U::Inner: TryFromBytes + CastFrom, + Initialized: TransmuteFromPtr, + // TODO: The `Sized` bound here is only required in order to call + // `.bikeshed_try_into_aligned`. There are other ways of getting the + // alignment of a type, and we could use these if we need to drop + // this bound. + as Validity>::Inner: Sized + CastFrom, + I::Aliasing: Reference, + { + let is_bit_valid = { + let ptr = self.reborrow(); + let ptr = ptr.transmute::, Unknown, _, _>(); + // 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. + ::is_bit_valid(ptr) + }; + + if is_bit_valid { + let ptr = unsafe { self.transmute_unchecked() }; + Ok(ptr.unify_invariants()) + } else { + Err(ValidityError::new(self)) + } + } + } } /// Casts of the referent type. mod _casts { use super::*; - use crate::{CastError, SizeError}; + use crate::{pointer::transmute::BecauseRead, CastError, SizeError}; impl<'a, V, I> Ptr<'a, V, I> where @@ -866,7 +990,7 @@ mod _casts { }) }; - ptr.bikeshed_recall_aligned().bikeshed_recall_valid() + ptr.bikeshed_recall_aligned().bikeshed_recall_valid::<(BecauseRead, _)>() } } @@ -1125,6 +1249,7 @@ mod tests { mod test_ptr_try_cast_into_soundness { use super::*; + use crate::IntoBytes; // This test is designed so that if `Ptr::try_cast_into_xxx` are // buggy, it will manifest as unsoundness that Miri can detect. @@ -1164,7 +1289,9 @@ mod tests { }; // SAFETY: The bytes in `slf` must be initialized. - unsafe fn validate_and_get_len( + unsafe fn validate_and_get_len< + T: ?Sized + KnownLayout + FromBytes + Immutable, + >( slf: Ptr<'_, Initialized, (Shared, Aligned)>, ) -> usize { let t = slf.bikeshed_recall_valid().as_ref(); diff --git a/src/pointer/transmute.rs b/src/pointer/transmute.rs new file mode 100644 index 0000000000..c6d266ca44 --- /dev/null +++ b/src/pointer/transmute.rs @@ -0,0 +1,505 @@ +// Copyright 2025 The Fuchsia Authors +// +// Licensed under a BSD-style license , Apache License, Version 2.0 +// , or the MIT +// license , at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +use core::{ + cell::UnsafeCell, + mem::{ManuallyDrop, MaybeUninit}, + num::Wrapping, +}; + +use crate::{pointer::invariant::*, FromBytes, Immutable, IntoBytes, Unalign}; + +define_because!(pub(crate) BecauseBidirectional); +define_because!(pub(crate) BecauseRead); +define_because!(pub(crate) BecauseFoo); +define_because!(BecauseExclusive); +define_because!(BecauseUnchanged); +define_because!(BecauseUnaligned); + +// TODO: Confirm that we don't need to explicitly mention size (that should be +// handled automatically by impls, usually via a `TransmuteFrom` bound). + +/// # Safety +/// +/// ## Post-conditions +/// +/// Given `Dst: TryTransmuteFromPtr`, callers may assume the +/// following: +/// +/// Given `src: Ptr` where `SI: Invariants`, if the referent of `src` contains a `Dst` which conforms to the +/// validity `DV`, then it is sound to transmute `src` into `dst: Ptr` +/// whre `DI: Invariants`. +/// +/// TODO: Mention alignment +/// +/// ## Pre-conditions +/// +/// Given `src: Ptr`, `dst: Ptr`, `SI: Invariants`, and `DV: Invariants`, `Dst: +/// TryTransmuteFromPtr` is sound if all of the following +/// hold: +/// - Forwards transmutation: Any of the following hold: +/// - So long as `dst` is active, no mutation of `dst`'s referent is allowed +/// except via `dst` itself +/// - The set of `DV`-valid `Dst`s is a superset of the set of `SV`-valid +/// `Src`s +/// - Reverse transmutation: Any of the following hold: +/// - `dst` does not permit mutation of its referent +/// - The set of `DV`-valid `Dst`s is a subset of the set of `SV`-valid +/// `Src`s +/// - `UnsafeCell` agreement: TODO +/// +/// ## Proof +/// +/// TODO: Prove that the pre-conditions imply the post-conditions. +#[cfg_attr(__ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS, marker)] +pub(crate) unsafe trait TryTransmuteFromPtr: + Validity +where + Self::Inner: CastFrom, +{ +} + +// SAFETY: +// - Forwards transmutation: Since `Src::Inner: Read`, one of the +// following holds: +// - `Src: Immutable`, so no mutation of `dst`'s referent is permitted via +// `src`. No other references to the same referent may exist which are typed +// using `T: !Immutable`, as this would permit violating `Src: Immutable`'s +// soundness precondition. +// - Aliasing `A` is `Exclusive`, so `dst` is the only reference permitted to +// mutate its referent. +// - Reverse transmutation: Since `Src: TransmuteFrom`, `Dst`'s validity +// set is a subset of `Src`'s validity set. +// - Since `Src::Inner: Read` and `Dst::Inner: Read`, one of the following +// holds: +// - Aliasing `A` is `Exclusive`, in which case `UnsafeCell`s do not need to +// agree +// - `Src::Inner: Immutable` and `Dst::Inner: Immutable`, in which case +// `UnsafeCell`s trivially agree +unsafe impl TryTransmuteFromPtr for Dst +where + Src: Validity + TransmuteFrom, + Dst: Validity, + Src::Inner: Read, + Dst::Inner: Read + CastFrom, + A: Aliasing, +{ +} + +unsafe impl TryTransmuteFromPtr for Dst +where + Src: Validity, + Dst: Validity, + Src::Inner: Immutable, + Dst::Inner: Immutable + CastFrom, +{ +} + +// SAFETY: +// - Forwards transmutation: `Dst: TransmuteFrom` guarantees that +// the set of `DV`-valid `Dst`s is a supserset of the set of `SV`-valid +// `Src`s. +// - Reverse transmutation: `Src: TransmuteFrom` guarantees that +// the set of `DV`-valid `Dst`s is a subset of the set of `SV`-valid `Src`s. +// - `UnsafeCell` agreement guaranteed by `Src: UnsafeCellsAgree + Dst: +// UnsafeCellsAgree`. +unsafe impl TryTransmuteFromPtr for Dst +where + A: Aliasing, + Src: Validity + TransmuteFrom, + Dst: Validity + TransmuteFrom, + Src::Inner: UnsafeCellsAgree, + Dst::Inner: UnsafeCellsAgree + CastFrom, +{ +} + +// SAFETY: +// - Forwards transmutation: `Src: Immutable` guarantees that no mutation of +// `dst`'s referent is possible via `src`. No other references to the same +// referent may exist which are typed using `T: !Immutable`, as this would +// permit violating `Src: Immutable`'s soundness precondition. +// - Reverse transmutation: `Src: TransmuteFrom` guarantees that +// the set of `DV`-valid `Dst`s is a subset of the set of `SV`-valid `Src`s. +// - `UnsafeCell` agreement guaranteed by `Src: Immutable + Dst: Immutable`. +unsafe impl TryTransmuteFromPtr for Dst +where + A: Aliasing, + Src: Validity + TransmuteFrom, + Dst: Validity, + Src::Inner: Immutable, + Dst::Inner: Immutable + CastFrom, +{ +} + +// TODO: Try to delete this impl and see if things still work + +// SAFETY: +// - Forwards transmutation: Because aliasing is `Exclusive`, `dst` is the only +// reference permitted to mutate its referent. +// - Reverse transmutation: `Src: TransmuteFrom` guarantees that +// the set of `DV`-valid `Dst`s is a subset of the set of `SV`-valid `Src`s. +// - `UnsafeCell` agreement is not necessary because aliasing is `Exclusive`. +unsafe impl TryTransmuteFromPtr for Dst +where + Src: Validity + TransmuteFrom, + Dst: Validity, + Dst::Inner: CastFrom, +{ +} + +#[cfg_attr(__ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS, marker)] +pub(crate) unsafe trait TransmuteFromPtr: + TryTransmuteFromPtr + TransmuteFrom +where + Self::Inner: CastFrom, +{ +} + +unsafe impl TransmuteFromPtr for Dst +where + Dst::Inner: CastFrom, + Dst: TransmuteFrom + TryTransmuteFromPtr, +{ +} + +#[marker] +pub(crate) unsafe trait TransmuteFromAlignment {} + +pub(crate) unsafe trait CastFrom { + /// Casts a `*mut T` to a `*mut Self`. + /// + /// # Safety + /// + /// The resulting pointer has the same address and provenance as `ptr`, and + /// addresses the same number of bytes. + fn cast_from(ptr: *mut Src) -> *mut Self; +} + +/// Pairs of types which have `UnsafeCells` covering the same byte ranges. +/// +/// # Safety +/// +/// Let `t: &T` and `u: &U`. Let `len = min(size_of_val(t), size_of_val(u))`. If +/// `U: UnsafeCellsAgree`, then the first `len` bytes of `t` and the first +/// `len` bytes of `u` have `UnsafeCell`s covering the same byte ranges. This +/// condition must hold for all `t: &T` and for all `u: &U`. +/// +/// Note that this safety invariant supports either or both of `T` and `U` being +/// unsized. +pub(crate) unsafe trait UnsafeCellsAgree +where + T: UnsafeCellsAgree, +{ +} + +// SAFETY: `T` has `UnsafeCell`s covering the same byte ranges as itself by +// definition. +unsafe impl UnsafeCellsAgree for T {} + +#[marker] +pub(crate) unsafe trait TransmuteFrom {} + +unsafe impl CastFrom for Src { + fn cast_from(ptr: *mut Src) -> *mut Src { + // SAFETY: `ptr` trivially has the same address as, addresses the same + // number of bytes as, and has the same provenance as `ptr`. + ptr + } +} + +unsafe impl TransmuteFrom for Src {} + +unsafe impl TransmuteFromAlignment for Src {} +unsafe impl + TransmuteFromAlignment for Dst +{ +} + +// TODO: Make sure to clarify that, for unsized types, this specifically refers +// to a cast that does not perform a metadata fix-up operation. +pub(crate) unsafe trait SizeGtEq {} + +unsafe impl SizeGtEq for T {} + +unsafe impl TransmuteFrom> for Valid +where + Dst: FromBytes, + Src: SizeGtEq, +{ +} + +unsafe impl TransmuteFrom> for Valid +where + Src: IntoBytes, + Dst: FromBytes, + Src: SizeGtEq, +{ +} + +unsafe impl TransmuteFrom> for Initialized +where + Src: IntoBytes, + Src: SizeGtEq, +{ +} + +// SAFETY: `MaybeUninit` has `UnsafeCell`s covering the same byte ranges as +// `T`. This is not explicitly documented, but it can be inferred. Per [1] in +// the following safety comment, `MaybeUninit` has the same size as `T`. +// Further, note the signature of `MaybeUninit::assume_init_ref` [1]: +// +// pub unsafe fn assume_init_ref(&self) -> &T +// +// If the argument `&MaybeUninit` and the returned `&T` had `UnsafeCell`s at +// different offsets, this would be unsound. Its existence is proof that this is +// not the case. +// +// [1] https://doc.rust-lang.org/1.81.0/std/mem/union.MaybeUninit.html#method.assume_init_ref +unsafe impl UnsafeCellsAgree for MaybeUninit {} +// SAFETY: See previous safety comment. +unsafe impl UnsafeCellsAgree> for T {} + +unsafe impl TransmuteFrom for Valid> where + Src::Inner: Sized +{ +} +unsafe impl TransmuteFrom for AsInitialized> where + Src::Inner: Sized +{ +} + +unsafe impl TransmuteFrom> for Initialized> {} + +unsafe impl TransmuteFrom>> for Initialized {} + +unsafe impl TransmuteFromAlignment + for MaybeUninit +{ +} +unsafe impl TransmuteFromAlignment, A, A, BecauseUnchanged> + for Src +{ +} + +unsafe impl CastFrom for MaybeUninit { + fn cast_from(ptr: *mut Src) -> *mut MaybeUninit { + // SAFETY: `.cast()` preserves address and provenance. Since + // `MaybeUninit` has the same size as `Src` [1], it also preserves + // size. + // + // [1] Per https://doc.rust-lang.org/std/mem/union.MaybeUninit.html#layout-1: + // + // `MaybeUninit` is guaranteed to have the same size, alignment, + // and ABI as `T`. + ptr.cast() + } +} + +unsafe impl CastFrom> for Src { + fn cast_from(ptr: *mut MaybeUninit) -> *mut Src { + // SAFETY: `.cast()` preserves address and provenance. Since + // `MaybeUninit` has the same size as `Src` [1], it also preserves + // size. + // + // [1] Per https://doc.rust-lang.org/std/mem/union.MaybeUninit.html#layout-1: + // + // `MaybeUninit` is guaranteed to have the same size, alignment, + // and ABI as `T`. + ptr.cast() + } +} + +// SAFETY: Per [1], `ManuallyDrop` has `UnsafeCell`s covering the same byte +// ranges as `Inner = T`. +// +// [1] Per https://doc.rust-lang.org/1.81.0/std/mem/struct.ManuallyDrop.html: +// +// `ManuallyDrop` is guaranteed to have the same layout and bit validity as +// `T`, and is subject to the same layout optimizations as `T`. As a +// consequence, it has no effect on the assumptions that the compiler makes +// about its contents. +unsafe impl UnsafeCellsAgree for ManuallyDrop {} +// SAFETY: See previous safety comment. +unsafe impl UnsafeCellsAgree> for T {} + +unsafe impl TransmuteFrom for Src::WithInner> {} +unsafe impl TransmuteFrom>> for Src {} + +unsafe impl TransmuteFromAlignment + for ManuallyDrop +{ +} +unsafe impl + TransmuteFromAlignment, A, A, BecauseUnchanged> for Src +{ +} + +unsafe impl CastFrom for ManuallyDrop { + fn cast_from(ptr: *mut Src) -> *mut ManuallyDrop { + // SAFETY: An `as` cast preserves address and provenance. Since + // `ManuallyDrop` has the same size as `Src` [1], it also preserves + // size. + // + // [1] Per https://doc.rust-lang.org/nightly/core/mem/struct.ManuallyDrop.html: + // + // `ManuallyDrop` is guaranteed to have the same layout and bit + // validity as `T` + ptr as *mut ManuallyDrop + } +} + +unsafe impl CastFrom> for Src { + fn cast_from(ptr: *mut ManuallyDrop) -> *mut Src { + // SAFETY: An `as` cast preserves address and provenance. Since + // `ManuallyDrop` has the same size as `Src` [1], it also preserves + // size. + // + // [1] Per https://doc.rust-lang.org/nightly/core/mem/struct.ManuallyDrop.html: + // + // `ManuallyDrop` is guaranteed to have the same layout and bit + // validity as `T` + ptr as *mut Src + } +} + +// SAFETY: Per [1], `Wrapping` has the same layout as `T`. Since its single +// field (of type `T`) is public, it would be a breaking change to add or remove +// fields. Thus, we know that `Wrapping` contains a `T` (as opposed to just +// having the same size and alignment as `T`) with no pre- or post-padding. +// Thus, `Wrapping` must have `UnsafeCell`s covering the same byte ranges as +// `Inner = T`. +// +// [1] Per https://doc.rust-lang.org/1.81.0/std/num/struct.Wrapping.html#layout-1: +// +// `Wrapping` is guaranteed to have the same layout and ABI as `T`. +unsafe impl UnsafeCellsAgree for Wrapping {} +// SAFETY: See previous safety comment. +unsafe impl UnsafeCellsAgree> for T {} + +unsafe impl TransmuteFrom for Src::WithInner> where + Src::Inner: Sized +{ +} +unsafe impl TransmuteFrom>> for Src where + Src::Inner: Sized +{ +} + +unsafe impl TransmuteFromAlignment + for Wrapping +{ +} +unsafe impl TransmuteFromAlignment, A, A, BecauseUnchanged> + for Src +{ +} + +unsafe impl CastFrom for Wrapping { + fn cast_from(ptr: *mut Src) -> *mut Wrapping { + // SAFETY: `.cast()` preserves address and provenance. Since + // `Wrapping` has the same size as `Src` [1], it also preserves size. + // + // [1] Per https://doc.rust-lang.org/core/num/struct.Wrapping.html#layout-1: + // + // `Wrapping` is guaranteed to have the same layout and ABI as `T`. + ptr.cast() + } +} + +unsafe impl CastFrom> for Src { + fn cast_from(ptr: *mut Wrapping) -> *mut Src { + // SAFETY: `.cast()` preserves address and provenance. Since + // `Wrapping` has the same size as `Src` [1], it also preserves size. + // + // [1] Per https://doc.rust-lang.org/core/num/struct.Wrapping.html#layout-1: + // + // `Wrapping` is guaranteed to have the same layout and ABI as `T`. + ptr.cast() + } +} + +unsafe impl TransmuteFrom for Src::WithInner> {} +unsafe impl TransmuteFrom>> for Src {} + +unsafe impl TransmuteFromAlignment + for UnsafeCell +{ +} +unsafe impl + TransmuteFromAlignment, A, A, BecauseUnchanged> for Src +{ +} + +unsafe impl CastFrom for UnsafeCell { + fn cast_from(ptr: *mut Src) -> *mut UnsafeCell { + // SAFETY: An `as` cast preserves address and provenance. Since + // `UnsafeCell` has the same size as `Src`, it also preserves size. + // + // [1] Per https://doc.rust-lang.org/1.81.0/core/cell/struct.UnsafeCell.html#memory-layout: + // + // `UnsafeCell` has the same in-memory representation as its inner + // type `T`. + ptr as *mut UnsafeCell + } +} + +unsafe impl CastFrom> for Src { + fn cast_from(ptr: *mut UnsafeCell) -> *mut Src { + // SAFETY: An `as` cast preserves address and provenance. Since + // `UnsafeCell` has the same size as `Src`, it also preserves size. + // + // [1] Per https://doc.rust-lang.org/1.81.0/core/cell/struct.UnsafeCell.html#memory-layout: + // + // `UnsafeCell` has the same in-memory representation as its inner + // type `T`. + ptr as *mut Src + } +} + +// SAFETY: `Unalign` promises to have `UnsafeCell`s covering the same byte +// ranges as `Inner = T`. +unsafe impl UnsafeCellsAgree for Unalign {} +// SAFETY: `Unalign` promises to have `UnsafeCell`s covering the same byte +// ranges as `Inner = T`. +unsafe impl UnsafeCellsAgree> for T {} + +unsafe impl TransmuteFrom for Src::WithInner> where + Src::Inner: Sized +{ +} +unsafe impl TransmuteFrom>> for Src where + Src::Inner: Sized +{ +} + +unsafe impl TransmuteFromAlignment + for Unalign +{ +} +unsafe impl TransmuteFromAlignment, A, Unknown, BecauseUnchanged> + for Src +{ +} + +unsafe impl CastFrom for Unalign { + fn cast_from(ptr: *mut Src) -> *mut Unalign { + // SAFETY: `.cast()` preserves address and provenance. Since + // `Unalign` has the same size as `T`, it also preserves size. + ptr.cast() + } +} + +unsafe impl CastFrom> for Src { + fn cast_from(ptr: *mut Unalign) -> *mut Src { + // SAFETY: `.cast()` preserves address and provenance. Since + // `Unalign` has the same size as `T`, it also preserves size. + ptr.cast() + } +} diff --git a/src/ref.rs b/src/ref.rs index 5a01b01442..153b4e1d3b 100644 --- a/src/ref.rs +++ b/src/ref.rs @@ -624,7 +624,7 @@ where let ptr = Ptr::from_ref(b.into_byte_slice()) .try_cast_into_no_leftover::(None) .expect("zerocopy internal error: into_ref should be infallible"); - let ptr = ptr.bikeshed_recall_valid(); + let ptr = ptr.transmute(); ptr.as_ref() } } @@ -658,7 +658,7 @@ where let ptr = Ptr::from_mut(b.into_byte_slice_mut()) .try_cast_into_no_leftover::(None) .expect("zerocopy internal error: into_ref should be infallible"); - let ptr = ptr.bikeshed_recall_valid(); + let ptr = ptr.bikeshed_recall_valid::<(BecauseRead, BecauseExclusive)>(); ptr.as_mut() } } @@ -770,7 +770,7 @@ where let ptr = Ptr::from_ref(b.deref()) .try_cast_into_no_leftover::(None) .expect("zerocopy internal error: Deref::deref should be infallible"); - let ptr = ptr.bikeshed_recall_valid(); + let ptr = ptr.transmute(); ptr.as_ref() } } @@ -799,7 +799,7 @@ where let ptr = Ptr::from_mut(b.deref_mut()) .try_cast_into_no_leftover::(None) .expect("zerocopy internal error: DerefMut::deref_mut should be infallible"); - let ptr = ptr.bikeshed_recall_valid(); + let ptr = ptr.bikeshed_recall_valid::<(BecauseRead, BecauseExclusive)>(); ptr.as_mut() } } diff --git a/src/util/macro_util.rs b/src/util/macro_util.rs index 4a7af46962..817dc36142 100644 --- a/src/util/macro_util.rs +++ b/src/util/macro_util.rs @@ -25,8 +25,11 @@ use core::mem::{self, ManuallyDrop}; use core::ptr::{self, NonNull}; use crate::{ - pointer::invariant::{self, BecauseExclusive, BecauseImmutable, Invariants, Valid}, - FromBytes, Immutable, IntoBytes, Ptr, TryFromBytes, Unalign, ValidityError, + pointer::{ + invariant::{self, Valid}, + transmute::{CastFrom, SizeGtEq, TransmuteFrom}, + }, + FromBytes, Immutable, IntoBytes, Ptr, TryFromBytes, ValidityError, }; /// Projects the type of the field at `Index` in `Self`. @@ -512,76 +515,6 @@ pub unsafe fn transmute_mut<'dst, 'src: 'dst, Src: 'src, Dst: 'dst>( unsafe { &mut *dst } } -/// Is a given source a valid instance of `Dst`? -/// -/// If so, returns `src` casted to a `Ptr`. Otherwise returns `None`. -/// -/// # Safety -/// -/// Unsafe code may assume that, if `try_cast_or_pme(src)` returns `Some`, -/// `*src` is a bit-valid instance of `Dst`, and that the size of `Src` is -/// greater than or equal to the size of `Dst`. -/// -/// # Panics -/// -/// `try_cast_or_pme` may either produce a post-monomorphization error or a -/// panic if `Dst` not the same size as `Src`. Otherwise, `try_cast_or_pme` -/// panics under the same circumstances as [`is_bit_valid`]. -/// -/// [`is_bit_valid`]: TryFromBytes::is_bit_valid -#[doc(hidden)] -#[inline] -fn try_cast_or_pme( - src: Ptr<'_, Valid, I>, -) -> Result< - Ptr<'_, Valid, (I::Aliasing, invariant::Unknown)>, - ValidityError, I>, Dst>, -> -where - // TODO(#2226): There should be a `Src: FromBytes` bound here, but doing so - // requires deeper surgery. - Src: IntoBytes + invariant::Read, - Dst: TryFromBytes + invariant::Read, - I: Invariants, - I::Aliasing: invariant::Reference, -{ - static_assert!(Src, Dst => mem::size_of::() == mem::size_of::()); - - // SAFETY: This is a pointer cast, satisfying the following properties: - // - `p as *mut Dst` addresses a subset of the `bytes` addressed by `src`, - // because we assert above that the size of `Dst` equal to the size of - // `Src`. - // - `p as *mut Dst` is a provenance-preserving cast - #[allow(clippy::as_conversions)] - let c_ptr = unsafe { src.cast_unsized(|p| p as *mut Dst) }; - - // SAFETY: `c_ptr` is derived from `src` which is `IntoBytes`. By - // invariant on `IntoByte`s, `c_ptr`'s referent consists entirely of - // initialized bytes. - let c_ptr = unsafe { c_ptr.assume_initialized() }; - - match c_ptr.try_into_valid() { - Ok(ptr) => Ok(ptr), - Err(err) => { - // Re-cast `Ptr` to `Ptr`. - let ptr = err.into_src(); - // SAFETY: This is a pointer cast, satisfying the following - // properties: - // - `p as *mut Src` addresses a subset of the `bytes` addressed by - // `ptr`, because we assert above that the size of `Dst` is equal - // to the size of `Src`. - // - `p as *mut Src` is a provenance-preserving cast - #[allow(clippy::as_conversions)] - let ptr = unsafe { ptr.cast_unsized(|p| p as *mut Src) }; - // SAFETY: `ptr` is `src`, and has the same alignment invariant. - let ptr = unsafe { ptr.assume_alignment::() }; - // SAFETY: `ptr` is `src` and has the same validity invariant. - let ptr = unsafe { ptr.assume_validity::() }; - Err(ValidityError::new(ptr.unify_invariants())) - } - } -} - /// Attempts to transmute `Src` into `Dst`. /// /// A helper for `try_transmute!`. @@ -599,19 +532,30 @@ where Src: IntoBytes, Dst: TryFromBytes, { - let mut src = ManuallyDrop::new(src); - let ptr = Ptr::from_mut(&mut src); - // Wrapping `Dst` in `Unalign` ensures that this cast does not fail due to - // alignment requirements. - match try_cast_or_pme::<_, ManuallyDrop>, _, BecauseExclusive>(ptr) { - Ok(ptr) => { - let dst = ptr.bikeshed_recall_aligned().as_mut(); - // SAFETY: By shadowing `dst`, we ensure that `dst` is not re-used - // after taking its inner value. - let dst = unsafe { ManuallyDrop::take(dst) }; - Ok(dst.into_inner()) - } - Err(_) => Err(ValidityError::new(ManuallyDrop::into_inner(src))), + static_assert!(Src, Dst => mem::size_of::() == mem::size_of::()); + + let mu_src = mem::MaybeUninit::new(src); + let mu_src_copy = unsafe { core::ptr::read(&mu_src) }; + // SAFETY: `MaybeUninit` has no validity constraints. + let mut mu_dst: mem::MaybeUninit = + unsafe { crate::util::transmute_unchecked(mu_src_copy) }; + + let ptr = Ptr::from_mut(&mut mu_dst); + + // SAFETY: Since `Src: IntoBytes`, and since `size_of::() == + // size_of::()` by the preceding assertion, all of `mu_dst`'s bytes are + // initialized. + let ptr = unsafe { ptr.assume_validity::() }; + + let ptr = ptr + .transmute::, invariant::Aligned, (crate::BecauseRead, _), _>(); + + if Dst::is_bit_valid(ptr.forget_aligned()) { + Ok(unsafe { mu_dst.assume_init() }) + } else { + // SAFETY: `mu_src` was constructed from `src` and never modified, so it + // is still bit-valid. + Err(ValidityError::new(unsafe { mu_src.assume_init() })) } } @@ -631,17 +575,47 @@ where pub fn try_transmute_ref(src: &Src) -> Result<&Dst, ValidityError<&Src, Dst>> where Src: IntoBytes + Immutable, - Dst: TryFromBytes + Immutable, + Dst: TryFromBytes + Immutable + CastFrom, { - match try_cast_or_pme::(Ptr::from_ref(src)) { + static_assert!(Src, Dst => mem::size_of::() <= mem::size_of::()); + + #[derive(IntoBytes, Immutable)] + #[repr(transparent)] + struct S(Src); + + #[derive(TryFromBytes, Immutable)] + #[repr(transparent)] + struct D(Dst); + + unsafe impl SizeGtEq> for S {} + + unsafe impl CastFrom> for D { + fn cast_from(ptr: *mut S) -> *mut D { + ptr.cast() + } + } + + let src: *const Src = src; + let src: &S = unsafe { &*src.cast() }; + + match Ptr::from_ref(src).try_transmute::>, _, _>() { Ok(ptr) => { static_assert!(Src, Dst => mem::align_of::() <= mem::align_of::()); // SAFETY: We have checked that `Dst` does not have a stricter - // alignment requirement than `Src`. + // alignment requirement than `Src`. Since `S` and `D` are + // `#[repr(transparent)]`, this also implies that `D` does not + // have a stricter alignment requirement than `S`. + // `Ptr::try_transmute` promises to return a pointer which addresses + // the same bytes as its receiver (`Ptr::from_ref(src)`), which was + // guaranteed to be validly-aligned for `S`, and thus its + // return value is validly-aligned for `D`. let ptr = unsafe { ptr.assume_alignment::() }; - Ok(ptr.as_ref()) + Ok(&ptr.as_ref().0) + } + Err(err) => { + let err = err.map_src(Ptr::as_ref).map_src(|src| &src.0); + Err(unsafe { err.with_dst::() }) } - Err(err) => Err(err.map_src(Ptr::as_ref)), } } @@ -661,17 +635,50 @@ where pub fn try_transmute_mut(src: &mut Src) -> Result<&mut Dst, ValidityError<&mut Src, Dst>> where Src: FromBytes + IntoBytes, - Dst: TryFromBytes + IntoBytes, + Dst: TryFromBytes + IntoBytes + CastFrom, { - match try_cast_or_pme::(Ptr::from_mut(src)) { + static_assert!(Src, Dst => mem::size_of::() == mem::size_of::()); + + #[derive(FromBytes, IntoBytes)] + #[repr(transparent)] + struct S(Src); + + #[derive(TryFromBytes, IntoBytes)] + #[repr(transparent)] + struct D(Dst); + + unsafe impl SizeGtEq> for D {} + unsafe impl SizeGtEq> for S {} + + unsafe impl CastFrom> for D { + fn cast_from(ptr: *mut S) -> *mut D { + ptr.cast() + } + } + + let src: *mut Src = src; + let src: &mut S = unsafe { &mut *src.cast() }; + + match Ptr::from_mut(src) + .try_transmute::>, (crate::BecauseRead, _), (crate::BecauseRead, _)>() + { Ok(ptr) => { static_assert!(Src, Dst => mem::align_of::() <= mem::align_of::()); // SAFETY: We have checked that `Dst` does not have a stricter - // alignment requirement than `Src`. + // alignment requirement than `Src`. Since `S` and `D` are + // `#[repr(transparent)]`, this also implies that `D` does not + // have a stricter alignment requirement than `S`. + // `Ptr::try_transmute` promises to return a pointer which addresses + // the same bytes as its receiver (`Ptr::from_ref(src)`), which was + // guaranteed to be validly-aligned for `S`, and thus its + // return value is validly-aligned for `D`. let ptr = unsafe { ptr.assume_alignment::() }; - Ok(ptr.as_mut()) + Ok(&mut ptr.as_mut().0) + } + Err(err) => { + let err = err.map_src(Ptr::as_mut).map_src(|src| &mut src.0); + Err(unsafe { err.with_dst::() }) } - Err(err) => Err(err.map_src(Ptr::as_mut)), } } diff --git a/src/util/macros.rs b/src/util/macros.rs index ef12e7b1ab..9717b8f7a0 100644 --- a/src/util/macros.rs +++ b/src/util/macros.rs @@ -176,7 +176,7 @@ macro_rules! unsafe_impl { #[allow(clippy::as_conversions)] let candidate = unsafe { candidate.cast_unsized_unchecked::<$repr, _>(|p| p as *mut _) }; - let $candidate = candidate.bikeshed_recall_valid(); + let $candidate = candidate.bikeshed_recall_valid::<(BecauseRead, _)>(); $is_bit_valid } };