diff --git a/src/impls.rs b/src/impls.rs index e9c45732f2..4b60069330 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -417,10 +417,12 @@ mod atomics { use super::*; macro_rules! impl_traits_for_atomics { - ($($atomics:ident),* $(,)?) => { + ($($atomics:ident [$primitives:ident]),* $(,)?) => { $( + impl_size_eq!($atomics, $primitives); impl_known_layout!($atomics); - impl_for_transparent_wrapper!(=> TryFromBytes for $atomics); + impl_for_transmute_from!(=> TryFromBytes for $atomics [UnsafeCell<$primitives>]); + // impl_for_transparent_wrapper!(=> TryFromBytes for $atomics); impl_for_transparent_wrapper!(=> FromZeros for $atomics); impl_for_transparent_wrapper!(=> FromBytes for $atomics); impl_for_transparent_wrapper!(=> IntoBytes for $atomics); @@ -435,11 +437,13 @@ mod atomics { use super::*; - impl_traits_for_atomics!(AtomicU8, AtomicI8); + impl_traits_for_atomics!(AtomicU8[u8], AtomicI8[i8]); + impl_size_eq!(AtomicBool, bool); impl_known_layout!(AtomicBool); - impl_for_transparent_wrapper!(=> TryFromBytes for AtomicBool); + // impl_for_transparent_wrapper!(=> TryFromBytes for AtomicBool); + impl_for_transmute_from!(=> TryFromBytes for AtomicBool [UnsafeCell]); impl_for_transparent_wrapper!(=> FromZeros for AtomicBool); impl_for_transparent_wrapper!(=> IntoBytes for AtomicBool); @@ -472,6 +476,7 @@ mod atomics { /// All of these pass an atomic type and that type's native equivalent, as /// required by the macro safety preconditions. unsafe_impl_transparent_wrapper_for_atomic!(AtomicU8 [u8], AtomicI8 [i8], AtomicBool [bool]); + unsafe_impl_transmute_from_for_atomic!(AtomicU8 [u8], AtomicI8 [i8], AtomicBool [bool]); } } @@ -482,13 +487,14 @@ mod atomics { use super::*; - impl_traits_for_atomics!(AtomicU16, AtomicI16); + impl_traits_for_atomics!(AtomicU16[u16], AtomicI16[i16]); safety_comment! { /// SAFETY: /// All of these pass an atomic type and that type's native equivalent, as /// required by the macro safety preconditions. unsafe_impl_transparent_wrapper_for_atomic!(AtomicU16 [u16], AtomicI16 [i16]); + unsafe_impl_transmute_from_for_atomic!(AtomicU16 [u16], AtomicI16 [i16]); } } @@ -499,13 +505,14 @@ mod atomics { use super::*; - impl_traits_for_atomics!(AtomicU32, AtomicI32); + impl_traits_for_atomics!(AtomicU32[u32], AtomicI32[i32]); safety_comment! { /// SAFETY: /// All of these pass an atomic type and that type's native equivalent, as /// required by the macro safety preconditions. unsafe_impl_transparent_wrapper_for_atomic!(AtomicU32 [u32], AtomicI32 [i32]); + unsafe_impl_transmute_from_for_atomic!(AtomicU32 [u32], AtomicI32 [i32]); } } @@ -516,13 +523,14 @@ mod atomics { use super::*; - impl_traits_for_atomics!(AtomicU64, AtomicI64); + impl_traits_for_atomics!(AtomicU64[u64], AtomicI64[i64]); safety_comment! { /// SAFETY: /// All of these pass an atomic type and that type's native equivalent, as /// required by the macro safety preconditions. unsafe_impl_transparent_wrapper_for_atomic!(AtomicU64 [u64], AtomicI64 [i64]); + unsafe_impl_transmute_from_for_atomic!(AtomicU64 [u64], AtomicI64 [i64]); } } @@ -533,13 +541,23 @@ mod atomics { use super::*; - impl_traits_for_atomics!(AtomicUsize, AtomicIsize); + impl_traits_for_atomics!(AtomicUsize[usize], AtomicIsize[isize]); impl_known_layout!(T => AtomicPtr); + // SAFETY: `AtomicPtr` and `*mut T` have the same size [1]. + // + // [1] Per https://doc.rust-lang.org/1.85.0/std/sync/atomic/struct.AtomicPtr.html: + // + // This type has the same size and bit validity as a `*mut T`. + unsafe impl crate::pointer::SizeEq<*mut T> for AtomicPtr {} + // SAFETY: See previous safety comment. + unsafe impl crate::pointer::SizeEq> for *mut T {} + // TODO(#170): Implement `FromBytes` and `IntoBytes` once we implement // those traits for `*mut T`. - impl_for_transparent_wrapper!(T => TryFromBytes for AtomicPtr); + // impl_for_transparent_wrapper!(T => TryFromBytes for AtomicPtr); + impl_for_transmute_from!(T => TryFromBytes for AtomicPtr [UnsafeCell<*mut T>]); impl_for_transparent_wrapper!(T => FromZeros for AtomicPtr); safety_comment! { @@ -548,6 +566,9 @@ mod atomics { /// required by the macro safety preconditions. unsafe_impl_transparent_wrapper_for_atomic!(AtomicUsize [usize], AtomicIsize [isize]); unsafe_impl_transparent_wrapper_for_atomic!(T => AtomicPtr [*mut T]); + + unsafe_impl_transmute_from_for_atomic!(AtomicUsize [usize], AtomicIsize [isize]); + unsafe_impl_transmute_from_for_atomic!(T => AtomicPtr [*mut T]); } } } @@ -578,7 +599,7 @@ safety_comment! { } impl_for_transparent_wrapper!(T: Immutable => Immutable for Wrapping); -impl_for_transparent_wrapper!(T: TryFromBytes => TryFromBytes for Wrapping); +impl_for_transmute_from!(T: TryFromBytes => TryFromBytes for Wrapping[T]); impl_for_transparent_wrapper!(T: FromZeros => FromZeros for Wrapping); impl_for_transparent_wrapper!(T: FromBytes => FromBytes for Wrapping); impl_for_transparent_wrapper!(T: IntoBytes => IntoBytes for Wrapping); @@ -599,7 +620,35 @@ impl_for_transparent_wrapper!(T: Unaligned => Unaligned for CoreMaybeUninit); assert_unaligned!(CoreMaybeUninit<()>, CoreMaybeUninit); impl_for_transparent_wrapper!(T: ?Sized + Immutable => Immutable for ManuallyDrop); -impl_for_transparent_wrapper!(T: ?Sized + TryFromBytes => TryFromBytes for ManuallyDrop); + +unsafe impl TryFromBytes for ManuallyDrop { + #[allow(clippy::missing_inline_in_public_items)] + fn only_derive_is_allowed_to_implement_this_trait() {} + + #[inline(always)] + fn is_bit_valid( + candidate: Maybe<'_, Self, A>, + ) -> bool { + // SAFETY: `ManuallyDrop` and `T` have the same size [1], so this + // cast preserves size. It also preserves provenance. + // + // [1] Per https://doc.rust-lang.org/1.85.0/std/mem/struct.ManuallyDrop.html: + // + // `ManuallyDrop` is guaranteed to have the same layout and bit + // validity as `T` + let c: Maybe<'_, T, A> = unsafe { candidate.cast_unsized(|p| cast!(p => NonNull<_>)) }; + + // SAFETY: `ManuallyDrop` and `T` have the same bit validity [1], so + // this is a sound implementation of `ManuallyDrop::is_bit_valid`. + // + // [1] Per https://doc.rust-lang.org/1.85.0/std/mem/struct.ManuallyDrop.html: + // + // `ManuallyDrop` is guaranteed to have the same layout and bit + // validity as `T` + ::is_bit_valid(c) + } +} + impl_for_transparent_wrapper!(T: ?Sized + FromZeros => FromZeros for ManuallyDrop); impl_for_transparent_wrapper!(T: ?Sized + FromBytes => FromBytes for ManuallyDrop); impl_for_transparent_wrapper!(T: ?Sized + IntoBytes => IntoBytes for ManuallyDrop); diff --git a/src/lib.rs b/src/lib.rs index 71193d661b..a38dcf4c5b 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}, + BecauseFoo, +}; #[cfg(any(feature = "alloc", test))] extern crate alloc; @@ -805,6 +808,14 @@ pub unsafe trait KnownLayout { // resulting size would not fit in a `usize`. meta.size_for_metadata(Self::LAYOUT) } + + fn cast_from_raw + ?Sized>( + ptr: NonNull

, + ) -> NonNull { + let data = ptr.cast::(); + let meta = P::pointer_to_metadata(ptr.as_ptr()); + Self::raw_from_ptr_len(data, meta) + } } /// The metadata associated with a [`KnownLayout`] type. @@ -2843,15 +2854,15 @@ unsafe fn try_read_from( // 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::() }; + let c_ptr = c_ptr.transmute(); // 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 @@ -2861,7 +2872,7 @@ unsafe fn try_read_from( // 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()) { + if !util::SizedKnownLayout::::is_bit_valid(c_ptr.forget_aligned()) { return Err(ValidityError::new(source).into()); } @@ -4258,7 +4269,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 + .bikeshed_recall_valid::<(_, (_, (BecauseExclusive, BecauseExclusive)))>() + .as_mut()), Err(err) => Err(err.map_src(|s| s.as_mut())), } } @@ -4728,7 +4741,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, diff --git a/src/pointer/mod.rs b/src/pointer/mod.rs index d2bc8727ab..dfcf59e7de 100644 --- a/src/pointer/mod.rs +++ b/src/pointer/mod.rs @@ -12,11 +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; +pub use { + invariant::{BecauseExclusive, BecauseImmutable, Read}, + ptr::Ptr, +}; use crate::Unaligned; diff --git a/src/pointer/ptr.rs b/src/pointer/ptr.rs index a3d487ce54..86bb192594 100644 --- a/src/pointer/ptr.rs +++ b/src/pointer/ptr.rs @@ -12,8 +12,12 @@ use core::{ ptr::NonNull, }; -use super::{inner::PtrInner, invariant::*}; use crate::{ + pointer::{ + inner::PtrInner, + invariant::*, + transmute::{Foo, TransmuteFromPtr}, + }, util::{AlignmentVariance, Covariant, TransparentWrapper, ValidityVariance}, AlignmentError, CastError, CastType, KnownLayout, SizeError, TryFromBytes, ValidityError, }; @@ -391,7 +395,7 @@ mod _conversions { { /// Converts `self` to a transparent wrapper type into a `Ptr` to the /// wrapped inner type. - pub(crate) fn transparent_wrapper_into_inner( + fn transparent_wrapper_into_inner( self, ) -> Ptr< 'a, @@ -430,6 +434,26 @@ mod _conversions { where I: Invariants, { + pub fn transmute(self) -> Ptr<'a, U, (I::Aliasing, Unaligned, V)> + where + T: KnownLayout, + V: Validity, + U: TransmuteFromPtr + + KnownLayout + + ?Sized, + { + unsafe { self.transmute_unchecked(|t: NonNull| U::cast_from_raw(t)) } + } + + pub fn transmute_sized(self) -> Ptr<'a, U, (I::Aliasing, Unaligned, V)> + where + T: Sized, + V: Validity, + U: TransmuteFromPtr, + { + unsafe { self.transmute_unchecked(|t: NonNull| cast!(t => NonNull)) } + } + /// Casts to a different (unsized) target type without checking interior /// mutability. /// @@ -460,14 +484,14 @@ mod _conversions { ) -> Ptr<'a, U, (I::Aliasing, Unaligned, V)> where V: Validity, - F: FnOnce(*mut T) -> *mut U, + F: FnOnce(NonNull) -> NonNull, { - let ptr = cast(self.as_inner().as_non_null().as_ptr()); + let ptr = cast(self.as_inner().as_non_null()); - // SAFETY: Caller promises that `cast` returns a pointer whose - // address is in the range of `self.as_inner().as_non_null()`'s referent. By - // invariant, none of these addresses are null. - let ptr = unsafe { NonNull::new_unchecked(ptr) }; + // // SAFETY: Caller promises that `cast` returns a pointer whose + // // address is in the range of `self.as_inner().as_non_null()`'s referent. By + // // invariant, none of these addresses are null. + // let ptr = unsafe { NonNull::new_unchecked(ptr) }; // SAFETY: // @@ -552,7 +576,7 @@ mod _conversions { // validity of the other. let ptr = unsafe { #[allow(clippy::as_conversions)] - self.transmute_unchecked(|p: *mut T| p as *mut crate::Unalign) + self.transmute_unchecked(NonNull::cast::>) }; ptr.bikeshed_recall_aligned() } @@ -561,6 +585,8 @@ mod _conversions { /// State transitions between invariants. mod _transitions { + use crate::pointer::transmute::TryTransmuteFromPtr; + use super::*; impl<'a, T, I> Ptr<'a, T, I> @@ -819,14 +845,11 @@ mod _transitions { #[inline] // TODO(#859): Reconsider the name of this method before making it // public. - pub fn bikeshed_recall_valid(self) -> Ptr<'a, T, (I::Aliasing, I::Alignment, Valid)> + pub fn bikeshed_recall_valid(self) -> Ptr<'a, T, (I::Aliasing, I::Alignment, Valid)> where - T: crate::FromBytes, + T: crate::FromBytes + TryTransmuteFromPtr, I: Invariants, { - // TODO(#1866): Fix this unsoundness. - - // SAFETY: This is unsound! unsafe { self.assume_valid() } } @@ -843,11 +866,13 @@ mod _transitions { /// On error, unsafe code may rely on this method's returned /// `ValidityError` containing `self`. #[inline] - pub(crate) fn try_into_valid( + pub(crate) fn try_into_valid( mut self, ) -> Result, ValidityError> where - T: TryFromBytes + Read, + T: TryFromBytes + + Read + + TryTransmuteFromPtr, I::Aliasing: Reference, I: Invariants, { @@ -855,12 +880,10 @@ mod _transitions { // issues, as we have not generated any invalid state which we need to // fix before returning. if T::is_bit_valid(self.reborrow().forget_aligned()) { - // SAFETY: If `T::is_bit_valid`, code may assume that `self` - // contains a bit-valid instance of `Self`. + // TODO: Complete this safety comment. // - // TODO(#1866): This is unsound! The returned `Ptr` may permit - // writing referents which do not satisfy the `Initialized` - // validity invariant of `self`. + // If `T::is_bit_valid`, code may assume that `self` contains a + // bit-valid instance of `Self`. Ok(unsafe { self.assume_valid() }) } else { Err(ValidityError::new(self)) @@ -902,9 +925,10 @@ mod _casts { /// - `u` has the same provenance as `p` /// - If `I::Aliasing` is [`Shared`], `UnsafeCell`s in `*u` must exist /// at ranges identical to those at which `UnsafeCell`s exist in `*p` + /// TODO: UnsafeCell compatibility #[doc(hidden)] #[inline] - pub unsafe fn cast_unsized_unchecked *mut U>( + pub unsafe fn cast_unsized_unchecked) -> NonNull>( self, cast: F, ) -> Ptr<'a, U, (I::Aliasing, Unaligned, I::Validity)> @@ -940,20 +964,29 @@ mod _casts { /// - `u` has the same provenance as `p` #[doc(hidden)] #[inline] - pub unsafe fn cast_unsized( + pub unsafe fn cast_unsized( self, cast: F, ) -> Ptr<'a, U, (I::Aliasing, Unaligned, I::Validity)> where - T: Read, - U: 'a + ?Sized + Read + CastableFrom, - F: FnOnce(*mut T) -> *mut U, + T: Foo, + U: 'a + ?Sized + CastableFrom, + F: FnOnce(NonNull) -> NonNull, { - // SAFETY: Because `T` and `U` both implement `Read`, - // either: - // - `I::Aliasing` is `Exclusive` - // - `T` and `U` are both `Immutable`, in which case they trivially - // contain `UnsafeCell`s at identical locations + // SAFETY: Because `T: Foo`, one of the following + // holds: + // - `T: Read` and `U: Read`, in which + // case one of the following holds: + // - `I::Aliasing` is `Exclusive` + // - `T` and `U` are both `Immutable` + // - `T` and `U` contain `UnsafeCell`s at identical locations + // + // In the first case, `I::Aliasing` is `Exclusive`, and in the + // second and third case, `T` and `U` contain `UnsafeCell`s at + // identical locations (in the second case, this is because `T` and + // `U` contain no `UnsafeCell`s at all). + // + // TODO: This should also promise UnsafeCell compatibility // // The caller promises all other safety preconditions. unsafe { self.cast_unsized_unchecked(cast) } @@ -988,9 +1021,8 @@ mod _casts { // returned pointer addresses the same bytes as `p` // - `slice_from_raw_parts_mut` and `.cast` both preserve provenance let ptr: Ptr<'a, [u8], _> = unsafe { - self.cast_unsized(|p: *mut T| { - #[allow(clippy::as_conversions)] - core::ptr::slice_from_raw_parts_mut(p.cast::(), bytes) + self.cast_unsized(|p: NonNull| { + core::ptr::NonNull::slice_from_raw_parts(p.cast::(), bytes) }) }; @@ -1214,7 +1246,7 @@ mod _casts { // inner type `T`. A consequence of this guarantee is that it is // possible to convert between `T` and `UnsafeCell`. #[allow(clippy::as_conversions)] - let ptr = unsafe { self.transmute_unchecked(|p| p as *mut T) }; + let ptr = unsafe { self.transmute_unchecked(|p| cast!(p => NonNull)) }; // SAFETY: `UnsafeCell` has the same alignment as `T` [1], // and so if `self` is guaranteed to be aligned, then so is the @@ -1321,10 +1353,12 @@ 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<'_, T, (Shared, Aligned, Initialized)>, ) -> usize { - let t = slf.bikeshed_recall_valid().as_ref(); + let t = slf.bikeshed_recall_valid::().as_ref(); let bytes = { let len = mem::size_of_val(t); diff --git a/src/pointer/transmute.rs b/src/pointer/transmute.rs new file mode 100644 index 0000000000..626a15800c --- /dev/null +++ b/src/pointer/transmute.rs @@ -0,0 +1,195 @@ +// 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}; + +/// # 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 +/// - Interior mutation: TODO (ie, at least one of `Exclusive` or `Immutable` +/// required) +/// +/// ## Proof +/// +/// TODO: Prove that the pre-conditions imply the post-conditions. +pub(crate) unsafe trait TryTransmuteFromPtr +{ +} + +pub(crate) enum BecauseFoo {} + +// 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 + A: Aliasing, + SV: Validity, + DV: Validity, + Src: TransmuteFrom + ?Sized, + Dst: Foo + ?Sized, +{ +} + +unsafe impl TryTransmuteFromPtr for Dst +where + SV: Validity, + DV: Validity, + Src: Immutable + ?Sized, + Dst: Immutable + ?Sized, +{ +} + +/// - T: Read *and* U: Read +/// - `UnsafeCell` agreement *and* something about semantics +pub(crate) unsafe trait Foo {} + +pub(crate) enum BecauseRead {} + +unsafe impl Foo for U +where + T: Read, + U: Read, +{ +} + +pub(crate) enum BecauseUnsafeCellCompatible {} + +// unsafe impl Foo for T {} + +unsafe impl Foo for Wrapping {} + +unsafe impl Foo, A, BecauseUnsafeCellCompatible> for T {} + +unsafe impl Foo for Unalign {} + +unsafe impl Foo, A, BecauseUnsafeCellCompatible> for T {} + +unsafe impl Foo for ManuallyDrop {} + +unsafe impl Foo, A, BecauseUnsafeCellCompatible> for T {} + +pub(crate) unsafe trait TransmuteFromPtr: + TryTransmuteFromPtr + TransmuteFrom +{ +} + +unsafe impl + TransmuteFromPtr for Dst +where + Dst: TransmuteFrom + TryTransmuteFromPtr, +{ +} + +// TODO: What about size equality? +// TODO: What about size equality when the destination type is unsized? +pub(crate) unsafe trait TransmuteFrom {} + +pub(crate) unsafe trait SizeEq {} + +unsafe impl SizeEq for T {} + +unsafe impl TransmuteFrom for Dst +where + Src: IntoBytes + ?Sized, + Dst: ?Sized, + Src: SizeEq, +{ +} + +unsafe impl TransmuteFrom for Dst +where + Src: ?Sized, + Dst: FromBytes + ?Sized, + Src: SizeEq, +{ +} + +// TODO: This seems like a smell - the soundness of this bound has nothing to do +// with `Src` or `Dst` - we're basically just saying `[u8; N]` is transmutable +// into `[u8; N]`. +unsafe impl TransmuteFrom for Dst +where + Src: ?Sized, + Dst: ?Sized, + Src: SizeEq, +{ +} + +// TODO: This seems like a smell - the soundness of this bound has nothing to do +// with `Src` or `Dst` - we're basically just saying `MaybeUninit<[u8; N]>` is +// transmutable into `MaybeUninit<[u8; N]>`. +unsafe impl TransmuteFrom for Dst +where + Src: ?Sized, + Dst: ?Sized, + Src: SizeEq, +{ +} + +unsafe impl TransmuteFrom, Valid, Uninit> for T {} + +unsafe impl TransmuteFrom for MaybeUninit {} + +unsafe impl TransmuteFrom, Valid, Valid> for T {} + +unsafe impl TransmuteFrom for Unalign {} + +unsafe impl SizeEq for Wrapping {} + +unsafe impl SizeEq> for T {} + +// unsafe impl TransmuteFrom, Initialized, Initialized> for T {} + +// unsafe impl TransmuteFrom for Wrapping {} diff --git a/src/ref.rs b/src/ref.rs index 0f4ce00214..e35d8572b2 100644 --- a/src/ref.rs +++ b/src/ref.rs @@ -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::<(_, (_, (BecauseExclusive, BecauseExclusive)))>(); ptr.as_mut() } } diff --git a/src/util/macro_util.rs b/src/util/macro_util.rs index 7db924fb59..5799001399 100644 --- a/src/util/macro_util.rs +++ b/src/util/macro_util.rs @@ -17,15 +17,21 @@ #![allow(missing_debug_implementations)] -use core::mem::{self, ManuallyDrop}; +use core::{ + mem::{self, ManuallyDrop}, + ptr::NonNull, +}; // TODO(#29), TODO(https://github.com/rust-lang/rust/issues/69835): Remove this // `cfg` when `size_of_val_raw` is stabilized. #[cfg(__ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS)] -use core::ptr::{self, NonNull}; +use core::ptr; use crate::{ - pointer::invariant::{self, BecauseExclusive, BecauseImmutable, Invariants}, + pointer::{ + invariant::{self, BecauseExclusive, BecauseImmutable, Invariants}, + TryTransmuteFromPtr, + }, FromBytes, Immutable, IntoBytes, Ptr, TryFromBytes, ValidityError, }; @@ -548,7 +554,7 @@ pub unsafe fn transmute_mut<'dst, 'src: 'dst, Src: 'src, Dst: 'dst>( /// [`is_bit_valid`]: TryFromBytes::is_bit_valid #[doc(hidden)] #[inline] -fn try_cast_or_pme( +fn try_cast_or_pme( src: Ptr<'_, Src, I>, ) -> Result< Ptr<'_, Dst, (I::Aliasing, invariant::Unaligned, invariant::Valid)>, @@ -558,7 +564,9 @@ where // TODO(#2226): There should be a `Src: FromBytes` bound here, but doing so // requires deeper surgery. Src: invariant::Read, - Dst: TryFromBytes + invariant::Read, + Dst: TryFromBytes + + invariant::Read + + TryTransmuteFromPtr, I: Invariants, I::Aliasing: invariant::Reference, { @@ -570,7 +578,7 @@ where // `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) }; + let c_ptr = unsafe { src.cast_unsized(NonNull::cast::) }; match c_ptr.try_into_valid() { Ok(ptr) => Ok(ptr), @@ -584,7 +592,7 @@ where // 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) }; + let ptr = unsafe { ptr.cast_unsized(NonNull::cast::) }; // 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. @@ -637,8 +645,7 @@ where // // `MaybeUninit` is guaranteed to have the same size, alignment, and // ABI as `T` - let ptr: Ptr<'_, Dst, _> = - unsafe { ptr.cast_unsized(|mu: *mut mem::MaybeUninit| mu.cast()) }; + let ptr: Ptr<'_, Dst, _> = unsafe { ptr.cast_unsized(NonNull::>::cast) }; if Dst::is_bit_valid(ptr.forget_aligned()) { // SAFETY: Since `Dst::is_bit_valid`, we know that `ptr`'s referent is @@ -672,7 +679,7 @@ where { let ptr = Ptr::from_ref(src); let ptr = ptr.bikeshed_recall_initialized_immutable(); - match try_cast_or_pme::(ptr) { + match try_cast_or_pme::(ptr) { Ok(ptr) => { static_assert!(Src, Dst => mem::align_of::() <= mem::align_of::()); // SAFETY: We have checked that `Dst` does not have a stricter @@ -716,7 +723,7 @@ where { let ptr = Ptr::from_mut(src); let ptr = ptr.bikeshed_recall_initialized_from_bytes(); - match try_cast_or_pme::(ptr) { + match try_cast_or_pme::(ptr) { Ok(ptr) => { static_assert!(Src, Dst => mem::align_of::() <= mem::align_of::()); // SAFETY: We have checked that `Dst` does not have a stricter diff --git a/src/util/macros.rs b/src/util/macros.rs index c98711926e..455ffb54ae 100644 --- a/src/util/macros.rs +++ b/src/util/macros.rs @@ -174,7 +174,7 @@ macro_rules! unsafe_impl { // - The caller has promised that the destination type has // `UnsafeCell`s at the same byte ranges as the source type. #[allow(clippy::as_conversions)] - let candidate = unsafe { candidate.cast_unsized_unchecked::<$repr, _>(|p| p as *mut _) }; + let candidate = unsafe { candidate.cast_unsized_unchecked::<$repr, _>(|p| cast!(p => NonNull<_>)) }; // TODO(#1866): Currently, `bikeshed_recall_valid` has a known // soundness hole. Eventually this will need to be fixed by @@ -204,7 +204,7 @@ macro_rules! unsafe_impl { // - The caller has promised that the destination type has // `UnsafeCell`s at the same byte ranges as the source type. #[allow(clippy::as_conversions)] - let $candidate = unsafe { candidate.cast_unsized_unchecked::<$repr, _>(|p| p as *mut _) }; + let $candidate = unsafe { candidate.cast_unsized_unchecked::<$repr, _>(|p| cast!(p => NonNull<_>)) }; $is_bit_valid } @@ -225,6 +225,78 @@ macro_rules! unsafe_impl { }; } +macro_rules! unsafe_impl_transmute_from_for_atomic { + ($($atomic:ty [$prim:ty]),*) => { + const _: () = { + use crate::pointer::{TransmuteFrom, invariant::Valid}; + + $( + unsafe impl TransmuteFrom<$atomic, Valid, Valid> for $prim {} + unsafe impl TransmuteFrom<$prim, Valid, Valid> for $atomic {} + )* + }; + }; + ($($tyvar:ident => $atomic:ty [$prim:ty]),*) => { + const _: () = { + use crate::pointer::{TransmuteFrom, invariant::Valid}; + + $( + unsafe impl<$tyvar> TransmuteFrom<$atomic, Valid, Valid> for $prim {} + unsafe impl<$tyvar> TransmuteFrom<$prim, Valid, Valid> for $atomic {} + )* + }; + } +} + +macro_rules! impl_for_transmute_from { + ( + $(#[$attr:meta])* + $($tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )?)? + => TryFromBytes for $ty:ty [UnsafeCell<$repr:ty>] + ) => { + unsafe impl<$($tyvar $(: $(? $optbound +)* $($bound +)*)?)?> crate::TryFromBytes for $ty { + #[allow(clippy::missing_inline_in_public_items)] + fn only_derive_is_allowed_to_implement_this_trait() {} + + #[inline(always)] + fn is_bit_valid(candidate: Maybe<'_, Self, A>) -> bool { + let c: Maybe<'_, Self, crate::pointer::invariant::Exclusive> = candidate.into_exclusive_or_pme(); + let c: Maybe<'_, $repr, _> = c.transmute::<_, _, (_, (_, (BecauseExclusive, BecauseExclusive)))>(); + <$repr as TryFromBytes>::is_bit_valid(c) + } + } + }; + ( + $(#[$attr:meta])* + $($tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )?)? + => TryFromBytes for $ty:ty [$repr:ty] + ) => { + unsafe impl<$($tyvar $(: $(? $optbound +)* $($bound +)*)?)?> crate::TryFromBytes for $ty { + #[allow(clippy::missing_inline_in_public_items)] + fn only_derive_is_allowed_to_implement_this_trait() {} + + + impl_for_transmute_from!(@is_bit_valid $repr: $($($(? $optbound)*)?)?); + + // #[inline(always)] + // fn is_bit_valid(candidate: Maybe<'_, Self, A>) -> bool { + // let c: Maybe<'_, $repr, A> = candidate.transmute(); + // <$repr as TryFromBytes>::is_bit_valid(c) + // } + } + }; + (@is_bit_valid $repr:ty:) => { + #[inline(always)] + fn is_bit_valid(candidate: Maybe<'_, Self, A>) -> bool { + let c: Maybe<'_, $repr, A> = candidate.transmute_sized(); + <$repr as TryFromBytes>::is_bit_valid(c) + } + }; + (@is_bit_valid $repr:ty: ?Sized) => { + compile_error!("impl_for_transmute_from! does not currently support unsized types"); + } +} + /// Implements `$trait` for a type which implements `TransparentWrapper`. /// /// Calling this macro is safe; the internals of the macro emit appropriate @@ -366,7 +438,7 @@ macro_rules! impl_for_transparent_wrapper { // implementation of `is_bit_valid`. #[inline] fn is_bit_valid(candidate: Maybe<'_, Self, A>) -> bool { - TryFromBytes::is_bit_valid(candidate.transparent_wrapper_into_inner()) + <>::Inner as TryFromBytes>::is_bit_valid(candidate.transmute()) } }; ( @@ -803,3 +875,26 @@ macro_rules! static_assert_dst_is_not_zst { }, "cannot call this method on a dynamically-sized type whose trailing slice element is zero-sized"); }} } + +macro_rules! cast { + ($e:expr => NonNull<$t:ty>) => { + // SAFETY: `NonNull::as_ptr` returns a non-null pointer, so the argument + // to `NonNull::new_unchecked` is also non-null. + unsafe { core::ptr::NonNull::new_unchecked(core::ptr::NonNull::as_ptr($e) as *mut $t) } + }; +} + +#[rustfmt::skip] +macro_rules! impl_size_eq { + ($t:ty, $u:ty) => { + const _: () = { + use {crate::pointer::SizeEq, core::mem::size_of}; + static_assert!(=> size_of::<$t>() == size_of::<$u>()); + + // SAFETY: The preceding assert ensures that sizes are equal. + unsafe impl SizeEq<$t> for $u {} + // SAFETY: The preceding assert ensures that sizes are equal. + unsafe impl SizeEq<$u> for $t {} + }; + }; +} diff --git a/src/util/mod.rs b/src/util/mod.rs index e3302aa1e8..79624bf49e 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -20,10 +20,12 @@ use core::{ ptr::NonNull, }; +use zerocopy_derive::TryFromBytes; + use crate::{ error::AlignmentError, pointer::invariant::{self, Invariants}, - Unalign, + KnownLayout, Unalign, }; /// A type which has the same layout as the type it wraps. @@ -59,7 +61,7 @@ pub unsafe trait TransparentWrapper { /// /// The resulting pointer has the same address and provenance as `ptr`, and /// addresses the same number of bytes. - fn cast_into_inner(ptr: *mut Self) -> *mut Self::Inner; + fn cast_into_inner(ptr: NonNull) -> NonNull; /// Casts an inner pointer to a wrapper pointer. /// @@ -67,7 +69,7 @@ pub unsafe trait TransparentWrapper { /// /// The resulting pointer has the same address and provenance as `ptr`, and /// addresses the same number of bytes. - fn cast_from_inner(ptr: *mut Self::Inner) -> *mut Self; + fn cast_from_inner(ptr: NonNull) -> NonNull; } #[allow(unreachable_pub)] @@ -144,7 +146,7 @@ unsafe impl TransparentWrapper for MaybeUninit { type ValidityVariance = Invariant; #[inline(always)] - fn cast_into_inner(ptr: *mut MaybeUninit) -> *mut T { + fn cast_into_inner(ptr: NonNull>) -> NonNull { // SAFETY: Per [1] (from comment above), `MaybeUninit` has the same // layout as `T`. Thus, this cast preserves size. // @@ -153,7 +155,7 @@ unsafe impl TransparentWrapper for MaybeUninit { } #[inline(always)] - fn cast_from_inner(ptr: *mut T) -> *mut MaybeUninit { + fn cast_from_inner(ptr: NonNull) -> NonNull> { // SAFETY: Per [1] (from comment above), `MaybeUninit` has the same // layout as `T`. Thus, this cast preserves size. // @@ -197,23 +199,21 @@ unsafe impl TransparentWrapper for ManuallyDrop type ValidityVariance = Covariant; #[inline(always)] - fn cast_into_inner(ptr: *mut ManuallyDrop) -> *mut T { + fn cast_into_inner(ptr: NonNull>) -> NonNull { // SAFETY: Per [1] (from comment above), `ManuallyDrop` has the same // layout as `T`. Thus, this cast preserves size even if `T` is unsized. // // This cast trivially preserves provenance. - #[allow(clippy::as_conversions)] - return ptr as *mut T; + cast!(ptr => NonNull) } #[inline(always)] - fn cast_from_inner(ptr: *mut T) -> *mut ManuallyDrop { + fn cast_from_inner(ptr: NonNull) -> NonNull> { // SAFETY: Per [1] (from comment above), `ManuallyDrop` has the same // layout as `T`. Thus, this cast preserves size even if `T` is unsized. // // This cast trivially preserves provenance. - #[allow(clippy::as_conversions)] - return ptr as *mut ManuallyDrop; + cast!(ptr => NonNull>) } } @@ -261,7 +261,7 @@ unsafe impl TransparentWrapper for Wrapping { type ValidityVariance = Covariant; #[inline(always)] - fn cast_into_inner(ptr: *mut Wrapping) -> *mut T { + fn cast_into_inner(ptr: NonNull>) -> NonNull { // SAFETY: Per [1] (from comment above), `Wrapping` has the same // layout as `T`. Thus, this cast preserves size. // @@ -270,7 +270,7 @@ unsafe impl TransparentWrapper for Wrapping { } #[inline(always)] - fn cast_from_inner(ptr: *mut T) -> *mut Wrapping { + fn cast_from_inner(ptr: NonNull) -> NonNull> { // SAFETY: Per [1] (from comment above), `Wrapping` has the same // layout as `T`. Thus, this cast preserves size. // @@ -310,23 +310,21 @@ unsafe impl TransparentWrapper for UnsafeCell { type ValidityVariance = Covariant; #[inline(always)] - fn cast_into_inner(ptr: *mut UnsafeCell) -> *mut T { + fn cast_into_inner(ptr: NonNull>) -> NonNull { // SAFETY: Per [1] (from comment above), `UnsafeCell` has the same // representation as `T`. Thus, this cast preserves size. // // This cast trivially preserves provenance. - #[allow(clippy::as_conversions)] - return ptr as *mut T; + cast!(ptr => NonNull) } #[inline(always)] - fn cast_from_inner(ptr: *mut T) -> *mut UnsafeCell { + fn cast_from_inner(ptr: NonNull) -> NonNull> { // SAFETY: Per [1] (from comment above), `UnsafeCell` has the same // representation as `T`. Thus, this cast preserves size. // // This cast trivially preserves provenance. - #[allow(clippy::as_conversions)] - return ptr as *mut UnsafeCell; + cast!(ptr => NonNull>) } } @@ -349,7 +347,7 @@ unsafe impl TransparentWrapper for Unalign { type ValidityVariance = Covariant; #[inline(always)] - fn cast_into_inner(ptr: *mut Unalign) -> *mut T { + fn cast_into_inner(ptr: NonNull>) -> NonNull { // SAFETY: Per the safety comment on the impl block, `Unalign` has // the size as `T`. Thus, this cast preserves size. // @@ -358,7 +356,7 @@ unsafe impl TransparentWrapper for Unalign { } #[inline(always)] - fn cast_from_inner(ptr: *mut T) -> *mut Unalign { + fn cast_from_inner(ptr: NonNull) -> NonNull> { // SAFETY: Per the safety comment on the impl block, `Unalign` has // the size as `T`. Thus, this cast preserves size. // @@ -454,7 +452,7 @@ macro_rules! unsafe_impl_transparent_wrapper_for_atomic { type ValidityVariance = crate::util::Covariant; #[inline(always)] - fn cast_into_inner(ptr: *mut $atomic) -> *mut UnsafeCell<$native> { + fn cast_into_inner(ptr: NonNull<$atomic>) -> NonNull> { // SAFETY: Per [1] (from comment on impl block), `$atomic` has the // same size as `$native`. Thus, this cast preserves size. // @@ -463,7 +461,7 @@ macro_rules! unsafe_impl_transparent_wrapper_for_atomic { } #[inline(always)] - fn cast_from_inner(ptr: *mut UnsafeCell<$native>) -> *mut $atomic { + fn cast_from_inner(ptr: NonNull>) -> NonNull<$atomic> { // SAFETY: Per [1] (from comment on impl block), `$atomic` has the // same size as `$native`. Thus, this cast preserves size. // @@ -904,6 +902,16 @@ pub(crate) mod polyfills { } } +#[derive(KnownLayout, TryFromBytes)] +#[repr(transparent)] +pub(crate) struct SizedKnownLayout(T); + +unsafe impl crate::pointer::SizeEq for SizedKnownLayout {} +unsafe impl crate::pointer::SizeEq> for T {} + +unsafe impl crate::pointer::SizeEq> for SizedKnownLayout {} +unsafe impl crate::pointer::SizeEq> for MaybeUninit {} + #[cfg(test)] pub(crate) mod testutil { use crate::*; diff --git a/src/wrappers.rs b/src/wrappers.rs index fd48236ae4..408db3ed78 100644 --- a/src/wrappers.rs +++ b/src/wrappers.rs @@ -188,7 +188,7 @@ impl Unalign { /// may prefer [`Deref::deref`], which is infallible. #[inline(always)] pub fn try_deref(&self) -> Result<&T, AlignmentError<&Self, T>> { - let inner = Ptr::from_ref(self).transparent_wrapper_into_inner(); + let inner = Ptr::from_ref(self).transmute_sized(); match inner.bikeshed_try_into_aligned() { Ok(aligned) => Ok(aligned.as_ref()), Err(err) => Err(err.map_src(|src| src.into_unalign().as_ref())), @@ -205,7 +205,7 @@ impl Unalign { /// callers may prefer [`DerefMut::deref_mut`], which is infallible. #[inline(always)] pub fn try_deref_mut(&mut self) -> Result<&mut T, AlignmentError<&mut Self, T>> { - let inner = Ptr::from_mut(self).transparent_wrapper_into_inner(); + let inner = Ptr::from_mut(self).transmute_sized::<_, _, (_, (_, _))>(); match inner.bikeshed_try_into_aligned() { Ok(aligned) => Ok(aligned.as_mut()), Err(err) => Err(err.map_src(|src| src.into_unalign().as_mut())), @@ -394,14 +394,17 @@ impl Deref for Unalign { #[inline(always)] fn deref(&self) -> &T { - Ptr::from_ref(self).transparent_wrapper_into_inner().bikeshed_recall_aligned().as_ref() + Ptr::from_ref(self).transmute_sized().bikeshed_recall_aligned().as_ref() } } impl DerefMut for Unalign { #[inline(always)] fn deref_mut(&mut self) -> &mut T { - Ptr::from_mut(self).transparent_wrapper_into_inner().bikeshed_recall_aligned().as_mut() + Ptr::from_mut(self) + .transmute_sized::<_, _, (_, (_, _))>() + .bikeshed_recall_aligned() + .as_mut() } } diff --git a/zerocopy-derive/src/lib.rs b/zerocopy-derive/src/lib.rs index 6da4fcffce..74f78362fd 100644 --- a/zerocopy-derive/src/lib.rs +++ b/zerocopy-derive/src/lib.rs @@ -653,6 +653,8 @@ fn derive_try_from_bytes_struct( where ___ZerocopyAliasing: ::zerocopy::pointer::invariant::Reference, { + use ::zerocopy::util::macro_util::core_reexport; + true #(&& { // SAFETY: // - `project` is a field projection, and so it addresses a @@ -662,8 +664,14 @@ fn derive_try_from_bytes_struct( // the same byte ranges in the returned pointer's referent // as they do in `*slf` let field_candidate = unsafe { - let project = |slf: *mut Self| - ::zerocopy::util::macro_util::core_reexport::ptr::addr_of_mut!((*slf).#field_names); + let project = |slf: core_reexport::ptr::NonNull| { + let slf = slf.as_ptr(); + let field = core_reexport::ptr::addr_of_mut!((*slf).#field_names); + // TODO: Safety comment. Needs precondition that the + // original `slf` pointer doesn't wrap around + // address space. + unsafe { core_reexport::ptr::NonNull::new_unchecked(field) } + }; candidate.reborrow().cast_unsized_unchecked(project) }; @@ -711,6 +719,8 @@ fn derive_try_from_bytes_union( where ___ZerocopyAliasing: ::zerocopy::pointer::invariant::Reference, { + use ::zerocopy::util::macro_util::core_reexport; + false #(|| { // SAFETY: // - `project` is a field projection, and so it addresses a @@ -720,8 +730,14 @@ fn derive_try_from_bytes_union( // `self_type_trait_bounds`, neither `*slf` nor the // returned pointer's referent contain any `UnsafeCell`s let field_candidate = unsafe { - let project = |slf: *mut Self| - ::zerocopy::util::macro_util::core_reexport::ptr::addr_of_mut!((*slf).#field_names); + let project = |slf: core_reexport::ptr::NonNull| { + let slf = slf.as_ptr(); + let field = core_reexport::ptr::addr_of_mut!((*slf).#field_names); + // TODO: Safety comment. Needs precondition that the + // original `slf` pointer doesn't wrap around + // address space. + unsafe { core_reexport::ptr::NonNull::new_unchecked(field) } + }; candidate.reborrow().cast_unsized_unchecked(project) };