From c2f0b928819c4d577a26e3b0b9c81338b4793b16 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Fri, 8 Dec 2023 23:13:05 +0000 Subject: [PATCH] Invariant-parameterize `Ptr` and make `is_bit_valid` safe Closes #715. --- src/lib.rs | 304 ++--- src/macros.rs | 55 +- src/pointer/mod.rs | 78 ++ src/pointer/ptr.rs | 1010 +++++++++++++++++ src/util.rs | 605 ---------- zerocopy-derive/src/lib.rs | 11 +- .../tests/struct_try_from_bytes.rs | 44 +- 7 files changed, 1223 insertions(+), 884 deletions(-) create mode 100644 src/pointer/mod.rs create mode 100644 src/pointer/ptr.rs diff --git a/src/lib.rs b/src/lib.rs index 94b7362cc2..7a9a89de15 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -204,6 +204,7 @@ clippy::unwrap_used, clippy::use_debug )] +#![allow(clippy::type_complexity)] #![deny( rustdoc::bare_urls, rustdoc::broken_intra_doc_links, @@ -249,6 +250,8 @@ mod macros; pub mod byteorder; #[doc(hidden)] pub mod macro_util; +#[doc(hidden)] +pub mod pointer; mod util; // TODO(#252): If we make this pub, come up with a better name. mod wrappers; @@ -304,7 +307,7 @@ use core::alloc::Layout; // Used by `TryFromBytes::is_bit_valid`. #[doc(hidden)] -pub use crate::util::ptr::Ptr; +pub use crate::pointer::{MaybeAligned, MaybeValid, Ptr}; // For each polyfill, as soon as the corresponding feature is stable, the // polyfill import will be unused because method/function resolution will prefer @@ -1216,52 +1219,6 @@ pub unsafe trait TryFromBytes { /// /// # Safety /// - /// ## Preconditions - /// - /// The memory referenced by `candidate` may only be accessed via reads for - /// the duration of this method call. This prohibits writes through mutable - /// references and through [`UnsafeCell`]s. There may exist immutable - /// references to the same memory which contain `UnsafeCell`s so long as: - /// - Those `UnsafeCell`s exist at the same byte ranges as `UnsafeCell`s in - /// `Self`. This is a bidirectional property: `Self` may not contain - /// `UnsafeCell`s where other references to the same memory do not, and - /// vice-versa. - /// - Those `UnsafeCell`s are never used to perform mutation for the - /// duration of this method call. - /// - /// The memory referenced by `candidate` may not be referenced by any - /// mutable references even if these references are not used to perform - /// mutation. - /// - /// `candidate` is not required to refer to a valid `Self`. However, it must - /// satisfy the requirement that uninitialized bytes may only be present - /// where it is possible for them to be present in `Self`. 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(candidate)`, at every byte - /// offset, `b`, in the range `[0, len)`: - /// - If, in all instances `s: Self` of length `len`, the byte at offset `b` - /// in `s` is initialized, then the byte at offset `b` within `*candidate` - /// must be initialized. - /// - Let `c` be the contents of the byte range `[0, b)` in `*candidate`. - /// Let `S` be the subset of valid instances of `Self` of length `len` - /// which contain `c` in the offset range `[0, b)`. If, for all instances - /// of `s: Self` in `S`, the byte at offset `b` in `s` is initialized, - /// then the byte at offset `b` in `*candidate` must be initialized. - /// - /// Pragmatically, this means that if `*candidate` is guaranteed to - /// contain an enum type at a particular offset, and the enum discriminant - /// stored in `*candidate` corresponds to a valid variant of that enum - /// type, then it is guaranteed that the appropriate bytes of `*candidate` - /// 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). - /// - /// ## Postconditions - /// /// Unsafe code may assume that, if `is_bit_valid(candidate)` returns true, /// `*candidate` contains a valid `Self`. /// @@ -1275,7 +1232,7 @@ pub unsafe trait TryFromBytes { /// /// [`UnsafeCell`]: core::cell::UnsafeCell #[doc(hidden)] - unsafe fn is_bit_valid(candidate: Ptr<'_, Self>) -> bool; + fn is_bit_valid(candidate: MaybeValid<'_, Self>) -> bool; /// Attempts to interpret a byte slice as a `Self`. /// @@ -1294,30 +1251,23 @@ pub unsafe trait TryFromBytes { where Self: KnownLayout, { - let maybe_self = Ref::<_, Self>::new_known_layout_name_to_be_bikeshedded(bytes)?; + let candidate = Ptr::from_ref(bytes).try_cast_into_no_leftover::()?; + + // SAFETY: `candidate` has no uninitialized sub-ranges because it + // derived from `bytes: &[u8]`, and is therefore at least as-initialized + // as `Self`. + let candidate = unsafe { candidate.assume_as_initialized() }; - // SAFETY: - // - Since `bytes` is an immutable reference, we know that no mutable - // references exist to this memory region. - // - Since `[u8]` contains no `UnsafeCell`s, we know there are no - // `&UnsafeCell` references to this memory region. - // - Since `TryFromBytes` types may not contain any `UnsafeCell`s, the - // requirement that all references contain `UnsafeCell`s at the same - // offsets is trivially satisfied. - // - All bytes of `bytes` are initialized. - // // 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. - if unsafe { !Self::is_bit_valid(maybe_self.as_ptr()) } { - return None; + if Self::is_bit_valid(candidate.forget_aligned()) { + // SAFETY: If `Self::is_bit_valid`, unsafe code may assume that + // `candidate` contains a bit-valid instance of `Self`. + Some(unsafe { candidate.assume_valid() }.as_ref()) + } else { + None } - - // SAFETY: - // - `is_bit_valid` guarantees that `*maybe_self` contains a valid - // `Self`. - // - `TryFromBytes` types may not contain any `UnsafeCell`s. - Some(unsafe { maybe_self.into_ref_unchecked() }) } } @@ -3123,19 +3073,18 @@ safety_comment! { /// - Given `t: *mut bool` and `let r = *mut u8`, `r` refers to an object /// of the same size as that referred to by `t`. This is true because /// `bool` and `u8` have the same size (1 byte) [1]. - /// - Since the closure takes a `&u8` argument, given a `Ptr<'a, bool>` - /// which satisfies the preconditions of + /// - Since the closure takes a `&u8` argument, given a `MaybeValid<'a, + /// bool>` which satisfies the preconditions of /// `TryFromBytes::::is_bit_valid`, it must be guaranteed that the - /// memory referenced by that `Ptr` always contains a valid `u8`. Since - /// `bool`'s single byte is always initialized, `is_bit_valid`'s + /// memory referenced by that `MaybeValid` always contains a valid `u8`. + /// Since `bool`'s single byte is always initialized, `is_bit_valid`'s /// precondition requires that the same is true of its argument. Since /// `u8`'s only bit validity invariant is that its single byte must be /// initialized, this memory is guaranteed to contain a valid `u8`. - /// - The alignment of `bool` is equal to the alignment of `u8`. [1] [2] /// - The impl must only return `true` for its argument if the original - /// `Ptr` refers to a valid `bool`. We only return true if the - /// `u8` value is 0 or 1, and both of these are valid values for `bool`. - /// [3] + /// `MaybeValid` refers to a valid `bool`. We only return true if + /// the `u8` value is 0 or 1, and both of these are valid values for + /// `bool`. [3] /// /// [1] Per https://doc.rust-lang.org/reference/type-layout.html#primitive-data-layout: /// @@ -3154,7 +3103,7 @@ safety_comment! { /// /// The value false has the bit pattern 0x00 and the value true has the /// bit pattern 0x01. - unsafe_impl!(bool: TryFromBytes; |byte: &u8| *byte < 2); + unsafe_impl!(bool: TryFromBytes; |byte: MaybeAligned| *byte.as_ref() < 2); } safety_comment! { /// SAFETY: @@ -3175,18 +3124,19 @@ safety_comment! { /// - Given `t: *mut char` and `let r = *mut u32`, `r` refers to an object /// of the same size as that referred to by `t`. This is true because /// `char` and `u32` have the same size [1]. - /// - Since the closure takes a `&u32` argument, given a `Ptr<'a, char>` - /// which satisfies the preconditions of + /// - Since the closure takes a `&u32` argument, given a `MaybeValid<'a, + /// char>` which satisfies the preconditions of /// `TryFromBytes::::is_bit_valid`, it must be guaranteed that the - /// memory referenced by that `Ptr` always contains a valid `u32`. Since - /// `char`'s bytes are always initialized [2], `is_bit_valid`'s - /// precondition requires that the same is true of its argument. Since - /// `u32`'s only bit validity invariant is that its bytes must be - /// initialized, this memory is guaranteed to contain a valid `u32`. - /// - The alignment of `char` is equal to the alignment of `u32`. [1] + /// memory referenced by that `MaybeValid` always contains a valid + /// `u32`. Since `char`'s bytes are always initialized [2], + /// `is_bit_valid`'s precondition requires that the same is true of its + /// argument. Since `u32`'s only bit validity invariant is that its + /// bytes must be initialized, this memory is guaranteed to contain a + /// valid `u32`. /// - The impl must only return `true` for its argument if the original - /// `Ptr` refers to a valid `char`. `char::from_u32` guarantees - /// that it returns `None` if its input is not a valid `char`. [3] + /// `MaybeValid` refers to a valid `char`. `char::from_u32` + /// guarantees that it returns `None` if its input is not a valid + /// `char`. [3] /// /// [1] Per https://doc.rust-lang.org/nightly/reference/types/textual.html#layout-and-bit-validity: /// @@ -3201,7 +3151,10 @@ safety_comment! { /// /// `from_u32()` will return `None` if the input is not a valid value for /// a `char`. - unsafe_impl!(char: TryFromBytes; |candidate: &u32| char::from_u32(*candidate).is_some()); + unsafe_impl!(char: TryFromBytes; |candidate: MaybeAligned| { + let candidate = candidate.read_unaligned(); + char::from_u32(candidate).is_some() + }); } safety_comment! { /// SAFETY: @@ -3226,18 +3179,19 @@ safety_comment! { /// - Given `t: *mut str` and `let r = *mut [u8]`, `r` refers to an object /// of the same size as that referred to by `t`. This is true because /// `str` and `[u8]` have the same representation. [1] - /// - Since the closure takes a `&[u8]` argument, given a `Ptr<'a, str>` - /// which satisfies the preconditions of + /// - Since the closure takes a `&[u8]` argument, given a `MaybeValid<'a, + /// str>` which satisfies the preconditions of /// `TryFromBytes::::is_bit_valid`, it must be guaranteed that the - /// memory referenced by that `Ptr` always contains a valid `[u8]`. - /// Since `str`'s bytes are always initialized [1], `is_bit_valid`'s - /// precondition requires that the same is true of its argument. Since - /// `[u8]`'s only bit validity invariant is that its bytes must be - /// initialized, this memory is guaranteed to contain a valid `[u8]`. - /// - The alignment of `str` is equal to the alignment of `[u8]`. [1] + /// memory referenced by that `MaybeValid` always contains a valid + /// `[u8]`. Since `str`'s bytes are always initialized [1], + /// `is_bit_valid`'s precondition requires that the same is true of its + /// argument. Since `[u8]`'s only bit validity invariant is that its + /// bytes must be initialized, this memory is guaranteed to contain a + /// valid `[u8]`. /// - The impl must only return `true` for its argument if the original - /// `Ptr` refers to a valid `str`. `str::from_utf8` guarantees that - /// it returns `Err` if its input is not a valid `str`. [2] + /// `MaybeValid` refers to a valid `str`. `str::from_utf8` + /// guarantees that it returns `Err` if its input is not a valid `str`. + /// [2] /// /// [1] Per https://doc.rust-lang.org/reference/types/textual.html: /// @@ -3246,7 +3200,10 @@ safety_comment! { /// [2] Per https://doc.rust-lang.org/core/str/fn.from_utf8.html#errors: /// /// Returns `Err` if the slice is not UTF-8. - unsafe_impl!(str: TryFromBytes; |candidate: &[u8]| core::str::from_utf8(candidate).is_ok()); + unsafe_impl!(str: TryFromBytes; |candidate: MaybeAligned<[u8]>| { + let candidate = candidate.as_ref(); + core::str::from_utf8(candidate).is_ok() + }); } safety_comment! { @@ -3297,37 +3254,35 @@ safety_comment! { /// - Given `t: *mut NonZeroXxx` and `let r = *mut xxx`, `r` refers to an /// object of the same size as that referred to by `t`. This is true /// because `NonZeroXxx` and `xxx` have the same size. [1] - /// - Since the closure takes a `&xxx` argument, given a `Ptr<'a, + /// - Since the closure takes a `&xxx` argument, given a `MaybeValid<'a, /// NonZeroXxx>` which satisfies the preconditions of /// `TryFromBytes::::is_bit_valid`, it must be guaranteed - /// that the memory referenced by that `Ptr` always contains a valid - /// `xxx`. Since `NonZeroXxx`'s bytes are always initialized [1], + /// 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 alignment of `NonZeroXxx` is equal to the alignment of `xxx`. - /// [1] /// - The impl must only return `true` for its argument if the original - /// `Ptr` refers to a valid `NonZeroXxx`. The only `xxx` - /// which is not also a valid `NonZeroXxx` is 0. [1] + /// `MaybeValid` refers to a valid `NonZeroXxx`. The only + /// `xxx` which is not also a valid `NonZeroXxx` is 0. [1] /// /// [1] Per https://doc.rust-lang.org/core/num/struct.NonZeroU16.html: /// /// `NonZeroU16` is guaranteed to have the same layout and bit validity as /// `u16` with the exception that `0` is not a valid instance. - unsafe_impl!(NonZeroU8: TryFromBytes; |n: &u8| *n != 0); - unsafe_impl!(NonZeroI8: TryFromBytes; |n: &i8| *n != 0); - unsafe_impl!(NonZeroU16: TryFromBytes; |n: &u16| *n != 0); - unsafe_impl!(NonZeroI16: TryFromBytes; |n: &i16| *n != 0); - unsafe_impl!(NonZeroU32: TryFromBytes; |n: &u32| *n != 0); - unsafe_impl!(NonZeroI32: TryFromBytes; |n: &i32| *n != 0); - unsafe_impl!(NonZeroU64: TryFromBytes; |n: &u64| *n != 0); - unsafe_impl!(NonZeroI64: TryFromBytes; |n: &i64| *n != 0); - unsafe_impl!(NonZeroU128: TryFromBytes; |n: &u128| *n != 0); - unsafe_impl!(NonZeroI128: TryFromBytes; |n: &i128| *n != 0); - unsafe_impl!(NonZeroUsize: TryFromBytes; |n: &usize| *n != 0); - unsafe_impl!(NonZeroIsize: TryFromBytes; |n: &isize| *n != 0); + unsafe_impl!(NonZeroU8: TryFromBytes; |n: MaybeAligned| NonZeroU8::new(n.read_unaligned()).is_some()); + unsafe_impl!(NonZeroI8: TryFromBytes; |n: MaybeAligned| NonZeroI8::new(n.read_unaligned()).is_some()); + unsafe_impl!(NonZeroU16: TryFromBytes; |n: MaybeAligned| NonZeroU16::new(n.read_unaligned()).is_some()); + unsafe_impl!(NonZeroI16: TryFromBytes; |n: MaybeAligned| NonZeroI16::new(n.read_unaligned()).is_some()); + unsafe_impl!(NonZeroU32: TryFromBytes; |n: MaybeAligned| NonZeroU32::new(n.read_unaligned()).is_some()); + unsafe_impl!(NonZeroI32: TryFromBytes; |n: MaybeAligned| NonZeroI32::new(n.read_unaligned()).is_some()); + unsafe_impl!(NonZeroU64: TryFromBytes; |n: MaybeAligned| NonZeroU64::new(n.read_unaligned()).is_some()); + unsafe_impl!(NonZeroI64: TryFromBytes; |n: MaybeAligned| NonZeroI64::new(n.read_unaligned()).is_some()); + unsafe_impl!(NonZeroU128: TryFromBytes; |n: MaybeAligned| NonZeroU128::new(n.read_unaligned()).is_some()); + unsafe_impl!(NonZeroI128: TryFromBytes; |n: MaybeAligned| NonZeroI128::new(n.read_unaligned()).is_some()); + unsafe_impl!(NonZeroUsize: TryFromBytes; |n: MaybeAligned| NonZeroUsize::new(n.read_unaligned()).is_some()); + unsafe_impl!(NonZeroIsize: TryFromBytes; |n: MaybeAligned| NonZeroIsize::new(n.read_unaligned()).is_some()); } safety_comment! { /// SAFETY: @@ -3446,7 +3401,7 @@ safety_comment! { /// because `Wrapping` and `T` have the same layout /// - The alignment of `Wrapping` is equal to the alignment of `T`. /// - The impl must only return `true` for its argument if the original - /// `Ptr>` refers to a valid `Wrapping`. Since + /// `MaybeValid>` refers to a valid `MaybeValid`. Since /// `Wrapping` has the same bit validity as `T`, and since our impl /// just calls `T::is_bit_valid`, our impl returns `true` exactly when /// its argument contains a valid `Wrapping`. @@ -3473,17 +3428,8 @@ safety_comment! { /// /// [2] https://doc.rust-lang.org/nomicon/other-reprs.html#reprtransparent unsafe_impl!(T: NoCell => NoCell for Wrapping); - unsafe_impl!(T: TryFromBytes => TryFromBytes for Wrapping; |candidate: Ptr| { - // SAFETY: - // - Since `T` and `Wrapping` have the same layout and bit validity - // and contain the same fields, `T` contains `UnsafeCell`s exactly - // where `Wrapping` does. Thus, all memory and `UnsafeCell` - // preconditions of `T::is_bit_valid` hold exactly when the same - // preconditions for `Wrapping::is_bit_valid` hold. - // - By the same token, since `candidate` is guaranteed to have its - // bytes initialized where there are always initialized bytes in - // `Wrapping`, the same is true for `T`. - unsafe { T::is_bit_valid(candidate) } + unsafe_impl!(T: TryFromBytes => TryFromBytes for Wrapping; |candidate: MaybeValid| { + T::is_bit_valid(candidate) }); unsafe_impl!(T: FromZeros => FromZeros for Wrapping); unsafe_impl!(T: FromBytes => FromBytes for Wrapping); @@ -3600,11 +3546,8 @@ safety_comment! { unsafe_impl!(const N: usize, T: Unaligned => Unaligned for [T; N]); assert_unaligned!([(); 0], [(); 1], [u8; 0], [u8; 1]); unsafe_impl!(T: NoCell => NoCell for [T]); - unsafe_impl!(T: TryFromBytes => TryFromBytes for [T]; |c: Ptr<[T]>| { - // SAFETY: Assuming the preconditions of `is_bit_valid` are satisfied, - // so too will the postcondition: that, if `is_bit_valid(candidate)` - // returns true, `*candidate` contains a valid `Self`. Per the reference - // [1]: + unsafe_impl!(T: TryFromBytes => TryFromBytes for [T]; |c: MaybeValid<[T]>| { + // SAFETY: Per the reference [1]: // // An array of `[T; N]` has a size of `size_of::() * N` and the // same alignment of `T`. Arrays are laid out so that the zero-based @@ -3624,17 +3567,7 @@ safety_comment! { // not panic (in fact, it explicitly warns that it's a possibility), and // we have not violated any safety invariants that we must fix before // returning. - c.iter().all(|elem| - // SAFETY: We uphold the safety contract of `is_bit_valid(elem)`, by - // precondition on the surrounding call to `is_bit_valid`. The - // memory referenced by `elem` is contained entirely within `c`, and - // satisfies the preconditions satisfied by `c`. By axiom, we assume - // that `Iterator:all` does not invalidate these preconditions - // (e.g., by writing to `elem`.) Since `elem` is derived from `c`, - // it is only possible for uninitialized bytes to occur in `elem` at - // the same bytes they occur within `c`. - unsafe { ::is_bit_valid(elem) } - ) + c.iter().all(::is_bit_valid) }); unsafe_impl!(T: FromZeros => FromZeros for [T]); unsafe_impl!(T: FromBytes => FromBytes for [T]); @@ -4266,20 +4199,6 @@ pub struct Ref( #[doc(hidden)] pub use Ref as LayoutVerified; -impl Ref -where - B: ByteSlice, - T: ?Sized + KnownLayout, -{ - fn new_known_layout_name_to_be_bikeshedded(bytes: B) -> Option> { - let _ = Ptr::from(bytes.deref()).try_cast_into_no_leftover::()?; - // INVARIANTS: `Ptr::try_cast_into_no_leftover` returns `None` if - // `bytes.deref()` is not properly aligned for `T` or doesn't have a - // valid size for `T`. - Some(Ref(bytes, PhantomData)) - } -} - impl Ref where B: ByteSlice, @@ -4873,61 +4792,6 @@ where } } -impl<'a, T> Ref<&'a [u8], T> -where - T: 'a + ?Sized + KnownLayout, -{ - fn as_ptr(&self) -> Ptr<'a, T> { - // PANICS: By invariant, `self.0` satisfies `T`'s alignment and is a - // valid size for `T`, which ensures that `try_cast_into_no_leftover` - // will not panic. - Ptr::from(self.0) - .try_cast_into_no_leftover::() - .expect("Ref::deref_unchecked: internal error (pointer cast should never fail)") - } - - /// Converts `self` into an immutable `T` reference without validating bit - /// validity. - /// - /// # Safety - /// - /// The caller must ensure that the referent contains a valid `T` before - /// calling this method. - /// - /// The caller must also ensure that `T` does not contain any - /// [`UnsafeCell`]s. - /// - /// [`UnsafeCell`]: core::cell::UnsafeCell - // TODO(#251): Once `NoCell` is stabilized, require `T: NoCell` and remove - // the "no `UnsafeCell`" safety precondition. - unsafe fn into_ref_unchecked(self) -> &'a T { - // SAFETY: - // - Preconditions for `as_ref`: - // - Caller has promised that `*ptr` contains a valid `T`. Since the - // only other references to this memory may be typed as `&T` or - // `&[u8]`, we know that, during `'a`, `*ptr` will never be updated - // to contain an invalid `T`: The `&T` cannot be used to store an - // invalid `T` and, since `&[u8]` does not permit interior - // mutability, the `&[u8]` cannot be used to update the contents at - // all. - // - Since the `self` and the returned `&T` both have the lifetime - // `'a`, Rust will prevent any `&mut Ref`s from being produced - // which refer to `self` during `'a`. The only APIs that permit - // constructing a mutable reference to the same memory as the - // returned `&T` operate on `&mut Ref`, so no mutable - // references can be constructed to this memory during `'a`. - // - The caller has promised that `T` contains no `UnsafeCell`s. We - // know that `[u8]` cannot contain any `UnsafeCell`s. Thus, there is - // no mismatch in which byte ranges can be viewed as `UnsafeCell`s. - // - Since the caller has promised that `T` contains no `UnsafeCell`s, - // there's no way for the returned reference to be used to modify the - // byte range, and thus there's no way for the returned reference to - // be used to write an invalid `[u8]` which would be observable via - // the original `&[u8]`. - unsafe { self.as_ptr().as_ref() } - } -} - impl Ref where B: ByteSlice, @@ -7952,16 +7816,8 @@ mod tests { macro_rules! assert_impls { ($ty:ty: TryFromBytes) => { <$ty as TryFromBytesTestable>::with_passing_test_cases(|val| { - let c = Ptr::from(val); - // SAFETY: - // - Since `val` is a normal reference, `c` is guranteed to - // be aligned, to point to a single allocation, and to - // have a size which doesn't overflow `isize`. - // - Since `val` is a valid `$ty`, `c`'s referent satisfies - // the bit validity constraints of `is_bit_valid`, which - // are a superset of the bit validity constraints of - // `$ty`. - let res = unsafe { <$ty as TryFromBytes>::is_bit_valid(c) }; + let c = Ptr::from_ref(val).forget_valid().forget_aligned(); + let res = <$ty as TryFromBytes>::is_bit_valid(c); assert!(res, "{}::is_bit_valid({:?}): got false, expected true", stringify!($ty), val); // TODO(#5): In addition to testing `is_bit_valid`, test the diff --git a/src/macros.rs b/src/macros.rs index c3d473e7dc..b0ebfa9cc7 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -52,16 +52,14 @@ macro_rules! safety_comment { /// $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 alignment of `$repr` is less than or equal to the alignment of -/// `$ty`. /// - 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 { // Implement `$trait` for `$ty` with no bounds. - ($(#[$attr:meta])* $ty:ty: $trait:ident $(; |$candidate:ident: &$repr:ty| $is_bit_valid:expr)?) => { + ($(#[$attr:meta])* $ty:ty: $trait:ident $(; |$candidate:ident: MaybeAligned<$repr:ty>| $is_bit_valid:expr)?) => { $(#[$attr])* unsafe impl $trait for $ty { - unsafe_impl!(@method $trait $(; |$candidate: &$repr| $is_bit_valid)?); + unsafe_impl!(@method $trait $(; |$candidate: MaybeAligned<$repr>| $is_bit_valid)?); } }; // Implement all `$traits` for `$ty` with no bounds. @@ -97,26 +95,26 @@ macro_rules! unsafe_impl { $(#[$attr:meta])* const $constname:ident : $constty:ident $(,)? $($tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )?),* - => $trait:ident for $ty:ty $(; |$candidate:ident $(: &$ref_repr:ty)? $(: Ptr<$ptr_repr:ty>)?| $is_bit_valid:expr)? + => $trait:ident for $ty:ty $(; |$candidate:ident $(: MaybeAligned<$ref_repr:ty>)? $(: MaybeValid<$ptr_repr:ty>)?| $is_bit_valid:expr)? ) => { unsafe_impl!( @inner $(#[$attr])* @const $constname: $constty, $($tyvar $(: $(? $optbound +)* + $($bound +)*)?,)* - => $trait for $ty $(; |$candidate $(: &$ref_repr)? $(: Ptr<$ptr_repr>)?| $is_bit_valid)? + => $trait for $ty $(; |$candidate $(: MaybeAligned<$ref_repr>)? $(: MaybeValid<$ptr_repr>)?| $is_bit_valid)? ); }; ( $(#[$attr:meta])* $($tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )?),* - => $trait:ident for $ty:ty $(; |$candidate:ident $(: &$ref_repr:ty)? $(: Ptr<$ptr_repr:ty>)?| $is_bit_valid:expr)? + => $trait:ident for $ty:ty $(; |$candidate:ident $(: MaybeAligned<$ref_repr:ty>)? $(: MaybeValid<$ptr_repr:ty>)?| $is_bit_valid:expr)? ) => { unsafe_impl!( @inner $(#[$attr])* $($tyvar $(: $(? $optbound +)* + $($bound +)*)?,)* - => $trait for $ty $(; |$candidate $(: &$ref_repr)? $(: Ptr<$ptr_repr>)?| $is_bit_valid)? + => $trait for $ty $(; |$candidate $(: MaybeAligned<$ref_repr>)? $(: MaybeValid<$ptr_repr>)?| $is_bit_valid)? ); }; ( @@ -124,66 +122,61 @@ macro_rules! unsafe_impl { $(#[$attr:meta])* $(@const $constname:ident : $constty:ident,)* $($tyvar:ident $(: $(? $optbound:ident +)* + $($bound:ident +)* )?,)* - => $trait:ident for $ty:ty $(; |$candidate:ident $(: &$ref_repr:ty)? $(: Ptr<$ptr_repr:ty>)?| $is_bit_valid:expr)? + => $trait:ident for $ty:ty $(; |$candidate:ident $(: MaybeAligned<$ref_repr:ty>)? $(: MaybeValid<$ptr_repr:ty>)?| $is_bit_valid:expr)? ) => { $(#[$attr])* unsafe impl<$(const $constname: $constty,)* $($tyvar $(: $(? $optbound +)* $($bound +)*)?),*> $trait for $ty { - unsafe_impl!(@method $trait $(; |$candidate: $(&$ref_repr)? $(Ptr<$ptr_repr>)?| $is_bit_valid)?); + unsafe_impl!(@method $trait $(; |$candidate: $(MaybeAligned<$ref_repr>)? $(MaybeValid<$ptr_repr>)?| $is_bit_valid)?); } }; - (@method TryFromBytes ; |$candidate:ident: &$repr:ty| $is_bit_valid:expr) => { + (@method TryFromBytes ; |$candidate:ident: MaybeAligned<$repr:ty>| $is_bit_valid:expr) => { #[allow(clippy::missing_inline_in_public_items)] fn only_derive_is_allowed_to_implement_this_trait() {} #[inline] - unsafe fn is_bit_valid(candidate: Ptr<'_, Self>) -> bool { + fn is_bit_valid(candidate: MaybeValid<'_, Self>) -> bool { // SAFETY: // - The argument to `cast_unsized` is `|p| p as *mut _` as required // by that method's safety precondition. // - The caller has promised that the cast results in an object of // equal or lesser size. - // - The caller has promised that `$repr`'s alignment is less than - // or equal to `Self`'s alignment. #[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` for `'a`. - // - The memory may not be referenced by any mutable references. - // This is a precondition of `is_bit_valid`. - // - The memory may not be mutated even via `UnsafeCell`s. This is a - // precondition of `is_bit_valid`. - // - There must not exist any references to the same memory region - // which contain `UnsafeCell`s at byte ranges which are not - // identical to the byte ranges at which `T` contains - // `UnsafeCell`s. This is a precondition of `is_bit_valid`. - let $candidate: &$repr = unsafe { candidate.as_ref() }; + + // SAFETY: The caller has promised that the referenced memory region + // will contain a valid `$repr`. + let candidate = unsafe { candidate.assume_valid() }; + + let $candidate = MaybeAligned::from_ptr(candidate); $is_bit_valid } }; - (@method TryFromBytes ; |$candidate:ident: Ptr<$repr:ty>| $is_bit_valid:expr) => { + (@method TryFromBytes ; |$candidate:ident: MaybeValid<$repr:ty>| $is_bit_valid:expr) => { #[allow(clippy::missing_inline_in_public_items)] fn only_derive_is_allowed_to_implement_this_trait() {} #[inline] - unsafe fn is_bit_valid(candidate: Ptr<'_, Self>) -> bool { + fn is_bit_valid(candidate: MaybeValid<'_, Self>) -> bool { // SAFETY: // - The argument to `cast_unsized` is `|p| p as *mut _` as required // by that method's safety precondition. // - The caller has promised that the cast results in an object of // equal or lesser size. - // - The caller has promised that `$repr`'s alignment is less than - // or equal to `Self`'s alignment. #[allow(clippy::as_conversions)] let $candidate = unsafe { candidate.cast_unsized::<$repr, _>(|p| p as *mut _) }; + + // SAFETY: The caller has promised that `$repr` is as-initialized as + // `Self`. + let $candidate = unsafe { $candidate.assume_as_initialized() }; + $is_bit_valid } }; (@method TryFromBytes) => { #[allow(clippy::missing_inline_in_public_items)] fn only_derive_is_allowed_to_implement_this_trait() {} - #[inline(always)] unsafe fn is_bit_valid(_: Ptr<'_, Self>) -> bool { true } + #[inline(always)] fn is_bit_valid(_: MaybeValid<'_, Self>) -> bool { true } }; (@method $trait:ident) => { #[allow(clippy::missing_inline_in_public_items)] diff --git a/src/pointer/mod.rs b/src/pointer/mod.rs new file mode 100644 index 0000000000..3d98204a28 --- /dev/null +++ b/src/pointer/mod.rs @@ -0,0 +1,78 @@ +//! Abstractions over raw pointers. + +mod ptr; + +pub use maybe_aligned::MaybeAligned; +pub use ptr::{invariant, Ptr}; + +/// A shorthand for a maybe-valid, maybe-aligned reference. Used as the argument +/// to [`TryFromBytes::is_bit_valid`][crate::TryFromBytes::is_bit_valid]. +pub type MaybeValid<'a, T> = + Ptr<'a, T, (invariant::Shared, invariant::AnyAlignment, invariant::AsInitialized)>; + +/// A semi-user-facing wrapper type representing a maybe-aligned reference, for +/// use in [`TryFromBytes::is_bit_valid`][crate::TryFromBytes::is_bit_valid]. +mod maybe_aligned { + use super::{ptr::invariant, Ptr}; + use crate::Unaligned; + use core::fmt::{Debug, Formatter}; + + /// A reference to a validly-initialized `T` of lifetime `'a` that might + /// *not* be validly aligned for `T`. + #[doc(hidden)] + pub struct MaybeAligned<'a, T: ?Sized> { + ptr: Ptr<'a, T, (invariant::Shared, invariant::AnyAlignment, invariant::Valid)>, + } + + impl<'a, T> MaybeAligned<'a, T> + where + T: 'a + ?Sized, + { + /// Constructs a `MaybeAligned` from a [`Ptr`]. + /// + /// The `Ptr`'s referent must be validly-initialized for `T`. + #[doc(hidden)] + #[inline] + pub const fn from_ptr( + ptr: Ptr<'a, T, (invariant::Shared, invariant::AnyAlignment, invariant::Valid)>, + ) -> Self { + Self { ptr } + } + + /// Reads the value from `MaybeAligned`. + /// + /// This is only available if `T` is [`Copy`]. + #[inline] + pub fn read_unaligned(self) -> T + where + T: Copy, + { + let raw = self.ptr.as_non_null().as_ptr(); + // SAFETY: By invariant on `MaybeAligned`, `raw` contains + // validly-initialized data for `T`. The value is safe to read and + // return, because `T` is copy. + unsafe { core::ptr::read_unaligned(raw) } + } + + /// Views the value as an aligned reference. + /// + /// This is only available if `T` is [`Unaligned`]. + #[inline] + pub fn as_ref(self) -> &'a T + where + T: Unaligned, + { + // SAFETY: The alignment of `T` is 1 and thus is always aligned + // because `T: Unaligned`. + let ptr = unsafe { self.ptr.assume_aligned() }; + ptr.as_ref() + } + } + + impl<'a, T: 'a + ?Sized> Debug for MaybeAligned<'a, T> { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + self.ptr.fmt(f) + } + } +} diff --git a/src/pointer/ptr.rs b/src/pointer/ptr.rs new file mode 100644 index 0000000000..6df8e505f7 --- /dev/null +++ b/src/pointer/ptr.rs @@ -0,0 +1,1010 @@ +use core::ptr::NonNull; + +use crate::{util::AsAddress, KnownLayout, _CastType}; + +/// Module used to gate access to [`Ptr`]'s fields. +mod def { + #[cfg(doc)] + use super::invariant; + use super::Invariants; + use core::{marker::PhantomData, ptr::NonNull}; + + /// A raw pointer with more restrictions. + /// + /// `Ptr` is similar to [`NonNull`], but it is more restrictive in the + /// following ways: + /// - It must derive from a valid allocation. + /// - It must reference a byte range which is contained inside the + /// allocation from which it derives. + /// - As a consequence, the byte range it references must have a size + /// which does not overflow `isize`. + /// + /// Depending on how `Ptr` is parameterized, it may have additional + /// invariants: + /// - `ptr` conforms to the aliasing invariant of + /// [`ALIASING_INVARIANT`](invariant::Aliasing). + /// - `ptr` conforms to the alignment invariant of + /// [`ALIGNMENT_INVARIANT`](invariant::Alignment). + /// - `ptr` conforms to the validity invariant of + /// [`VALIDITY_INVARIANT`](invariant::Validity). + /// + /// `Ptr<'a, T>` is [covariant] in `'a` and `T`. + /// + /// [covariant]: https://doc.rust-lang.org/reference/subtyping.html + pub struct Ptr<'a, T, I> + where + T: 'a + ?Sized, + I: Invariants, + { + /// # Invariants + /// + /// 0. `ptr` is derived from some valid Rust allocation, `A`. + /// 1. `ptr` has valid provenance for `A`. + /// 2. `ptr` addresses a byte range which is entirely contained in `A`. + /// 3. `ptr` addresses a byte range whose length fits in an `isize`. + /// 4. `ptr` addresses a byte range which does not wrap around the + /// address space. + /// 5. `A` is guaranteed to live for at least `'a`. + /// 6. `T: 'a`. + /// 7. `ptr` conforms to the aliasing invariant of + /// [`ALIASING_INVARIANT`](invariant::Aliasing); namely: + /// - If [`invariant::Shared`], the pointer confoms to the aliasing + /// rules of a shared reference. + /// - If [`invariant::Exclusive`], the pointer conforms to the + /// aliasing rules of an exclusive reference. + /// 8. `ptr` conforms to the alignment invariant of + /// [`ALIGNMENT_INVARIANT`](invariant::Alignment); namely: + /// - If [`invariant::Aligned`], the pointer's referent is + /// well-aligned. + /// 9. `ptr` conforms to the validity invariant of + /// [`VALIDITY_INVARIANT`](invariant::Validity); namely: + /// - If [`invariant::AsInitialized`], the pointer's referent is + /// initialized (but not necessarily validly so) in all of the + /// initialized ranges of `T`. + /// - If [`invariant::Valid`], the pointer's referent is bit-valid. + // SAFETY: `NonNull` is covariant over `T` [1]. + // + // [1]: https://doc.rust-lang.org/std/ptr/struct.NonNull.html + ptr: NonNull, + // SAFETY: `&'a ()` is covariant over `'a` [1]. + // + // [1]: https://doc.rust-lang.org/reference/subtyping.html#variance + _invariants: PhantomData<&'a I>, + } + + impl<'a, T, I> Ptr<'a, T, I> + where + T: 'a + ?Sized, + I: Invariants, + { + /// Constructs a `Ptr` from a [`NonNull`]. + /// + /// # Safety + /// + /// The caller promises that: + /// + /// 0. `ptr` is derived from some valid Rust allocation, `A`. + /// 1. `ptr` has valid provenance for `A`. + /// 2. `ptr` addresses a byte range which is entirely contained in `A`. + /// 3. `ptr` addresses a byte range whose length fits in an `isize`. + /// 4. `ptr` addresses a byte range which does not wrap around the + /// address space. + /// 5. `A` is guaranteed to live for at least `'a`. + /// 6. `ptr` conforms to the aliasing invariant of + /// [`ALIASING_INVARIANT`](invariant::Aliasing). + /// 7. `ptr` conforms to the alignment invariant of + /// [`ALIGNMENT_INVARIANT`](invariant::Alignment). + /// 8. `ptr` conforms to the validity invariant of + /// [`VALIDITY_INVARIANT`](invariant::Validity). + pub(super) unsafe fn new(ptr: NonNull) -> Ptr<'a, T, I> { + // SAFETY: The caller has promised to satisfy all safety invariants + // of `Ptr`. + Self { ptr, _invariants: PhantomData } + } + + /// Constructs a `Ptr` from another `Ptr`, possibly with different + /// parameterized safety invariants. + /// + /// # Safety + /// + /// The caller promises that `ptr` conforms to the invariants of `I`. + pub(super) unsafe fn from_ptr(ptr: Ptr<'a, T, H>) -> Self + where + H: Invariants, + { + // SAFETY: The caller has promised to satisfy all parameterized + // invariants of `Ptr`. `Ptr`'s other invariants are satisfied + // by-contract by the source `Ptr`. + unsafe { Self::new(ptr.as_non_null()) } + } + + /// Converts this `Ptr` to a [`NonNull`]. + /// + /// Note that this method does not consume `self`. The caller should + /// watch out for `unsafe` code which uses the returned `NonNull` in a + /// way that violates the safety invariants of `self`. + pub(crate) fn as_non_null(&self) -> NonNull { + self.ptr + } + } +} + +pub use def::Ptr; + +/// Used to define the system of [invariants][invariant] of `Ptr`. +macro_rules! define_system { + ($(#[$system_attr:meta])* $system:ident { + $($(#[$set_attr:meta])* $set:ident { + $( $(#[$elem_attr:meta])* $elem:ident,)* + })* + }) => { + mod sealed { + pub trait Sealed {} + + impl<$($set,)*> Sealed for ($($set,)*) + where + $($set: super::$set,)* + {} + + $($( + impl Sealed for super::$elem {} + )*)* + } + + $(#[$system_attr])* + pub trait $system: sealed::Sealed { + $( + $(#[$set_attr])* + type $set: $set; + )* + } + + mod here { + pub(super) use super::*; + } + + impl<$($set,)*> $system for ($($set,)*) + where + $($set: here::$set,)* + { + $(type $set = $set;)* + } + + $( + $(#[$set_attr])* + pub trait $set: sealed::Sealed {} + + $( + $(#[$elem_attr])* + #[allow(missing_copy_implementations, missing_debug_implementations)] + pub enum $elem {} + + $(#[$elem_attr])* + impl $set for $elem {} + )* + )* + }; +} + +/// The parameterized invariants of a [`Ptr`]. +pub mod invariant { + define_system! { + /// The invariants of a [`Ptr`][super::Ptr]. + Invariants { + /// The aliasing invariant of a [`Ptr`][super::Ptr]. + Aliasing { + /// No aliasing invariant. + AnyAliasing, + + /// The `Ptr<'a, T>` adheres to the aliasing rules of a `&'a T`. + /// + /// The referent of a shared-aliased `Ptr` may be concurrently + /// referenced by any number of shared-aliased `Ptr` or `&T` + /// references, and may not be concurrently referenced by any + /// exclusively-aliased `Ptr`s or `&mut T` references. + /// + /// Unsafe code must assume that safe code will assume that the + /// referent of such a `Ptr` will not be mutated, except through + /// interior [`UnsafeCell`]s (if any). + /// + /// [`UnsafeCell`]: core::cell::UnsafeCell + Shared, + + /// The `Ptr<'a, T>` adheres to the aliasing rules of a `&'a mut T`. + /// + /// The referent of an exclusively-aliased `Ptr` may not be + /// concurrently referenced by any other `Ptr`s or references. + /// Owners of an exclusively-aliased `Ptr` may safely (contigent on + /// upholding the referent's original validity invariants) mutate + /// its referent. + Exclusive, + } + + /// The alignment invariant of a [`Ptr`][super::Ptr]. + Alignment { + /// The referent is not necessarily aligned. + AnyAlignment, + /// The referent is aligned. + Aligned, + } + + /// The validity invariant of a [`Ptr`][super::Ptr]. + Validity { + /// The referent is not necessarily initialized. + AnyValidity, + + /// The byte ranges initialized in `T` are also initialized in the + /// referent. + /// + /// Formally: uninitialized bytes may only be present in `Ptr`'s + /// referent where it is possible for them 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 all instances `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, for + /// all instances 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). + AsInitialized, + + /// The referent is bit-valid for `T`. + Valid, + } + } + } +} + +pub(crate) use invariant::Invariants; + +/// External trait implementations on [`Ptr`]. +mod _external { + use super::*; + use core::fmt::{Debug, Formatter}; + + /// SAFETY: Shared pointers are safely `Copy`. We do not implement `Copy` + /// for exclusive pointers, since at most one may exist at a time. `Ptr`'s + /// other invariants are unaffected by the number of references that exist + /// to `Ptr`'s referent. + impl<'a, T, I> Copy for Ptr<'a, T, I> + where + T: 'a + ?Sized, + I: Invariants, + { + } + + /// SAFETY: Shared pointers are safely `Clone`. We do not implement `Clone` + /// for exclusive pointers, since at most one may exist at a time. `Ptr`'s + /// other invariants are unaffected by the number of references that exist + /// to `Ptr`'s referent. + impl<'a, T, I> Clone for Ptr<'a, T, I> + where + T: 'a + ?Sized, + I: Invariants, + { + #[inline] + fn clone(&self) -> Self { + *self + } + } + + impl<'a, T, I> Debug for Ptr<'a, T, I> + where + T: 'a + ?Sized, + I: Invariants, + { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + self.as_non_null().fmt(f) + } + } +} + +/// Methods for converting to and from `Ptr` and Rust's safe reference types. +mod _conversions { + use super::*; + + /// `&'a T` → `Ptr<'a, T>` + impl<'a, T> Ptr<'a, T, (invariant::Shared, invariant::Aligned, invariant::Valid)> + where + T: 'a + ?Sized, + { + /// Constructs a `Ptr` from a shared reference. + #[doc(hidden)] + #[inline] + pub fn from_ref(ptr: &'a T) -> Self { + let ptr = core::ptr::NonNull::from(ptr); + // SAFETY: + // 0. `ptr`, by invariant on `&'a T`, is derived from some valid + // Rust allocation, `A`. + // 1. `ptr`, by invariant on `&'a T`, has valid provenance for `A`. + // 2. `ptr`, by invariant on `&'a T`, addresses a byte range which + // is entirely contained in `A`. + // 3. `ptr`, by invariant on `&'a T`, addresses a byte range whose + // length fits in an `isize`. + // 4. `ptr`, by invariant on `&'a T`, addresses a byte range which + // does not wrap around the address space. + // 5. `A`, by invariant on `&'a T`, is guaranteed to live for at + // least `'a`. + // 6. `ptr`, by invariant on `&'a T`, conforms to the aliasing + // invariant of `invariant::Shared`. + // 7. `ptr`, by invariant on `&'a T`, conforms to the alignment + // invariant of `invariant::Aligned`. + // 8. `ptr`, by invariant on `&'a T`, conforms to the validity + // invariant of `invariant::Valid`. + unsafe { Self::new(ptr) } + } + } + + /// `Ptr<'a, T>` → `&'a T` + impl<'a, T> Ptr<'a, T, (invariant::Shared, invariant::Aligned, invariant::Valid)> + where + T: 'a + ?Sized, + { + /// Converts the `Ptr` to a shared reference. + // This consumes `self`, not `&self`, because `self` is, logically, a + // pointer. Since this method is only available for `invariant::Shared`, + // `Self: Copy`, and so this doesn't prevent the caller from still + // using the pointer after calling `as_ref`. + #[allow(clippy::wrong_self_convention)] + pub(crate) fn as_ref(self) -> &'a T { + let raw = self.as_non_null(); + // SAFETY: This invocation of `NonNull::as_ref` satisfies its + // documented safety preconditions: + // + // 1. The pointer is properly aligned. This is ensured by-contract + // on `Ptr`, because the `ALIGNMENT_INVARIANT` is `Aligned`. + // + // 2. It must be “dereferenceable” in the sense defined in the + // module documentation; i.e.: + // + // > The memory range of the given size starting at the pointer + // > must all be within the bounds of a single allocated object. + // > [2] + // + // This is ensured by contract on all `Ptr`s. + // + // 3. The pointer must point to an initialized instance of `T`. This + // is ensured by-contract on `Ptr`, because the + // `VALIDITY_INVARIANT` is `Valid`. + // + // 4. You must enforce Rust’s aliasing rules. This is ensured by + // contract on `Ptr`, because the `ALIASING_INVARIANT` is + // `Shared`. + // + // [1]: https://doc.rust-lang.org/std/ptr/struct.NonNull.html#method.as_ref + // [2]: https://doc.rust-lang.org/std/ptr/index.html#safety + unsafe { raw.as_ref() } + } + } +} + +/// State transitions between invariants. +mod _transitions { + use super::*; + + impl<'a, T, I> Ptr<'a, T, I> + where + T: 'a + ?Sized, + I: Invariants, + { + /// Assumes that `Ptr`'s referent is validly-aligned for `T`. + /// + /// # Safety + /// + /// The caller promises that `Ptr`'s referent conforms to the alignment + /// invariant of `T`. + #[inline] + pub(crate) unsafe fn assume_aligned( + self, + ) -> Ptr<'a, T, (I::Aliasing, invariant::Aligned, I::Validity)> { + // SAFETY: The caller promises that `self`'s referent is + // well-aligned for `T`. + unsafe { Ptr::from_ptr(self) } + } + + /// Assumes that `Ptr`'s referent is as-initialized as `T`. + /// + /// # Safety + /// + /// The caller promises that `Ptr`'s referent conforms to the + /// [`invariant::AsInitialized`] invariant (see documentation there). + #[doc(hidden)] + #[inline] + pub unsafe fn assume_as_initialized( + self, + ) -> Ptr<'a, T, (I::Aliasing, I::Alignment, invariant::AsInitialized)> { + // SAFETY: The caller promises that `self`'s referent only contains + // uninitialized bytes in a subset of the uninitialized ranges in + // `T`. for `T`. + unsafe { Ptr::from_ptr(self) } + } + + /// Assumes that `Ptr`'s referent is validly initialized for `T`. + /// + /// # Safety + /// + /// The caller promises that `Ptr`'s referent conforms to the + /// bit validity invariants on `T`. + #[inline] + pub(crate) unsafe fn assume_valid( + self, + ) -> Ptr<'a, T, (I::Aliasing, I::Alignment, invariant::Valid)> { + // SAFETY: The caller promises that `self`'s referent is bit-valid + // for `T`. + unsafe { Ptr::from_ptr(self) } + } + + /// Forgets that `Ptr`'s referent is validly-aligned for `T`. + #[doc(hidden)] + #[inline] + pub fn forget_aligned( + self, + ) -> Ptr<'a, T, (I::Aliasing, invariant::AnyAlignment, I::Validity)> { + // SAFETY: `AnyAlignment` is less restrictive than `Aligned`. + unsafe { Ptr::from_ptr(self) } + } + + /// Forgets that `Ptr`'s referent is bit-valid for `T`. + #[doc(hidden)] + #[inline] + pub fn forget_valid( + self, + ) -> Ptr<'a, T, (I::Aliasing, I::Alignment, invariant::AsInitialized)> { + // SAFETY: `AnyValidity` is less restrictive than `Valid`. + unsafe { Ptr::from_ptr(self) } + } + } +} + +/// Casts of the referent type. +mod _casts { + use super::*; + + impl<'a, T, I> Ptr<'a, T, I> + where + T: 'a + ?Sized, + I: Invariants, + { + /// Casts to a different (unsized) target type. + /// + /// # Safety + /// + /// The caller promises that + /// - `cast(p)` is implemented exactly as follows: `|p: *mut T| p as + /// *mut U`. + /// - The size of the object referenced by the resulting pointer is less + /// than or equal to the size of the object referenced by `self`. + #[doc(hidden)] + #[inline] + pub unsafe fn cast_unsized *mut U>( + self, + cast: F, + ) -> Ptr<'a, U, (I::Aliasing, invariant::AnyAlignment, invariant::AnyValidity)> + where + U: 'a, + { + let ptr = cast(self.as_non_null().as_ptr()); + + // SAFETY: Caller promises that `cast` is just an `as` cast. We call + // `cast` on `self.ptr.as_non_null().as_ptr()`, which is non-null by + // construction. + let ptr = unsafe { NonNull::new_unchecked(ptr) }; + + // SAFETY: Lemma: In the following safety arguments, note that + // the caller promises that `cast` produces a pointer which + // addresses no more bytes than those addressed by its input + // pointer. As a result, the bytes addressed by `ptr` are a subset + // of the bytes addressed by `self`. + // + // 0. By invariant on `self` and contract on `cast`, `ptr` is + // derived from some valid Rust allocation, `A`. + // 1. By invariant on `self` and contract on `cast` (its + // implementation is provenance-preserving), `ptr` has valid + // provenance for `A`. + // 2. By the above lemma, `ptr` addresses a byte range which is + // entirely contained in `A`. + // 3. By the above lemma, `ptr` addresses a byte range whose + // length fits in an `isize`. + // 4. By the above lemma, `ptr` addresses a byte range which + // does not wrap around the address space. + // 5. By invariant on `self` and contract on `cast`, `A` is + // guaranteed to live for at least `'a`. + // 6. `ptr` conforms to the aliasing invariant of + // `ALIASING_INVARIANT` because casting does not impact the + // aliasing invariant. + // 7. `ptr`, trivially, conforms to the alignment invariant of + // `AnyAlignment`. + // 8. `ptr`, trivially, conforms to the validity invariant of + // `AnyValidity`. + unsafe { Ptr::new(ptr) } + } + } + + /// For caller convenience, these methods are generic over alignment + /// invariant. In practice, the referent is always well-aligned, because the + /// alignment of `[u8]` is 1. + impl<'a, I> Ptr<'a, [u8], I> + where + I: Invariants, + { + /// Attempts to cast `self` to a `U` using the given cast type. + /// + /// Returns `None` if the resulting `U` would be invalidly-aligned or if + /// no `U` can fit in `self`. On success, returns a pointer to the + /// largest-possible `U` which fits in `self`. + /// + /// # Safety + /// + /// The caller may assume that this implementation is correct, and may + /// rely on that assumption for the soundness of their code. In + /// particular, the caller may assume that, if `try_cast_into` returns + /// `Some((ptr, split_at))`, then: + /// - If this is a prefix cast, `ptr` refers to the byte range `[0, + /// split_at)` in `self`. + /// - If this is a suffix cast, `ptr` refers to the byte range + /// `[split_at, self.len())` in `self`. + /// + /// # Panics + /// + /// Panics if `U` is a DST whose trailing slice element is zero-sized. + pub(crate) fn try_cast_into( + &self, + cast_type: _CastType, + ) -> Option<(Ptr<'a, U, (I::Aliasing, invariant::Aligned, invariant::AnyValidity)>, usize)> + { + // PANICS: By invariant, the byte range addressed by `self.ptr` does + // not wrap around the address space. This implies that the sum of + // the address (represented as a `usize`) and length do not overflow + // `usize`, as required by `validate_cast_and_convert_metadata`. + // Thus, this call to `validate_cast_and_convert_metadata` will only + // panic if `U` is a DST whose trailing slice element is zero-sized. + let (elems, split_at) = U::LAYOUT.validate_cast_and_convert_metadata( + AsAddress::addr(self.as_non_null().as_ptr()), + self.len(), + cast_type, + )?; + + let offset = match cast_type { + _CastType::_Prefix => 0, + _CastType::_Suffix => split_at, + }; + + let ptr = self.as_non_null().cast::().as_ptr(); + + // SAFETY: `offset` is either `0` or `split_at`. + // `validate_cast_and_convert_metadata` promises that `split_at` is + // in the range `[0, self.len()]`. Thus, in both cases, `offset` is + // in `[0, self.len()]`. Thus: + // - The resulting pointer is in or one byte past the end of the + // same byte range as `self.ptr`. Since, by invariant, `self.ptr` + // addresses a byte range entirely contained within a single + // allocation, the pointer resulting from this operation is within + // or one byte past the end of that same allocation. + // - By invariant, `self.len() <= isize::MAX`. Since `offset <= + // self.len()`, `offset <= isize::MAX`. + // - By invariant, `self.ptr` addresses a byte range which does not + // wrap around the address space. This means that the base pointer + // plus the `self.len()` does not overflow `usize`. Since `offset + // <= self.len()`, this addition does not overflow `usize`. + let base = unsafe { ptr.add(offset) }; + + // SAFETY: Since `add` is not allowed to wrap around, the preceding line + // produces a pointer whose address is greater than or equal to that of + // `ptr`. Since `ptr` is a `NonNull`, `base` is also non-null. + let base = unsafe { NonNull::new_unchecked(base) }; + let ptr = U::raw_from_ptr_len(base, elems); + + // SAFETY: + // 0. By invariant, `self.ptr` is derived from some valid Rust + // allocation, `A`. By contract on `cast`, `ptr` is derived from + // `self`, and thus from the same valid Rust allocation, `A`. + // 1. By invariant, `self.ptr` has provenance valid for some Rust + // allocation, `A`. By contract on `cast`, and because `ptr` is + // derived from `self` via provenance-preserving operations, + // `ptr` will also have provenance valid for `A`. + // 2. By invariant, `self.ptr` addresses a byte range which is + // entirely contained in `A`. By contract on `cast`, `ptr` + // addresses a subset of the bytes referenced by `self.ptr`, + // which is itself entirely contained by `A`. + // - `validate_cast_and_convert_metadata` promises that the object + // described by `elems` and `split_at` lives at a byte range + // which is a subset of the input byte range. Thus: + // 3. Since, by invariant, `self.ptr` addresses a byte range + // entirely contained in `A`, so does `ptr`. + // 4. Since, by invariant, `self.ptr` addresses a range whose + // length is not longer than `isize::MAX` bytes, so does + // `ptr`. + // 5. Since, by invariant, `self.ptr` addresses a range which + // does not wrap around the address space, so does `ptr`. + // 6. `ptr` conforms to the aliasing invariant of + // `ALIASING_INVARIANT` because casting does not impact the + // aliasing invariant. + // 7. `ptr` conforms to the alignment invariant of `Aligned` because + // it is derived from `validate_cast_and_convert_metadata` + // promises that the object described by `split_at` is validly + // aligned for `U`. + // 8. `ptr`, trivially, conforms to the validity invariant of + // `AnyValidity`. + Some((unsafe { Ptr::new(ptr) }, split_at)) + } + + /// Attempts to cast `self` into a `U`, failing if all of the bytes of + /// `self` cannot be treated as a `U`. + /// + /// In particular, this method fails if `self` is not validly-aligned + /// for `U` or if `self`'s size is not a valid size for `U`. + /// + /// # Safety + /// + /// On success, the caller may assume that the returned pointer + /// references the same byte range as `self`. + #[allow(unused)] + #[inline(always)] + pub(crate) fn try_cast_into_no_leftover( + &self, + ) -> Option> { + // TODO(#67): Remove this allow. See NonNulSlicelExt for more + // details. + #[allow(unstable_name_collisions)] + match self.try_cast_into(_CastType::_Prefix) { + Some((slf, split_at)) if split_at == self.len() => Some(slf), + Some(_) | None => None, + } + } + } +} + +/// Projections through the referent. +mod _project { + use super::*; + + impl<'a, T, I> Ptr<'a, T, I> + where + T: 'a + ?Sized, + I: Invariants, + { + /// Projects a field from `self`. + /// + /// # Safety + /// + /// ## Preconditions + /// + /// The caller promises that `projector` projects a field of its + /// argument. Its argument will be `self` casted to a raw pointer. The + /// pointer it returns must reference only a subset of `self`'s bytes. + /// + /// ## Postconditions + /// + /// If the preconditions of this function are met, this function will + /// return a `Ptr` to the field projected from `self` by `projector`. + #[doc(hidden)] + #[inline] + pub unsafe fn project( + self, + projector: impl FnOnce(*mut T) -> *mut U, + ) -> Ptr<'a, U, (I::Aliasing, invariant::AnyAlignment, I::Validity)> { + // SAFETY: `projector` is provided with `self` casted to a raw + // pointer. + let field = projector(self.as_non_null().as_ptr()); + + // SAFETY: We promise that `projector` is provided with `self` + // casted to a raw pointer, and the caller promises that `projector` + // is a field projection of its argument. By invariant on `Ptr`, + // `self` is non-null, and by contract on `projector`, so too will + // its return value. + // + // Note that field projection cannot wrap around the address space + // to null. + // + // TODO(https://github.com/rust-lang/rust/pull/116675): Cite + // documentation that allocated objects do not wrap around the + // address space. + let field = unsafe { NonNull::new_unchecked(field) }; + + // SAFETY: The safety invariants of `Ptr::new` (see definition) are + // satisfied: + // 0. `field` is derived from a valid Rust allocation, because + // `self` is derived from a valid Rust allocation, by invariant + // on `Ptr`, and `projector` (by contract) is a field projection + // through `self`. + // 1. `field` has valid provenance for `self`, because it derived + // from `self` using a series of provenance-preserving + // operations. + // 2. `field` is entirely contained in the allocation of `self`, + // because it is derived by `projector`, which is (by contract) a + // field projection through `self`. + // 3. `field` addresses a byte range whose length fits in an + // `isize`, because it is a field projection through `self` and + // thus is entirely contained by `self`, which satisfies this + // invariant. + // 4. `field` addresses a byte range which does not wrap around the + // address space (see above). + // 5. The allocation of `field` is guaranteed to live for at least + // `'a`, because `field` is entirely contained in `self`, which + // lives for at least `'a` by invariant on `Ptr`. + // 6. `field` conforms to the aliasing invariant of + // `ALIASING_INVARIANT` because projection does not impact the + // aliasing invariant. + // 7. `field`, trivially, conforms to the alignment invariant of + // `AnyAlignment`. + // 8. `field`, conditionally, conforms to the validity invariant of + // `VALIDITY_INVARIANT`. If `field` is projected from data valid + // for `T`, `field` will be valid for `U`. + unsafe { Ptr::new(field) } + } + } + + impl<'a, T, I> Ptr<'a, [T], I> + where + T: 'a, + I: Invariants, + { + /// The number of slice elements referenced by `self`. + /// + /// # Safety + /// + /// Unsafe code my rely on `len` satisfying the above contract. + pub(super) fn len(&self) -> usize { + #[allow(clippy::as_conversions)] + let slc = self.as_non_null().as_ptr() as *const [()]; + + // SAFETY: + // - `()` has alignment 1, so `slc` is trivially aligned. + // - `slc` was derived from a non-null pointer. + // - The size is 0 regardless of the length, so it is sound to + // materialize a reference regardless of location. + // - By invariant, `self.ptr` has valid provenance. + let slc = unsafe { &*slc }; + + // This is correct because the preceding `as` cast preserves the + // number of slice elements. Per + // https://doc.rust-lang.org/nightly/reference/expressions/operator-expr.html#slice-dst-pointer-to-pointer-cast: + // + // For slice types like `[T]` and `[U]`, the raw pointer types + // `*const [T]`, `*mut [T]`, `*const [U]`, and `*mut [U]` encode + // the number of elements in this slice. Casts between these raw + // pointer types preserve the number of elements. Note that, as a + // consequence, such casts do *not* necessarily preserve the size + // of the pointer's referent (e.g., casting `*const [u16]` to + // `*const [u8]` will result in a raw pointer which refers to an + // object of half the size of the original). The same holds for + // `str` and any compound type whose unsized tail is a slice type, + // such as struct `Foo(i32, [u8])` or `(u64, Foo)`. + // + // TODO(#429), + // TODO(https://github.com/rust-lang/reference/pull/1417): Once this + // text is available on the Stable docs, cite those instead of the + // Nightly docs. + slc.len() + } + + /// Iteratively projects the elements `Ptr` from `Ptr<[T]>`. + pub(crate) fn iter(&self) -> impl Iterator> { + // TODO(#429): Once `NonNull::cast` documents that it preserves + // provenance, cite those docs. + let base = self.as_non_null().cast::().as_ptr(); + (0..self.len()).map(move |i| { + // TODO(https://github.com/rust-lang/rust/issues/74265): Use + // `NonNull::get_unchecked_mut`. + + // SAFETY: If the following conditions are not satisfied + // `pointer::cast` may induce Undefined Behavior [1]: + // > 1. Both the starting and resulting pointer must be either + // > in bounds or one byte past the end of the same allocated + // > object. + // > 2. The computed offset, in bytes, cannot overflow an + // > `isize`. + // > 3. The offset being in bounds cannot rely on “wrapping + // > around” the address space. That is, the + // > infinite-precision sum must fit in a `usize`. + // + // [1] https://doc.rust-lang.org/std/primitive.pointer.html#method.add + // + // We satisfy all three of these conditions here: + // 1. `base` (by invariant on `self`) points to an allocated + // object. By contract, `self.len()` accurately reflects the + // number of elements in the slice. `i` is in bounds of + // `c.len()` by construction, and so the result of this + // addition cannot overflow past the end of the allocation + // referred to by `c`. + // 2. By invariant on `Ptr`, `self` addresses a byte range whose + // length fits in an `isize`. Since `elem` is contained in + // `self`, the computed offset of `elem` must fit within + // `isize.` + // 3. By invariant on `Ptr`, `self` addresses a byte range which + // does not wrap around the address space. Since `elem` is + // contained in `self`, the computed offset of `elem` must + // wrap around the address space. + // + // TODO(#429): Once `pointer::add` documents that it preserves + // provenance, cite those docs. + let elem = unsafe { base.add(i) }; + + // SAFETY: + // - `elem` must not be null. `base` is constructed from a + // `NonNull` pointer, and the addition that produces `elem` + // must not overflow or wrap around, so `elem >= base > 0`. + // + // TODO(#429): Once `NonNull::new_unchecked` documents that it + // preserves provenance, cite those docs. + let elem = unsafe { NonNull::new_unchecked(elem) }; + + // SAFETY: The safety invariants of `Ptr::new` (see definition) + // are satisfied: + // 0. `elem` is derived from a valid Rust allocation, because + // `self` is derived from a valid Rust allocation, by + // invariant on `Ptr`. + // 1. `elem` has valid provenance for `self`, because it + // derived from `self` using a series of + // provenance-preserving operations. + // 2. `elem` is entirely contained in the allocation of `self` + // (see above). + // 3. `elem` addresses a byte range whose length fits in an + // `isize` (see above). + // 4. `elem` addresses a byte range which does not wrap around + // the address space (see above). + // 5. The allocation of `elem` is guaranteed to live for at + // least `'a`, because `elem` is entirely contained in + // `self`, which lives for at least `'a` by invariant on + // `Ptr`. + // 6. `elem` conforms to the aliasing invariant of + // `ALIASING_INVARIANT` because projection does not impact + // the aliasing invariant. + // 7. `elem`, conditionally, conforms to the validity invariant + // of `ALIGNMENT_INVARIANT`. If `elem` is projected from data + // well-aligned for `[T]`, `elem` will be valid for `T`. + // 8. `elem`, conditionally, conforms to the validity invariant of + // `VALIDITY_INVARIANT`. If `elem` is projected from data valid + // for `[T]`, `elem` will be valid for `T`. + unsafe { Ptr::new(elem) } + }) + } + } +} + +#[cfg(test)] +mod tests { + use core::mem::{self, MaybeUninit}; + + use super::*; + use crate::{util::testutil::AU64, FromBytes}; + + #[test] + fn test_ptr_try_cast_into_soundness() { + // This test is designed so that if `Ptr::try_cast_into_xxx` are + // buggy, it will manifest as unsoundness that Miri can detect. + + // - If `size_of::() == 0`, `N == 4` + // - Else, `N == 4 * size_of::()` + fn test() { + let mut bytes = [MaybeUninit::::uninit(); N]; + let initialized = [MaybeUninit::new(0u8); N]; + for start in 0..=bytes.len() { + for end in start..=bytes.len() { + // Set all bytes to uninitialized other than those in + // the range we're going to pass to `try_cast_from`. + // This allows Miri to detect out-of-bounds reads + // because they read uninitialized memory. Without this, + // some out-of-bounds reads would still be in-bounds of + // `bytes`, and so might spuriously be accepted. + bytes = [MaybeUninit::::uninit(); N]; + let bytes = &mut bytes[start..end]; + // Initialize only the byte range we're going to pass to + // `try_cast_from`. + bytes.copy_from_slice(&initialized[start..end]); + + let bytes = { + let bytes: *const [MaybeUninit] = bytes; + #[allow(clippy::as_conversions)] + let bytes = bytes as *const [u8]; + // SAFETY: We just initialized these bytes to valid + // `u8`s. + unsafe { &*bytes } + }; + + // SAFETY: The bytes in `slf` must be initialized. + unsafe fn validate_and_get_len( + slf: Ptr< + '_, + T, + (invariant::Shared, invariant::Aligned, invariant::AnyValidity), + >, + ) -> usize { + // SAFETY: + // - Since all bytes in `slf` are initialized and + // `T: FromBytes`, `slf` contains a valid `T`. + let t = unsafe { slf.as_non_null().as_ref() }; + + let bytes = { + let len = mem::size_of_val(t); + let t: *const T = t; + // SAFETY: + // - We know `t`'s bytes are all initialized + // because we just read it from `slf`, which + // points to an initialized range of bytes. If + // there's a bug and this doesn't hold, then + // that's exactly what we're hoping Miri will + // catch! + // - Since `T: FromBytes`, `T` doesn't contain + // any `UnsafeCell`s, so it's okay for `t: T` + // and a `&[u8]` to the same memory to be + // alive concurrently. + unsafe { core::slice::from_raw_parts(t.cast::(), len) } + }; + + // This assertion ensures that `t`'s bytes are read + // and compared to another value, which in turn + // ensures that Miri gets a chance to notice if any + // of `t`'s bytes are uninitialized, which they + // shouldn't be (see the comment above). + assert_eq!(bytes, vec![0u8; bytes.len()]); + + mem::size_of_val(t) + } + + for cast_type in [_CastType::_Prefix, _CastType::_Suffix] { + if let Some((slf, split_at)) = + Ptr::from_ref(bytes).try_cast_into::(cast_type) + { + // SAFETY: All bytes in `bytes` have been + // initialized. + let len = unsafe { validate_and_get_len(slf) }; + match cast_type { + _CastType::_Prefix => assert_eq!(split_at, len), + _CastType::_Suffix => assert_eq!(split_at, bytes.len() - len), + } + } + } + + if let Some(slf) = Ptr::from_ref(bytes).try_cast_into_no_leftover::() { + // SAFETY: All bytes in `bytes` have been + // initialized. + let len = unsafe { validate_and_get_len(slf) }; + assert_eq!(len, bytes.len()); + } + } + } + } + + macro_rules! test { + ($($ty:ty),*) => { + $({ + const S: usize = core::mem::size_of::<$ty>(); + const N: usize = if S == 0 { 4 } else { S * 4 }; + test::(); + // We don't support casting into DSTs whose trailing slice + // element is a ZST. + if S > 0 { + test::(); + } + // TODO: Test with a slice DST once we have any that + // implement `KnownLayout + FromBytes`. + })* + }; + } + + test!(()); + test!(u8, u16, u32, u64, u128, usize, AU64); + test!(i8, i16, i32, i64, i128, isize); + test!(f32, f64); + } +} diff --git a/src/util.rs b/src/util.rs index 48190f7be8..fee7e3989b 100644 --- a/src/util.rs +++ b/src/util.rs @@ -11,611 +11,6 @@ pub(crate) mod core_layout; use core::{mem, num::NonZeroUsize}; -pub(crate) mod ptr { - use core::{ - fmt::{Debug, Formatter}, - marker::PhantomData, - ptr::NonNull, - }; - - use crate::{util::AsAddress, KnownLayout, _CastType}; - - /// A raw pointer with more restrictions. - /// - /// `Ptr` is similar to `NonNull`, but it is more restrictive in the - /// following ways: - /// - It must derive from a valid allocation - /// - It must reference a byte range which is contained inside the - /// allocation from which it derives - /// - As a consequence, the byte range it references must have a size - /// which does not overflow `isize` - /// - It must satisfy `T`'s alignment requirement - /// - /// Thanks to these restrictions, it is easier to prove the soundness of - /// some operations using `Ptr`s. - /// - /// `Ptr<'a, T>` is [covariant] in `'a` and `T`. - /// - /// [covariant]: https://doc.rust-lang.org/reference/subtyping.html - pub struct Ptr<'a, T: 'a + ?Sized> { - // INVARIANTS: - // 1. `ptr` is derived from some valid Rust allocation, `A` - // 2. `ptr` has the same provenance as `A` - // 3. `ptr` addresses a byte range which is entirely contained in `A` - // 4. `ptr` addresses a byte range whose length fits in an `isize` - // 5. `ptr` addresses a byte range which does not wrap around the address - // space - // 6. `ptr` is validly-aligned for `T` - // 7. `A` is guaranteed to live for at least `'a` - // 8. `T: 'a` - ptr: NonNull, - _lifetime: PhantomData<&'a ()>, - } - - impl<'a, T: ?Sized> Copy for Ptr<'a, T> {} - impl<'a, T: ?Sized> Clone for Ptr<'a, T> { - #[inline] - fn clone(&self) -> Self { - *self - } - } - - impl<'a, T: ?Sized> Ptr<'a, T> { - /// Returns a shared reference to the value. - /// - /// # Safety - /// - /// For the duration of `'a`: - /// - The referenced memory must contain a validly-initialized `T` for - /// the duration of `'a`. - /// - The referenced memory must not also be referenced by any mutable - /// references. - /// - The referenced memory must not be mutated, even via an - /// [`UnsafeCell`]. - /// - There must not exist any references to the same memory region - /// which contain `UnsafeCell`s at byte ranges which are not identical - /// to the byte ranges at which `T` contains `UnsafeCell`s. - /// - /// [`UnsafeCell`]: core::cell::UnsafeCell - // TODO(#429): The safety requirements are likely overly-restrictive. - // Notably, mutation via `UnsafeCell`s is probably fine. Once the rules - // are more clearly defined, we should relax the safety requirements. - // For an example of why this is subtle, see: - // https://github.com/rust-lang/unsafe-code-guidelines/issues/463#issuecomment-1736771593 - #[allow(unused)] - pub(crate) unsafe fn as_ref(&self) -> &'a T { - // SAFETY: - // - By invariant, `self.ptr` is properly-aligned for `T`. - // - By invariant, `self.ptr` is "dereferenceable" in that it points - // to a single allocation. - // - By invariant, the allocation is live for `'a`. - // - The caller promises that no mutable references exist to this - // region during `'a`. - // - The caller promises that `UnsafeCell`s match exactly. - // - The caller promises that no mutation will happen during `'a`, - // even via `UnsafeCell`s. - // - The caller promises that the memory region contains a - // validly-intialized `T`. - unsafe { self.ptr.as_ref() } - } - - /// Casts to a different (unsized) target type. - /// - /// # Safety - /// - /// The caller promises that - /// - `cast(p)` is implemented exactly as follows: `|p: *mut T| p as - /// *mut U`. - /// - The size of the object referenced by the resulting pointer is less - /// than or equal to the size of the object referenced by `self`. - /// - The alignment of `U` is less than or equal to the alignment of - /// `T`. - #[doc(hidden)] - #[inline] - pub unsafe fn cast_unsized *mut U>( - self, - cast: F, - ) -> Ptr<'a, U> { - let ptr = cast(self.ptr.as_ptr()); - // SAFETY: Caller promises that `cast` is just an `as` cast. We call - // `cast` on `self.ptr.as_ptr()`, which is non-null by construction. - let ptr = unsafe { NonNull::new_unchecked(ptr) }; - // SAFETY: - // - By invariant, `self.ptr` is derived from some valid Rust - // allocation, and since `ptr` is just `self.ptr as *mut U`, so is - // `ptr`. - // - By invariant, `self.ptr` has the same provenance as `A`, and so - // the same is true of `ptr`. - // - By invariant, `self.ptr` addresses a byte range which is - // entirely contained in `A`, and so the same is true of `ptr`. - // - By invariant, `self.ptr` addresses a byte range whose length - // fits in an `isize`, and so the same is true of `ptr`. - // - By invariant, `self.ptr` addresses a byte range which does not - // wrap around the address space, and so the same is true of - // `ptr`. - // - By invariant, `self.ptr` is validly-aligned for `T`. Since - // `ptr` has the same address, and since the caller promises that - // the alignment of `U` is less than or equal to the alignment of - // `T`, `ptr` is validly-aligned for `U`. - // - By invariant, `A` is guaranteed to live for at least `'a`. - // - `U: 'a` - Ptr { ptr, _lifetime: PhantomData } - } - - /// Projects a field from `self`. - /// - /// # Safety - /// - /// ## Preconditions - /// - /// The caller promises that `projector` projects a well-aligned field - /// of its argument. Its argument will be `self` casted to a raw - /// pointer. - /// - /// ## Postconditions - /// - /// If the preconditions of this function are met, this function will - /// return a `Ptr` to the field projected from `self` by `projector`. - #[doc(hidden)] - #[inline] - pub unsafe fn project( - self, - projector: impl FnOnce(*mut T) -> *mut U, - ) -> Ptr<'a, U> { - // SAFETY: `projector` is provided with `self` casted to a raw - // pointer. - let field = projector(self.ptr.as_ptr()); - - // SAFETY: We promise that `projector` is provided with `self` - // casted to a raw pointer, and the caller promises that `projector` - // is a field projection of its argument. By invariant on `Ptr`, - // `self` is non-null, and by contract on `projector`, so too will - // its return value. - // - // Note that field projection cannot wrap around the address space - // to null. - // - // TODO(https://github.com/rust-lang/rust/pull/116675): Cite - // documentation that allocated objects do not wrap around the - // address space. - let field = unsafe { NonNull::new_unchecked(field) }; - - // SAFETY: The safety invariants of `Ptr` (see definition) are - // satisfied: - // 1. `field` is derived from a valid Rust allocation, because - // `self` is derived from a valid Rust allocation, by invariant - // on `Ptr`, and `projector` (by contract) is a field projection - // through `self`. - // 2. `field` has the same provenance as `self`, because it derived - // from `self` using a series of provenance-preserving - // operations. - // 3. `field` is entirely contained in the allocation of `self`, - // because it is derived by `projector`, which is (by contract) a - // field projection through `self`. - // 4. `field` addresses a byte range whose length fits in an - // `isize`, because it is a field projection through `self` and - // thus is entirely contained by `self`, which satisfies this - // invariant. - // 5. `field` addresses a byte range which does not wrap around the - // address space (see above). - // 6. `field` is validly-aligned for `U`, by contract on - // `projector`. - // 7. The allocation of `field` is guaranteed to live for at least - // `'a`, because `field` is entirely contained in `self`, which - // lives for at least `'a` by invariant on `Ptr`. - // 8. `U: 'a`, because `field` is an element within `T`, and `T: 'a` - // by invariant on `Ptr`. - Ptr { ptr: field, _lifetime: PhantomData } - } - } - - impl<'a> Ptr<'a, [u8]> { - /// Attempts to cast `self` to a `U` using the given cast type. - /// - /// Returns `None` if the resulting `U` would be invalidly-aligned or if - /// no `U` can fit in `self`. On success, returns a pointer to the - /// largest-possible `U` which fits in `self`. - /// - /// # Safety - /// - /// The caller may assume that this implementation is correct, and may - /// rely on that assumption for the soundness of their code. In - /// particular, the caller may assume that, if `try_cast_into` returns - /// `Some((ptr, split_at))`, then: - /// - If this is a prefix cast, `ptr` refers to the byte range `[0, - /// split_at)` in `self`. - /// - If this is a suffix cast, `ptr` refers to the byte range - /// `[split_at, self.len())` in `self`. - /// - /// # Panics - /// - /// Panics if `U` is a DST whose trailing slice element is zero-sized. - pub(crate) fn try_cast_into( - &self, - cast_type: _CastType, - ) -> Option<(Ptr<'a, U>, usize)> { - // PANICS: By invariant, the byte range addressed by `self.ptr` does - // not wrap around the address space. This implies that the sum of - // the address (represented as a `usize`) and length do not overflow - // `usize`, as required by `validate_cast_and_convert_metadata`. - // Thus, this call to `validate_cast_and_convert_metadata` won't - // panic. - let (elems, split_at) = U::LAYOUT.validate_cast_and_convert_metadata( - AsAddress::addr(self.ptr.as_ptr()), - self.len(), - cast_type, - )?; - let offset = match cast_type { - _CastType::_Prefix => 0, - _CastType::_Suffix => split_at, - }; - - let ptr = self.ptr.cast::().as_ptr(); - // SAFETY: `offset` is either `0` or `split_at`. - // `validate_cast_and_convert_metadata` promises that `split_at` is - // in the range `[0, self.len()]`. Thus, in both cases, `offset` is - // in `[0, self.len()]`. Thus: - // - The resulting pointer is in or one byte past the end of the - // same byte range as `self.ptr`. Since, by invariant, `self.ptr` - // addresses a byte range entirely contained within a single - // allocation, the pointer resulting from this operation is within - // or one byte past the end of that same allocation. - // - By invariant, `self.len() <= isize::MAX`. Since `offset <= - // self.len()`, `offset <= isize::MAX`. - // - By invariant, `self.ptr` addresses a byte range which does not - // wrap around the address space. This means that the base pointer - // plus the `self.len()` does not overflow `usize`. Since `offset - // <= self.len()`, this addition does not overflow `usize`. - let base = unsafe { ptr.add(offset) }; - // SAFETY: Since `add` is not allowed to wrap around, the preceding line - // produces a pointer whose address is greater than or equal to that of - // `ptr`. Since `ptr` is a `NonNull`, `base` is also non-null. - let base = unsafe { NonNull::new_unchecked(base) }; - let ptr = U::raw_from_ptr_len(base, elems); - // SAFETY: - // - By invariant, `self.ptr` is derived from some valid Rust - // allocation, `A`, and has the same provenance as `A`. All - // operations performed on `self.ptr` and values derived from it - // in this method preserve provenance, so: - // - `ptr` is derived from a valid Rust allocation, `A`. - // - `ptr` has the same provenance as `A`. - // - `validate_cast_and_convert_metadata` promises that the object - // described by `elems` and `split_at` lives at a byte range which - // is a subset of the input byte range. Thus: - // - Since, by invariant, `self.ptr` addresses a byte range - // entirely contained in `A`, so does `ptr`. - // - Since, by invariant, `self.ptr` addresses a range whose - // length is not longer than `isize::MAX` bytes, so does `ptr`. - // - Since, by invariant, `self.ptr` addresses a range which does - // not wrap around the address space, so does `ptr`. - // - `validate_cast_and_convert_metadata` promises that the object - // described by `split_at` is validly-aligned for `U`. - // - By invariant on `self`, `A` is guaranteed to live for at least - // `'a`. - // - `U: 'a` by trait bound. - Some((Ptr { ptr, _lifetime: PhantomData }, split_at)) - } - - /// Attempts to cast `self` into a `U`, failing if all of the bytes of - /// `self` cannot be treated as a `U`. - /// - /// In particular, this method fails if `self` is not validly-aligned - /// for `U` or if `self`'s size is not a valid size for `U`. - /// - /// # Safety - /// - /// On success, the caller may assume that the returned pointer - /// references the same byte range as `self`. - #[allow(unused)] - #[inline(always)] - pub(crate) fn try_cast_into_no_leftover( - &self, - ) -> Option> { - // TODO(#67): Remove this allow. See NonNulSlicelExt for more - // details. - #[allow(unstable_name_collisions)] - match self.try_cast_into(_CastType::_Prefix) { - Some((slf, split_at)) if split_at == self.len() => Some(slf), - Some(_) | None => None, - } - } - } - - impl<'a, T> Ptr<'a, [T]> { - /// The number of slice elements referenced by `self`. - /// - /// # Safety - /// - /// Unsafe code my rely on `len` satisfying the above contract. - fn len(&self) -> usize { - #[allow(clippy::as_conversions)] - let slc = self.ptr.as_ptr() as *const [()]; - // SAFETY: - // - `()` has alignment 1, so `slc` is trivially aligned. - // - `slc` was derived from a non-null pointer. - // - The size is 0 regardless of the length, so it is sound to - // materialize a reference regardless of location. - // - By invariant, `self.ptr` has valid provenance. - let slc = unsafe { &*slc }; - // This is correct because the preceding `as` cast preserves the - // number of slice elements. Per - // https://doc.rust-lang.org/nightly/reference/expressions/operator-expr.html#slice-dst-pointer-to-pointer-cast: - // - // For slice types like `[T]` and `[U]`, the raw pointer types - // `*const [T]`, `*mut [T]`, `*const [U]`, and `*mut [U]` encode - // the number of elements in this slice. Casts between these raw - // pointer types preserve the number of elements. Note that, as a - // consequence, such casts do *not* necessarily preserve the size - // of the pointer's referent (e.g., casting `*const [u16]` to - // `*const [u8]` will result in a raw pointer which refers to an - // object of half the size of the original). The same holds for - // `str` and any compound type whose unsized tail is a slice type, - // such as struct `Foo(i32, [u8])` or `(u64, Foo)`. - // - // TODO(#429), - // TODO(https://github.com/rust-lang/reference/pull/1417): Once this - // text is available on the Stable docs, cite those instead of the - // Nightly docs. - slc.len() - } - - pub(crate) fn iter(&self) -> impl Iterator> { - // TODO(#429): Once `NonNull::cast` documents that it preserves - // provenance, cite those docs. - let base = self.ptr.cast::().as_ptr(); - (0..self.len()).map(move |i| { - // TODO(https://github.com/rust-lang/rust/issues/74265): Use - // `NonNull::get_unchecked_mut`. - - // SAFETY: If the following conditions are not satisfied - // `pointer::cast` may induce Undefined Behavior [1]: - // > 1. Both the starting and resulting pointer must be either - // > in bounds or one byte past the end of the same allocated - // > object. - // > 2. The computed offset, in bytes, cannot overflow an - // > `isize`. - // > 3. The offset being in bounds cannot rely on “wrapping - // > around” the address space. That is, the - // > infinite-precision sum must fit in a `usize`. - // - // [1] https://doc.rust-lang.org/std/primitive.pointer.html#method.add - // - // We satisfy all three of these conditions here: - // 1. `base` (by invariant on `self`) points to an allocated - // object. By contract, `self.len()` accurately reflects the - // number of elements in the slice. `i` is in bounds of - // `c.len()` by construction, and so the result of this - // addition cannot overflow past the end of the allocation - // referred to by `c`. - // 2. By invariant on `Ptr`, `self` addresses a byte range whose - // length fits in an `isize`. Since `elem` is contained in - // `self`, the computed offset of `elem` must fit within - // `isize.` - // 3. By invariant on `Ptr`, `self` addresses a byte range which - // does not wrap around the address space. Since `elem` is - // contained in `self`, the computed offset of `elem` must - // wrap around the address space. - // - // TODO(#429): Once `pointer::add` documents that it preserves - // provenance, cite those docs. - let elem = unsafe { base.add(i) }; - - // SAFETY: - // - `elem` must not be null. `base` is constructed from a - // `NonNull` pointer, and the addition that produces `elem` - // must not overflow or wrap around, so `elem >= base > 0`. - // - // TODO(#429): Once `NonNull::new_unchecked` documents that it - // preserves provenance, cite those docs. - let elem = unsafe { NonNull::new_unchecked(elem) }; - - // SAFETY: The safety invariants of `Ptr` (see definition) are - // satisfied: - // 1. `elem` is derived from a valid Rust allocation, because - // `self` is derived from a valid Rust allocation, by - // invariant on `Ptr` - // 2. `elem` has the same provenance as `self`, because it - // derived from `self` using a series of - // provenance-preserving operations - // 3. `elem` is entirely contained in the allocation of `self` - // (see above) - // 4. `elem` addresses a byte range whose length fits in an - // `isize` (see above) - // 5. `elem` addresses a byte range which does not wrap around - // the address space (see above) - // 6. `elem` is validly-aligned for `T`. `self`, which - // represents a `[T]` is validly aligned for `T`, and `elem` - // is an element within that `[T]` - // 7. The allocation of `elem` is guaranteed to live for at - // least `'a`, because `elem` is entirely contained in - // `self`, which lives for at least `'a` by invariant on - // `Ptr`. - // 8. `T: 'a`, because `elem` is an element within `[T]`, and - // `[T]: 'a` by invariant on `Ptr` - Ptr { ptr: elem, _lifetime: PhantomData } - }) - } - } - - impl<'a, T: 'a + ?Sized> From<&'a T> for Ptr<'a, T> { - #[inline(always)] - fn from(t: &'a T) -> Ptr<'a, T> { - // SAFETY: `t` points to a valid Rust allocation, `A`, by - // construction. Thus: - // - `ptr` is derived from `A` - // - Since we use `NonNull::from`, which preserves provenance, `ptr` - // has the same provenance as `A` - // - Since `NonNull::from` creates a pointer which addresses the - // same bytes as `t`, `ptr` addresses a byte range entirely - // contained in (in this case, identical to) `A` - // - Since `t: &T`, it addresses no more than `isize::MAX` bytes [1] - // - Since `t: &T`, it addresses a byte range which does not wrap - // around the address space [2] - // - Since it is constructed from a valid `&T`, `ptr` is - // validly-aligned for `T` - // - Since `t: &'a T`, the allocation `A` is guaranteed to live for - // at least `'a` - // - `T: 'a` by trait bound - // - // TODO(#429), - // TODO(https://github.com/rust-lang/rust/issues/116181): Once it's - // documented, reference the guarantee that `NonNull::from` - // preserves provenance. - // - // TODO(#429), - // TODO(https://github.com/rust-lang/unsafe-code-guidelines/issues/465): - // - [1] Where does the reference document that allocations fit in - // `isize`? - // - [2] Where does the reference document that allocations don't - // wrap around the address space? - Ptr { ptr: NonNull::from(t), _lifetime: PhantomData } - } - } - - impl<'a, T: 'a + ?Sized> Debug for Ptr<'a, T> { - #[inline] - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - self.ptr.fmt(f) - } - } - - #[cfg(test)] - mod tests { - use core::mem::{self, MaybeUninit}; - - use super::*; - use crate::{util::testutil::AU64, FromBytes}; - - #[test] - fn test_ptrtry_cast_into_soundness() { - // This test is designed so that if `Ptr::try_cast_into_xxx` are - // buggy, it will manifest as unsoundness that Miri can detect. - - // - If `size_of::() == 0`, `N == 4` - // - Else, `N == 4 * size_of::()` - fn test() { - let mut bytes = [MaybeUninit::::uninit(); N]; - let initialized = [MaybeUninit::new(0u8); N]; - for start in 0..=bytes.len() { - for end in start..=bytes.len() { - // Set all bytes to uninitialized other than those in - // the range we're going to pass to `try_cast_from`. - // This allows Miri to detect out-of-bounds reads - // because they read uninitialized memory. Without this, - // some out-of-bounds reads would still be in-bounds of - // `bytes`, and so might spuriously be accepted. - bytes = [MaybeUninit::::uninit(); N]; - let bytes = &mut bytes[start..end]; - // Initialize only the byte range we're going to pass to - // `try_cast_from`. - bytes.copy_from_slice(&initialized[start..end]); - - let bytes = { - let bytes: *const [MaybeUninit] = bytes; - #[allow(clippy::as_conversions)] - let bytes = bytes as *const [u8]; - // SAFETY: We just initialized these bytes to valid - // `u8`s. - unsafe { &*bytes } - }; - - /// # Safety - /// - /// - `slf` must reference a byte range which is - /// entirely initialized. - /// - `slf` must reference a byte range which is only - /// referenced by shared references which do not - /// contain `UnsafeCell`s during its lifetime. - unsafe fn validate_and_get_len( - slf: Ptr<'_, T>, - ) -> usize { - // SAFETY: - // - Since all bytes in `slf` are initialized and - // `T: FromBytes`, `slf` contains a valid `T`. - // - The caller promises that the referenced memory - // is not also referenced by any mutable - // references. - // - The caller promises that the referenced memory - // is not also referenced as a type which contains - // `UnsafeCell`s. - let t = unsafe { slf.as_ref() }; - - let bytes = { - let len = mem::size_of_val(t); - let t: *const T = t; - // SAFETY: - // - We know `t`'s bytes are all initialized - // because we just read it from `slf`, which - // points to an initialized range of bytes. If - // there's a bug and this doesn't hold, then - // that's exactly what we're hoping Miri will - // catch! - // - Since `T: FromBytes`, `T` doesn't contain - // any `UnsafeCell`s, so it's okay for `t: T` - // and a `&[u8]` to the same memory to be - // alive concurrently. - unsafe { core::slice::from_raw_parts(t.cast::(), len) } - }; - - // This assertion ensures that `t`'s bytes are read - // and compared to another value, which in turn - // ensures that Miri gets a chance to notice if any - // of `t`'s bytes are uninitialized, which they - // shouldn't be (see the comment above). - assert_eq!(bytes, vec![0u8; bytes.len()]); - - mem::size_of_val(t) - } - - for cast_type in [_CastType::_Prefix, _CastType::_Suffix] { - if let Some((slf, split_at)) = - Ptr::from(bytes).try_cast_into::(cast_type) - { - // SAFETY: All bytes in `bytes` have been - // initialized. - let len = unsafe { validate_and_get_len(slf) }; - match cast_type { - _CastType::_Prefix => assert_eq!(split_at, len), - _CastType::_Suffix => assert_eq!(split_at, bytes.len() - len), - } - } - } - - if let Some(slf) = Ptr::from(bytes).try_cast_into_no_leftover::() { - // SAFETY: All bytes in `bytes` have been - // initialized. - let len = unsafe { validate_and_get_len(slf) }; - assert_eq!(len, bytes.len()); - } - } - } - } - - macro_rules! test { - ($($ty:ty),*) => { - $({ - const S: usize = core::mem::size_of::<$ty>(); - const N: usize = if S == 0 { 4 } else { S * 4 }; - test::(); - // We don't support casting into DSTs whose trailing slice - // element is a ZST. - if S > 0 { - test::(); - } - // TODO: Test with a slice DST once we have any that - // implement `KnownLayout + FromBytes`. - })* - }; - } - - test!(()); - test!(u8, u16, u32, u64, u128, usize, AU64); - test!(i8, i16, i32, i64, i128, isize); - test!(f32, f64); - } - } -} - pub(crate) trait AsAddress { fn addr(self) -> usize; } diff --git a/zerocopy-derive/src/lib.rs b/zerocopy-derive/src/lib.rs index 7a2005d1fa..a806b420d9 100644 --- a/zerocopy-derive/src/lib.rs +++ b/zerocopy-derive/src/lib.rs @@ -356,14 +356,17 @@ fn derive_try_from_bytes_struct(ast: &DeriveInput, strct: &DataStruct) -> proc_m // validity of a struct is just the composition of the bit // validities of its fields, so this is a sound implementation of // `is_bit_valid`. - unsafe fn is_bit_valid(candidate: zerocopy::Ptr) -> bool { + fn is_bit_valid(candidate: zerocopy::MaybeValid) -> bool { true #(&& { - let project = |slf: *mut Self| ::core::ptr::addr_of_mut!((*slf).#field_names); - // SAFETY: `project` is a field projection of `candidate`. // The projected field will be well-aligned because this // derive rejects packed types. - let field_candidate = unsafe { candidate.project(project) }; + let field_candidate = unsafe { + let project = |slf: *mut Self| + ::core::ptr::addr_of_mut!((*slf).#field_names); + + candidate.project(project) + }; // SAFETY: The below invocation of `is_bit_valid` satisfies // the safety preconditions of `is_bit_valid`: diff --git a/zerocopy-derive/tests/struct_try_from_bytes.rs b/zerocopy-derive/tests/struct_try_from_bytes.rs index 2a659947ac..2a01b38010 100644 --- a/zerocopy-derive/tests/struct_try_from_bytes.rs +++ b/zerocopy-derive/tests/struct_try_from_bytes.rs @@ -30,10 +30,9 @@ assert_impl_all!(Zst: TryFromBytes); #[test] fn zst() { // TODO(#5): Use `try_transmute` in this test once it's available. - let candidate = zerocopy::Ptr::from(&Zst); - // SAFETY: `candidate` is derived from the tested type, so it will contain - // `UnsafeCell`s at the same offsets as the tested type. - let is_bit_valid = unsafe { Zst::is_bit_valid(candidate) }; + let candidate = zerocopy::Ptr::from_ref(&Zst); + let candidate = candidate.forget_aligned().forget_valid(); + let is_bit_valid = Zst::is_bit_valid(candidate); assert!(is_bit_valid); } @@ -47,10 +46,9 @@ assert_impl_all!(One: TryFromBytes); #[test] fn one() { // TODO(#5): Use `try_transmute` in this test once it's available. - let candidate = zerocopy::Ptr::from(&One { a: 42 }); - // SAFETY: `candidate` is derived from the tested type, so it will contain - // `UnsafeCell`s at the same offsets as the tested type. - let is_bit_valid = unsafe { One::is_bit_valid(candidate) }; + let candidate = zerocopy::Ptr::from_ref(&One { a: 42 }); + let candidate = candidate.forget_aligned().forget_valid(); + let is_bit_valid = One::is_bit_valid(candidate); assert!(is_bit_valid); } @@ -65,17 +63,18 @@ assert_impl_all!(Two: TryFromBytes); #[test] fn two() { // TODO(#5): Use `try_transmute` in this test once it's available. - let candidate = zerocopy::Ptr::from(&Two { a: false, b: Zst }); - // SAFETY: `candidate` is derived from the tested type, so it will contain - // `UnsafeCell`s at the same offsets as the tested type. - let is_bit_valid = unsafe { Two::is_bit_valid(candidate) }; + let candidate = zerocopy::Ptr::from_ref(&Two { a: false, b: Zst }); + let candidate = candidate.forget_aligned().forget_valid(); + let is_bit_valid = Two::is_bit_valid(candidate); assert!(is_bit_valid); } #[test] fn two_bad() { // TODO(#5): Use `try_transmute` in this test once it's available. - let candidate = zerocopy::Ptr::from(&[2u8][..]); + let candidate = zerocopy::Ptr::from_ref(&[2u8][..]); + let candidate = candidate.forget_aligned().forget_valid(); + // SAFETY: // - The cast `cast(p)` is implemented exactly as follows: `|p: *mut T| p as // *mut U`. @@ -83,9 +82,11 @@ fn two_bad() { // the size of the object referenced by `self`. // - The alignment of `Unsized` is equal to the alignment of `[u8]`. let candidate = unsafe { candidate.cast_unsized(|p| p as *mut Two) }; - // SAFETY: `candidate` is derived from the tested type, so it will contain - // `UnsafeCell`s at the same offsets as the tested type. - let is_bit_valid = unsafe { Two::is_bit_valid(candidate) }; + + // SAFETY: `candidate`'s referent is as-initialized as `Two`. + let candidate = unsafe { candidate.assume_as_initialized() }; + + let is_bit_valid = Two::is_bit_valid(candidate); assert!(!is_bit_valid); } @@ -99,7 +100,9 @@ assert_impl_all!(Unsized: TryFromBytes); #[test] fn un_sized() { // TODO(#5): Use `try_transmute` in this test once it's available. - let candidate = zerocopy::Ptr::from(&[16, 12, 42][..]); + let candidate = zerocopy::Ptr::from_ref(&[16, 12, 42][..]); + let candidate = candidate.forget_aligned().forget_valid(); + // SAFETY: // - The cast `cast(p)` is implemented exactly as follows: `|p: *mut T| p as // *mut U`. @@ -107,9 +110,10 @@ fn un_sized() { // the size of the object referenced by `self`. // - The alignment of `Unsized` is equal to the alignment of `[u8]`. let candidate = unsafe { candidate.cast_unsized(|p| p as *mut Unsized) }; - // SAFETY: `candidate` is derived from the tested type, so it will contain - // `UnsafeCell`s at the same offsets as the tested type. - let is_bit_valid = unsafe { Unsized::is_bit_valid(candidate) }; + + // SAFETY: `candidate`'s referent is as-initialized as `Two`. + let candidate = unsafe { candidate.assume_as_initialized() }; + let is_bit_valid = Unsized::is_bit_valid(candidate); assert!(is_bit_valid); }