diff --git a/src/impls.rs b/src/impls.rs index 0c84a115f11..408d94c86ca 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -290,15 +290,6 @@ safety_comment! { /// because `NonZeroXxx` and `xxx` have the same size. [1] Neither `r` /// nor `t` refer to any `UnsafeCell`s because neither `NonZeroXxx` [2] /// nor `xxx` do. - /// - Since the closure takes a `&xxx` argument, given a `Maybe<'a, - /// NonZeroXxx>` which satisfies the preconditions of - /// `TryFromBytes::::is_bit_valid`, it must be guaranteed - /// that the memory referenced by that `MabyeValid` always contains a - /// valid `xxx`. Since `NonZeroXxx`'s bytes are always initialized [1], - /// `is_bit_valid`'s precondition requires that the same is true of its - /// argument. Since `xxx`'s only bit validity invariant is that its - /// bytes must be initialized, this memory is guaranteed to contain a - /// valid `xxx`. /// - The impl must only return `true` for its argument if the original /// `Maybe` refers to a valid `NonZeroXxx`. The only /// `xxx` which is not also a valid `NonZeroXxx` is 0. [1] diff --git a/src/pointer/invariant.rs b/src/pointer/invariant.rs index f85b1271a31..9c0f52d034c 100644 --- a/src/pointer/invariant.rs +++ b/src/pointer/invariant.rs @@ -65,13 +65,22 @@ pub trait Aliasing: Sealed { /// Aliasing>::Variance<'a, T>` to inherit this variance. #[doc(hidden)] type Variance<'a, T: 'a + ?Sized>; + + #[doc(hidden)] + type MappedTo: Aliasing; } /// The alignment invariant of a [`Ptr`][super::Ptr]. -pub trait Alignment: Sealed {} +pub trait Alignment: Sealed { + #[doc(hidden)] + type MappedTo: Alignment; +} /// The validity invariant of a [`Ptr`][super::Ptr]. -pub trait Validity: Sealed {} +pub trait Validity: Sealed { + #[doc(hidden)] + type MappedTo: Validity; +} /// An [`Aliasing`] invariant which is either [`Shared`] or [`Exclusive`]. /// @@ -96,9 +105,15 @@ impl Aliasing for Any { // // [1] https://doc.rust-lang.org/1.81.0/reference/subtyping.html#variance type Variance<'a, T: 'a + ?Sized> = fn(&'a T) -> &'a T; + + type MappedTo = M::FromAny; +} +impl Alignment for Any { + type MappedTo = M::FromAny; +} +impl Validity for Any { + type MappedTo = M::FromAny; } -impl Alignment for Any {} -impl Validity for Any {} /// The `Ptr<'a, T>` adheres to the aliasing rules of a `&'a T`. /// @@ -112,6 +127,7 @@ pub enum Shared {} impl Aliasing for Shared { const IS_EXCLUSIVE: bool = false; type Variance<'a, T: 'a + ?Sized> = &'a T; + type MappedTo = M::FromShared; } impl Reference for Shared {} @@ -124,51 +140,60 @@ pub enum Exclusive {} impl Aliasing for Exclusive { const IS_EXCLUSIVE: bool = true; type Variance<'a, T: 'a + ?Sized> = &'a mut T; + type MappedTo = M::FromExclusive; } impl Reference for Exclusive {} -/// The referent is aligned: for `Ptr`, the referent's address is a multiple -/// of the `T`'s alignment. +/// The referent is aligned: for `Ptr`, the referent's address is a +/// multiple of the `T`'s alignment. pub enum Aligned {} -impl Alignment for Aligned {} +impl Alignment for Aligned { + type MappedTo = M::FromAligned; +} /// The byte ranges initialized in `T` are also initialized in the referent. /// /// Formally: uninitialized bytes may only be present in `Ptr`'s referent -/// where they are guaranteed to be present in `T`. This is a dynamic -/// property: if, at a particular byte offset, a valid enum discriminant is -/// set, the subsequent bytes may only have uninitialized bytes as -/// specificed by the corresponding enum. +/// where they are guaranteed to be present in `T`. This is a dynamic property: +/// if, at a particular byte offset, a valid enum discriminant is set, the +/// subsequent bytes may only have uninitialized bytes as specificed by the +/// corresponding enum. /// -/// Formally, given `len = size_of_val_raw(ptr)`, at every byte offset, `b`, -/// in the range `[0, len)`: -/// - If, in any instance `t: T` of length `len`, the byte at offset `b` in -/// `t` is initialized, then the byte at offset `b` within `*ptr` must be +/// Formally, given `len = size_of_val_raw(ptr)`, at every byte offset, `b`, in +/// the range `[0, len)`: +/// - If, in any instance `t: T` of length `len`, the byte at offset `b` in `t` +/// is initialized, then the byte at offset `b` within `*ptr` must be /// initialized. -/// - Let `c` be the contents of the byte range `[0, b)` in `*ptr`. Let `S` -/// be the subset of valid instances of `T` of length `len` which contain -/// `c` in the offset range `[0, b)`. If, in any instance of `t: T` in -/// `S`, the byte at offset `b` in `t` is initialized, then the byte at -/// offset `b` in `*ptr` must be initialized. +/// - Let `c` be the contents of the byte range `[0, b)` in `*ptr`. Let `S` be +/// the subset of valid instances of `T` of length `len` which contain `c` in +/// the offset range `[0, b)`. If, in any instance of `t: T` in `S`, the byte +/// at offset `b` in `t` is initialized, then the byte at offset `b` in `*ptr` +/// must be initialized. /// -/// Pragmatically, this means that if `*ptr` is guaranteed to contain an -/// enum type at a particular offset, and the enum discriminant stored in -/// `*ptr` corresponds to a valid variant of that enum type, then it is -/// guaranteed that the appropriate bytes of `*ptr` are initialized as -/// defined by that variant's bit validity (although note that the variant -/// may contain another enum type, in which case the same rules apply -/// depending on the state of its discriminant, and so on recursively). +/// Pragmatically, this means that if `*ptr` is guaranteed to contain an enum +/// type at a particular offset, and the enum discriminant stored in `*ptr` +/// corresponds to a valid variant of that enum type, then it is guaranteed +/// that the appropriate bytes of `*ptr` are initialized as defined by that +/// variant's bit validity (although note that the variant may contain another +/// enum type, in which case the same rules apply depending on the state of +/// its discriminant, and so on recursively). pub enum AsInitialized {} -impl Validity for AsInitialized {} +impl Validity for AsInitialized { + type MappedTo = M::FromAsInitialized; +} /// The byte ranges in the referent are fully initialized. In other words, if /// the referent is `N` bytes long, then it contains a bit-valid `[u8; N]`. pub enum Initialized {} -impl Validity for Initialized {} +impl Validity for Initialized { + type MappedTo = M::FromInitialized; +} /// The referent is bit-valid for `T`. pub enum Valid {} -impl Validity for Valid {} +impl Validity for Valid { + type MappedTo = M::FromValid; +} pub mod aliasing_safety { use super::*; @@ -266,3 +291,76 @@ mod sealed { impl Sealed for super::aliasing_safety::BecauseImmutable {} impl Sealed for (S,) {} } + +pub use mapping::*; +mod mapping { + use super::*; + + pub trait AliasingMapping { + type FromAny: Aliasing; + type FromShared: Aliasing; + type FromExclusive: Aliasing; + } + + pub trait AlignmentMapping { + type FromAny: Alignment; + type FromAligned: Alignment; + } + + pub trait ValidityMapping { + type FromAny: Validity; + type FromAsInitialized: Validity; + type FromInitialized: Validity; + type FromValid: Validity; + } + + #[allow(type_alias_bounds)] + pub type MappedAliasing = I::MappedTo; + + #[allow(type_alias_bounds)] + pub type MappedAlignment = I::MappedTo; + + #[allow(type_alias_bounds)] + pub type MappedValidity = I::MappedTo; + + impl AliasingMapping + for ((Any, FromAny), (Shared, FromShared), (Exclusive, FromExclusive)) + { + type FromAny = FromAny; + type FromShared = FromShared; + type FromExclusive = FromExclusive; + } + + impl AlignmentMapping + for ((Any, FromAny), (Shared, FromAligned)) + { + type FromAny = FromAny; + type FromAligned = FromAligned; + } + + impl< + FromAny: Validity, + FromAsInitialized: Validity, + FromInitialized: Validity, + FromValid: Validity, + > ValidityMapping + for ( + (Any, FromAny), + (AsInitialized, FromAsInitialized), + (Initialized, FromInitialized), + (Valid, FromValid), + ) + { + type FromAny = FromAny; + type FromAsInitialized = FromAsInitialized; + type FromInitialized = FromInitialized; + type FromValid = FromValid; + } + + impl ValidityMapping for (Initialized, FromInitialized) { + type FromAny = Any; + type FromAsInitialized = Any; + type FromInitialized = FromInitialized; + type FromValid = Any; + } +} diff --git a/src/pointer/ptr.rs b/src/pointer/ptr.rs index 8b490284c5a..55f1e2bc0cc 100644 --- a/src/pointer/ptr.rs +++ b/src/pointer/ptr.rs @@ -708,7 +708,8 @@ mod _casts { pub unsafe fn cast_unsized *mut U>( self, cast: F, - ) -> Ptr<'a, U, (I::Aliasing, Any, Any)> { + ) -> Ptr<'a, U, (I::Aliasing, Any, MappedValidity)> + { let ptr = cast(self.as_inner().as_non_null().as_ptr()); // SAFETY: Caller promises that `cast` returns a pointer whose @@ -764,7 +765,13 @@ mod _casts { // not happen. // 7. `ptr`, trivially, conforms to the alignment invariant of // `Any`. - // 8. `ptr`, trivially, conforms to the validity invariant of `Any`. + // 8. If `I::Validity = Any`, `AsInitialized`, or `Valid`, the + // output validity invariant is `Any`. `ptr` trivially conforms + // to this invariant. If `I::Validity = Initialized`, the output + // validity invariant is `Initialized`. Regardless of what subset + // of `self`'s referent is referred to by `ptr`, if all of + // `self`'s referent is initialized, then the same holds of + // `ptr`'s referent. unsafe { Ptr::new(ptr) } } } @@ -807,14 +814,7 @@ mod _casts { }) }; - let ptr = ptr.bikeshed_recall_aligned(); - - // SAFETY: `ptr`'s referent begins as `Initialized`, denoting that - // all bytes of the referent are initialized bytes. The referent - // type is then casted to `[u8]`, whose only validity invariant is - // that its bytes are initialized. This validity invariant is - // satisfied by the `Initialized` invariant on the starting `ptr`. - unsafe { ptr.assume_validity::() } + ptr.bikeshed_recall_aligned().bikeshed_recall_valid() } } @@ -1047,12 +1047,7 @@ mod _project { // SAFETY: This method has the same safety preconditions as // `cast_unsized`. - let ptr = unsafe { self.cast_unsized(projector) }; - - // SAFETY: If all of the bytes of `self` are initialized (as - // promised by `I: Invariants`), then any - // subset of those bytes are also all initialized. - unsafe { ptr.assume_validity::() } + unsafe { self.cast_unsized(projector) } } } diff --git a/src/util/macro_util.rs b/src/util/macro_util.rs index b94e82c5738..bb008c3cb5e 100644 --- a/src/util/macro_util.rs +++ b/src/util/macro_util.rs @@ -424,7 +424,7 @@ macro_rules! assert_align_gt_eq { #[doc(hidden)] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`. #[macro_export] macro_rules! assert_size_eq { - ($t:ident, $u: ident) => {{ + ($t:ident, $u:ident) => {{ // The comments here should be read in the context of this macro's // invocations in `transmute_ref!` and `transmute_mut!`. if false { diff --git a/src/util/macros.rs b/src/util/macros.rs index 32fd7dd69af..84b7d31dfcf 100644 --- a/src/util/macros.rs +++ b/src/util/macros.rs @@ -52,10 +52,6 @@ macro_rules! safety_comment { /// referred to by `t`. /// - `r` refers to an object with `UnsafeCell`s at the same byte ranges as /// the object referred to by `t`. -/// - If the provided closure takes a `&$repr` argument, then given a `Ptr<'a, -/// $ty>` which satisfies the preconditions of -/// `TryFromBytes::<$ty>::is_bit_valid`, it must be guaranteed that the -/// memory referenced by that `Ptr` always contains a valid `$repr`. /// - The impl of `is_bit_valid` must only return `true` for its argument /// `Ptr<$repr>` if the original `Ptr<$ty>` refers to a valid `$ty`. macro_rules! unsafe_impl { @@ -153,9 +149,7 @@ macro_rules! unsafe_impl { #[allow(clippy::as_conversions)] let candidate = unsafe { candidate.cast_unsized::<$repr, _>(|p| p as *mut _) }; - // SAFETY: The caller has promised that the referenced memory region - // will contain a valid `$repr`. - let $candidate = unsafe { candidate.assume_validity::() }; + let $candidate = candidate.bikeshed_recall_valid(); $is_bit_valid } }; @@ -176,12 +170,6 @@ macro_rules! unsafe_impl { // `UnsafeCell`s at the same byte ranges as the source type. #[allow(clippy::as_conversions)] let $candidate = unsafe { candidate.cast_unsized::<$repr, _>(|p| p as *mut _) }; - - // Restore the invariant that the referent bytes are initialized. - // SAFETY: The above cast does not uninitialize any referent bytes; - // they remain initialized. - let $candidate = unsafe { $candidate.assume_validity::() }; - $is_bit_valid } }; diff --git a/zerocopy-derive/src/enum.rs b/zerocopy-derive/src/enum.rs index 7836117231c..dfb747e65e3 100644 --- a/zerocopy-derive/src/enum.rs +++ b/zerocopy-derive/src/enum.rs @@ -265,10 +265,6 @@ pub(crate) fn derive_is_bit_valid( } ) }; - // SAFETY: `cast_unsized` removes the initialization - // invariant from `p`, so we re-assert that all of the bytes - // are initialized. - let variant = unsafe { variant.assume_initialized() }; < #variant_struct_ident #ty_generics as #trait_path >::is_bit_valid(variant) @@ -325,10 +321,6 @@ pub(crate) fn derive_is_bit_valid( p as *mut ___ZerocopyTagPrimitive }) }; - // SAFETY: `tag_ptr` is casted from `candidate`, whose referent - // is `Initialized`. Since we have not written uninitialized - // bytes into the referent, `tag_ptr` is also `Initialized`. - let tag_ptr = unsafe { tag_ptr.assume_initialized() }; tag_ptr.bikeshed_recall_valid().read_unaligned::<::zerocopy::BecauseImmutable>() }; @@ -347,9 +339,6 @@ pub(crate) fn derive_is_bit_valid( p as *mut ___ZerocopyRawEnum #ty_generics }) }; - // SAFETY: `cast_unsized` removes the initialization invariant from - // `p`, so we re-assert that all of the bytes are initialized. - let raw_enum = unsafe { raw_enum.assume_initialized() }; // SAFETY: // - This projection returns a subfield of `this` using // `addr_of_mut!`. diff --git a/zerocopy-derive/src/output_tests.rs b/zerocopy-derive/src/output_tests.rs index 8bf8e5bb138..5e99ba9ed88 100644 --- a/zerocopy-derive/src/output_tests.rs +++ b/zerocopy-derive/src/output_tests.rs @@ -591,13 +591,11 @@ fn test_try_from_bytes_enum() { let tag_ptr = unsafe { candidate.reborrow().cast_unsized(|p: *mut Self| { p as *mut ___ZerocopyTagPrimitive }) }; - let tag_ptr = unsafe { tag_ptr.assume_initialized() }; tag_ptr.bikeshed_recall_valid().read_unaligned::<::zerocopy::BecauseImmutable>() }; let raw_enum = unsafe { candidate.cast_unsized(|p: *mut Self| { p as *mut ___ZerocopyRawEnum<'a, N, X, Y> }) }; - let raw_enum = unsafe { raw_enum.assume_initialized() }; let variants = unsafe { raw_enum.project(|p: *mut ___ZerocopyRawEnum<'a, N, X, Y>| { core_reexport::ptr::addr_of_mut!((*p).variants) @@ -612,7 +610,6 @@ fn test_try_from_bytes_enum() { p as *mut ___ZerocopyVariantStruct_StructLike<'a, N, X, Y> }) }; - let variant = unsafe { variant.assume_initialized() }; <___ZerocopyVariantStruct_StructLike<'a, N, X, Y> as ::zerocopy ::TryFromBytes>::is_bit_valid ( variant) } @@ -622,7 +619,6 @@ fn test_try_from_bytes_enum() { p as *mut ___ZerocopyVariantStruct_TupleLike<'a, N, X, Y> }) }; - let variant = unsafe { variant.assume_initialized() }; <___ZerocopyVariantStruct_TupleLike<'a, N, X, Y> as ::zerocopy ::TryFromBytes>::is_bit_valid ( variant) } @@ -882,13 +878,11 @@ fn test_try_from_bytes_enum() { let tag_ptr = unsafe { candidate.reborrow().cast_unsized(|p: *mut Self| { p as *mut ___ZerocopyTagPrimitive }) }; - let tag_ptr = unsafe { tag_ptr.assume_initialized() }; tag_ptr.bikeshed_recall_valid().read_unaligned::<::zerocopy::BecauseImmutable>() }; let raw_enum = unsafe { candidate.cast_unsized(|p: *mut Self| { p as *mut ___ZerocopyRawEnum<'a, N, X, Y> }) }; - let raw_enum = unsafe { raw_enum.assume_initialized() }; let variants = unsafe { raw_enum.project(|p: *mut ___ZerocopyRawEnum<'a, N, X, Y>| { core_reexport::ptr::addr_of_mut!((*p).variants) @@ -903,7 +897,6 @@ fn test_try_from_bytes_enum() { p as *mut ___ZerocopyVariantStruct_StructLike<'a, N, X, Y> }) }; - let variant = unsafe { variant.assume_initialized() }; <___ZerocopyVariantStruct_StructLike<'a, N, X, Y> as ::zerocopy ::TryFromBytes>::is_bit_valid ( variant) } @@ -913,7 +906,6 @@ fn test_try_from_bytes_enum() { p as *mut ___ZerocopyVariantStruct_TupleLike<'a, N, X, Y> }) }; - let variant = unsafe { variant.assume_initialized() }; <___ZerocopyVariantStruct_TupleLike<'a, N, X, Y> as ::zerocopy ::TryFromBytes>::is_bit_valid ( variant) } @@ -1173,13 +1165,11 @@ fn test_try_from_bytes_enum() { let tag_ptr = unsafe { candidate.reborrow().cast_unsized(|p: *mut Self| { p as *mut ___ZerocopyTagPrimitive }) }; - let tag_ptr = unsafe { tag_ptr.assume_initialized() }; tag_ptr.bikeshed_recall_valid().read_unaligned::<::zerocopy::BecauseImmutable>() }; let raw_enum = unsafe { candidate.cast_unsized(|p: *mut Self| { p as *mut ___ZerocopyRawEnum<'a, N, X, Y> }) }; - let raw_enum = unsafe { raw_enum.assume_initialized() }; let variants = unsafe { raw_enum.project(|p: *mut ___ZerocopyRawEnum<'a, N, X, Y>| { core_reexport::ptr::addr_of_mut!((*p).variants) @@ -1194,7 +1184,6 @@ fn test_try_from_bytes_enum() { p as *mut ___ZerocopyVariantStruct_StructLike<'a, N, X, Y> }) }; - let variant = unsafe { variant.assume_initialized() }; <___ZerocopyVariantStruct_StructLike<'a, N, X, Y> as ::zerocopy ::TryFromBytes>::is_bit_valid ( variant) } @@ -1204,7 +1193,6 @@ fn test_try_from_bytes_enum() { p as *mut ___ZerocopyVariantStruct_TupleLike<'a, N, X, Y> }) }; - let variant = unsafe { variant.assume_initialized() }; <___ZerocopyVariantStruct_TupleLike<'a, N, X, Y> as ::zerocopy ::TryFromBytes>::is_bit_valid ( variant) }