// SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT // 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. #![allow(missing_docs)] use core::{ cell::{Cell, UnsafeCell}, mem::{ManuallyDrop, MaybeUninit}, num::Wrapping, }; use crate::{ pointer::{ cast::{self, CastExact, CastSizedExact}, invariant::*, }, FromBytes, Immutable, IntoBytes, Unalign, }; /// Transmutations which are sound to attempt, conditional on validating the bit /// validity of the destination type. /// /// If a `Ptr` transmutation is `TryTransmuteFromPtr`, then it is sound to /// perform that transmutation so long as some additional mechanism is used to /// validate that the referent is bit-valid for the destination type. That /// validation mechanism could be a type bound (such as `TransmuteFrom`) or a /// runtime validity check. /// /// # Safety /// /// ## Post-conditions /// /// Given `Dst: TryTransmuteFromPtr`, callers may assume /// the following: /// /// Given `src: Ptr<'a, Src, (A, _, SV)>`, if the referent of `src` is /// `DV`-valid for `Dst`, then it is sound to transmute `src` into `dst: Ptr<'a, /// Dst, (A, Unaligned, DV)>` using `C`. /// /// ## Pre-conditions /// /// Given `src: Ptr` and `dst: Ptr`, /// `Dst: TryTransmuteFromPtr` is sound if all of the /// following hold: /// - Forwards transmutation: Either 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 referents of `dst` is a superset of the set of /// `SV`-valid referents of `src` (NOTE: this condition effectively bans /// shrinking or overwriting transmutes, which cannot satisfy this /// condition) /// - Reverse transmutation: Either of the following hold: /// - `dst` does not permit mutation of its referent /// - The set of `DV`-valid referents of `dst` is a subset of the set of /// `SV`-valid referents of `src` (NOTE: this condition effectively bans /// shrinking or overwriting transmutes, which cannot satisfy this /// condition) /// - No safe code, given access to `src` and `dst`, can cause undefined /// behavior: Any of the following hold: /// - `A` is `Exclusive` /// - `Src: Immutable` and `Dst: Immutable` /// - It is sound for shared code to operate on a `&Src` and `&Dst` which /// reference the same byte range at the same time /// /// ## Proof /// /// Given: /// - `src: Ptr<'a, Src, (A, _, SV)>` /// - `src`'s referent is `DV`-valid for `Dst` /// /// We are trying to prove that it is sound to perform a cast from `src` to a /// `dst: Ptr<'a, Dst, (A, Unaligned, DV)>` using `C`. We need to prove that /// such a cast does not violate any of `src`'s invariants, and that it /// satisfies all invariants of the destination `Ptr` type. /// /// First, by `C: CastExact`, `src`'s address is unchanged, so it still satisfies /// its alignment. Since `dst`'s alignment is `Unaligned`, it trivially satisfies /// its alignment. /// /// Second, aliasing is either `Exclusive` or `Shared`: /// - If it is `Exclusive`, then both `src` and `dst` satisfy `Exclusive` /// aliasing trivially: since `src` and `dst` have the same lifetime, `src` is /// inaccessible so long as `dst` is alive, and no other live `Ptr`s or /// references may reference the same referent. /// - If it is `Shared`, then either: /// - `Src: Immutable` and `Dst: Immutable`, and so neither `src` nor `dst` /// permit interior mutation. /// - It is explicitly sound for safe code to operate on a `&Src` and a `&Dst` /// pointing to the same byte range at the same time. /// /// Third, `src`'s validity is satisfied. By invariant, `src`'s referent began /// as an `SV`-valid `Src`. It is guaranteed to remain so, as either of the /// following hold: /// - `dst` does not permit mutation of its referent. /// - The set of `DV`-valid referents of `dst` is a subset of the set of /// `SV`-valid referents of `src`. Thus, any value written via `dst` is /// guaranteed to be an `SV`-valid referent of `src`. /// /// Fourth, `dst`'s validity is satisfied. It is a given of this proof that the /// referent is `DV`-valid for `Dst`. It is guaranteed to remain so, as either /// of the following hold: /// - So long as `dst` is active, no mutation of the referent is allowed except /// via `dst` itself. /// - The set of `DV`-valid referents of `dst` is a superset of the set of /// `SV`-valid referents of `src`. Thus, any value written via `src` is /// guaranteed to be a `DV`-valid referent of `dst`. pub unsafe trait TryTransmuteFromPtr< Src: ?Sized, A: Aliasing, SV: Validity, DV: Validity, C: CastExact, R, > { } #[allow(missing_copy_implementations, missing_debug_implementations)] pub enum BecauseMutationCompatible {} // SAFETY: // - Forwards transmutation: By `Dst: MutationCompatible`, we // know that at least one of the following holds: // - So long as `dst: Ptr` is active, no mutation of its referent is // allowed except via `dst` itself if either of the following hold: // - Aliasing is `Exclusive`, in which case, so long as the `Dst` `Ptr` // exists, no mutation is permitted except via that `Ptr` // - Aliasing is `Shared`, `Src: Immutable`, and `Dst: Immutable`, in which // case no mutation is possible via either `Ptr` // - Since the underlying cast is size-preserving, `dst` addresses the same // referent as `src`. By `Dst: TransmuteFrom`, the set of // `DV`-valid referents of `dst` is a superset of the set of `SV`-valid // referents of `src`. // - Reverse transmutation: Since the underlying cast is size-preserving, `dst` // addresses the same referent as `src`. By `Src: TransmuteFrom`, // the set of `DV`-valid referents of `src` is a subset of the set of // `SV`-valid referents of `dst`. // - No safe code, given access to `src` and `dst`, can cause undefined // behavior: By `Dst: MutationCompatible`, at least one of // the following holds: // - `A` is `Exclusive` // - `Src: Immutable` and `Dst: Immutable` // - `Dst: InvariantsEq`, which guarantees that `Src` and `Dst` have the // same invariants, and permit interior mutation on the same byte ranges unsafe impl TryTransmuteFromPtr for Dst where A: Aliasing, SV: Validity, DV: Validity, Src: TransmuteFrom + ?Sized, Dst: MutationCompatible + ?Sized, C: CastExact, { } // SAFETY: // - Forwards transmutation: Since aliasing is `Shared` and `Src: Immutable`, // `src` does not permit mutation of its referent. // - Reverse transmutation: Since aliasing is `Shared` and `Dst: Immutable`, // `dst` does not permit mutation of its referent. // - No safe code, given access to `src` and `dst`, can cause undefined // behavior: `Src: Immutable` and `Dst: Immutable` unsafe impl TryTransmuteFromPtr for Dst where SV: Validity, DV: Validity, Src: Immutable + ?Sized, Dst: Immutable + ?Sized, C: CastExact, { } /// Denotes that `src: Ptr` and `dst: Ptr`, /// referencing the same referent at the same time, cannot be used by safe code /// to break library safety invariants of `Src` or `Self`. /// /// # Safety /// /// At least one of the following must hold: /// - `Src: Read` and `Self: Read` /// - `Self: InvariantsEq`, and, for some `V`: /// - `Dst: TransmuteFrom` /// - `Src: TransmuteFrom` pub unsafe trait MutationCompatible {} #[allow(missing_copy_implementations, missing_debug_implementations)] pub enum BecauseRead {} // SAFETY: `Src: Read` and `Dst: Read`. unsafe impl MutationCompatible for Dst where Src: Read, Dst: Read, { } /// Denotes that two types have the same invariants. /// /// # Safety /// /// It is sound for safe code to operate on a `&T` and a `&Self` pointing to the /// same referent at the same time - no such safe code can cause undefined /// behavior. pub unsafe trait InvariantsEq {} // SAFETY: Trivially sound to have multiple `&T` pointing to the same referent. unsafe impl InvariantsEq for T {} // SAFETY: `Dst: InvariantsEq + TransmuteFrom`, and `Src: // TransmuteFrom`. unsafe impl MutationCompatible for Dst where Src: TransmuteFrom, Dst: TransmuteFrom + InvariantsEq, { } #[allow(missing_debug_implementations, missing_copy_implementations)] pub enum BecauseInvariantsEq {} macro_rules! unsafe_impl_invariants_eq { ($tyvar:ident => $t:ty, $u:ty) => {{ crate::util::macros::__unsafe(); // SAFETY: The caller promises that this is sound. unsafe impl<$tyvar> InvariantsEq<$t> for $u {} // SAFETY: The caller promises that this is sound. unsafe impl<$tyvar> InvariantsEq<$u> for $t {} }}; } impl_transitive_transmute_from!(T => MaybeUninit => T => Wrapping); impl_transitive_transmute_from!(T => Wrapping => T => MaybeUninit); // SAFETY: `ManuallyDrop` has the same size and bit validity as `T` [1], and // implements `Deref` [2]. Thus, it is already possible for safe // code to obtain a `&T` and a `&ManuallyDrop` to the same referent at the // same time. // // [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` // // [2] https://doc.rust-lang.org/1.81.0/std/mem/struct.ManuallyDrop.html#impl-Deref-for-ManuallyDrop%3CT%3E unsafe impl InvariantsEq for ManuallyDrop {} // SAFETY: See previous safety comment. unsafe impl InvariantsEq> for T {} /// Transmutations which are always sound. /// /// `TransmuteFromPtr` is a shorthand for [`TryTransmuteFromPtr`] and /// [`TransmuteFrom`]. /// /// # Safety /// /// `Dst: TransmuteFromPtr` is equivalent to `Dst: /// TryTransmuteFromPtr + TransmuteFrom`. pub unsafe trait TransmuteFromPtr< Src: ?Sized, A: Aliasing, SV: Validity, DV: Validity, C: CastExact, R, >: TryTransmuteFromPtr + TransmuteFrom { } // SAFETY: The `where` bounds are equivalent to the safety invariant on // `TransmuteFromPtr`. unsafe impl< Src: ?Sized, Dst: ?Sized, A: Aliasing, SV: Validity, DV: Validity, C: CastExact, R, > TransmuteFromPtr for Dst where Dst: TransmuteFrom + TryTransmuteFromPtr, { } /// Denotes that any `SV`-valid `Src` may soundly be transmuted into a /// `DV`-valid `Self`. /// /// # Safety /// /// Given `src: Ptr` and `dst: Ptr`, if the /// referents of `src` and `dst` are the same size, then the set of bit patterns /// allowed to appear in `src`'s referent must be a subset of the set allowed to /// appear in `dst`'s referent. /// /// If the referents are not the same size, then `Dst: TransmuteFrom` conveys no safety guarantee. pub unsafe trait TransmuteFrom {} /// Carries the ability to perform a size-preserving cast or conversion from a /// raw pointer to `Src` to a raw pointer to `Self`. /// /// The cast/conversion is carried by the associated [`CastFrom`] type, and /// may be a no-op cast (without updating pointer metadata) or a conversion /// which updates pointer metadata. /// /// # Safety /// /// `SizeEq` on its own conveys no safety guarantee. Any safety guarantees come /// from the safety invariants on the associated [`CastFrom`] type, specifically /// the [`CastExact`] bound. /// /// [`CastFrom`]: SizeEq::CastFrom /// [`CastExact`]: CastExact pub trait SizeEq { type CastFrom: CastExact; } impl SizeEq for T { type CastFrom = cast::IdCast; } // SAFETY: Since `Src: IntoBytes`, the set of valid `Src`'s is the set of // initialized bit patterns, which is exactly the set allowed in the referent of // any `Initialized` `Ptr`. unsafe impl TransmuteFrom for Dst where Src: IntoBytes + ?Sized, Dst: ?Sized, { } // SAFETY: Since `Dst: FromBytes`, any initialized bit pattern may appear in the // referent of a `Ptr`. This is exactly equal to the set of // bit patterns which may appear in the referent of any `Initialized` `Ptr`. unsafe impl TransmuteFrom for Dst where Src: ?Sized, Dst: FromBytes + ?Sized, { } // FIXME(#2354): 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]`. // SAFETY: The set of allowed bit patterns in the referent of any `Initialized` // `Ptr` is the same regardless of referent type. unsafe impl TransmuteFrom for Dst where Src: ?Sized, Dst: ?Sized, { } // FIXME(#2354): This seems like a smell - the soundness of this bound has // nothing to do with `Dst` - we're basically just saying that any type is // transmutable into `MaybeUninit<[u8; N]>`. // SAFETY: A `Dst` with validity `Uninit` permits any byte sequence, and // therefore can be transmuted from any value. unsafe impl TransmuteFrom for Dst where Src: ?Sized, Dst: ?Sized, V: Validity, { } // SAFETY: // - `ManuallyDrop` has the same size as `T` [1] // - `ManuallyDrop` has the same validity as `T` [1] // // [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` #[allow(clippy::multiple_unsafe_ops_per_block)] const _: () = unsafe { unsafe_impl_for_transparent_wrapper!(pub T: ?Sized => ManuallyDrop) }; // SAFETY: // - `Unalign` promises to have the same size as `T`. // - `Unalign` promises to have the same validity as `T`. #[allow(clippy::multiple_unsafe_ops_per_block)] const _: () = unsafe { unsafe_impl_for_transparent_wrapper!(pub T => Unalign) }; // SAFETY: `Unalign` promises to have the same size and validity as `T`. // Given `u: &Unalign`, it is already possible to obtain `let t = // u.try_deref().unwrap()`. Because `Unalign` has the same size as `T`, the // returned `&T` must point to the same referent as `u`, and thus it must be // sound for these two references to exist at the same time since it's already // possible for safe code to get into this state. #[allow(clippy::multiple_unsafe_ops_per_block)] const _: () = unsafe { unsafe_impl_invariants_eq!(T => T, Unalign) }; // SAFETY: // - `Wrapping` has the same size as `T` [1]. // - `Wrapping` has only one field, which is `pub` [2]. We are also // guaranteed per that `Wrapping` has the same layout as `T` [1]. The only // way for both of these to be true simultaneously is for `Wrapping` to // have the same bit validity as `T`. In particular, in order to change the // bit validity, one of the following would need to happen: // - `Wrapping` could change its `repr`, but this would violate the layout // guarantee. // - `Wrapping` could add or change its fields, but this would be a // stability-breaking change. // // [1] Per https://doc.rust-lang.org/1.85.0/core/num/struct.Wrapping.html#layout-1: // // `Wrapping` is guaranteed to have the same layout and ABI as `T`. // // [2] Definition from https://doc.rust-lang.org/1.85.0/core/num/struct.Wrapping.html: // // ``` // #[repr(transparent)] // pub struct Wrapping(pub T); // ``` #[allow(clippy::multiple_unsafe_ops_per_block)] const _: () = unsafe { unsafe_impl_for_transparent_wrapper!(pub T => Wrapping) }; // SAFETY: By the preceding safety proof, `Wrapping` and `T` have the same // layout and bit validity. Since a `Wrapping`'s `T` field is `pub`, given // `w: &Wrapping`, it's possible to do `let t = &w.t`, which means that it's // already possible for safe code to obtain a `&Wrapping` and a `&T` pointing // to the same referent at the same time. Thus, this must be sound. #[allow(clippy::multiple_unsafe_ops_per_block)] const _: () = unsafe { unsafe_impl_invariants_eq!(T => T, Wrapping) }; // SAFETY: // - `UnsafeCell` has the same size as `T` [1]. // - Per [1], `UnsafeCell` has the same bit validity as `T`. Technically the // term "representation" doesn't guarantee this, but the subsequent sentence // in the documentation makes it clear that this is the intention. // // [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`. A consequence of this guarantee is that it is possible to convert // between `T` and `UnsafeCell`. #[allow(clippy::multiple_unsafe_ops_per_block)] const _: () = unsafe { unsafe_impl_for_transparent_wrapper!(pub T: ?Sized => UnsafeCell) }; // SAFETY: // - `Cell` has the same size as `T` [1]. // - Per [1], `Cell` has the same bit validity as `T`. Technically the term // "representation" doesn't guarantee this, but it does promise to have the // "same memory layout and caveats as `UnsafeCell`." The `UnsafeCell` docs // [2] make it clear that bit validity is the intention even if that phrase // isn't used. // // [1] Per https://doc.rust-lang.org/1.85.0/std/cell/struct.Cell.html#memory-layout: // // `Cell` has the same memory layout and caveats as `UnsafeCell`. In // particular, this means that `Cell` has the same in-memory representation // as its inner type `T`. // // [2] 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`. A consequence of this guarantee is that it is possible to convert // between `T` and `UnsafeCell`. #[allow(clippy::multiple_unsafe_ops_per_block)] const _: () = unsafe { unsafe_impl_for_transparent_wrapper!(pub T: ?Sized => Cell) }; impl_transitive_transmute_from!(T: ?Sized => Cell => T => UnsafeCell); impl_transitive_transmute_from!(T: ?Sized => UnsafeCell => T => Cell); // SAFETY: `MaybeUninit` has no validity requirements. Currently this is not // explicitly guaranteed, but it's obvious from `MaybeUninit`'s documentation // that this is the intention: // https://doc.rust-lang.org/1.85.0/core/mem/union.MaybeUninit.html unsafe impl TransmuteFrom for MaybeUninit {} impl SizeEq for MaybeUninit { type CastFrom = CastSizedExact; } impl SizeEq> for T { type CastFrom = CastSizedExact; } #[cfg(test)] mod tests { use super::*; use crate::pointer::cast::Project as _; fn test_size_eq>(mut src: Src) { let _: *mut Dst = >::CastFrom::project(crate::pointer::PtrInner::from_mut(&mut src)); } #[test] fn test_transmute_coverage() { // SizeEq for MaybeUninit test_size_eq::>(0u8); // SizeEq> for T test_size_eq::, u8>(MaybeUninit::::new(0)); // Transitive: MaybeUninit -> Wrapping // T => MaybeUninit => T => Wrapping test_size_eq::>(0u8); // T => Wrapping => T => MaybeUninit test_size_eq::, MaybeUninit>(Wrapping(0u8)); // T: ?Sized => Cell => T => UnsafeCell test_size_eq::, UnsafeCell>(Cell::new(0u8)); // T: ?Sized => UnsafeCell => T => Cell test_size_eq::, Cell>(UnsafeCell::new(0u8)); } }