diff --git a/src/impls.rs b/src/impls.rs index 91ea3fe991..e9c45732f2 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -101,19 +101,11 @@ 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]. Neither `r` nor `t` - /// contain `UnsafeCell`s because neither `bool` nor `u8` do [4]. - /// - Since the closure takes a `&u8` argument, given a `Maybe<'a, - /// bool>` which satisfies the preconditions of - /// `TryFromBytes::::is_bit_valid`, it must be guaranteed that the - /// 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`. + /// contain `UnsafeCell`s because neither `bool` nor `u8` do [3]. /// - The impl must only return `true` for its argument if the original - /// `Maybe` 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] + /// `Maybe` 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`. + /// [2] /// /// [1] Per https://doc.rust-lang.org/1.81.0/reference/type-layout.html#primitive-data-layout: /// @@ -124,16 +116,12 @@ safety_comment! { /// | `bool` | 1 | /// | `u8`/`i8` | 1 | /// - /// [2] Per https://doc.rust-lang.org/1.81.0/reference/type-layout.html#size-and-alignment: - /// - /// The size of a value is always a multiple of its alignment. - /// - /// [3] Per https://doc.rust-lang.org/1.81.0/reference/types/boolean.html: + /// [2] Per https://doc.rust-lang.org/1.81.0/reference/types/boolean.html: /// /// The value false has the bit pattern 0x00 and the value true has the /// bit pattern 0x01. /// - /// [4] TODO(#429): Justify this claim. + /// [3] TODO(#429): Justify this claim. unsafe_impl!(bool: TryFromBytes; |byte: MaybeAligned| *byte.unaligned_as_ref() < 2); } safety_comment! { @@ -155,20 +143,10 @@ 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]. Neither `r` nor `t` contain - /// `UnsafeCell`s because neither `char` nor `u32` do [4]. - /// - Since the closure takes a `&u32` argument, given a `Maybe<'a, - /// char>` which satisfies the preconditions of - /// `TryFromBytes::::is_bit_valid`, it must be guaranteed that the - /// 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`. + /// `UnsafeCell`s because neither `char` nor `u32` do [3]. /// - The impl must only return `true` for its argument if the original - /// `Maybe` refers to a valid `char`. `char::from_u32` - /// guarantees that it returns `None` if its input is not a valid - /// `char`. [3] + /// `Maybe` refers to a valid `char`. `char::from_u32` guarantees + /// that it returns `None` if its input is not a valid `char`. [2] /// /// [1] Per https://doc.rust-lang.org/nightly/reference/types/textual.html#layout-and-bit-validity: /// @@ -177,14 +155,10 @@ safety_comment! { /// /// [2] Per https://doc.rust-lang.org/core/primitive.char.html#method.from_u32: /// - /// Every byte of a `char` is guaranteed to be initialized. - /// - /// [3] Per https://doc.rust-lang.org/core/primitive.char.html#method.from_u32: - /// /// `from_u32()` will return `None` if the input is not a valid value for /// a `char`. /// - /// [4] TODO(#429): Justify this claim. + /// [3] TODO(#429): Justify this claim. unsafe_impl!(char: TryFromBytes; |candidate: MaybeAligned| { let candidate = candidate.read_unaligned::(); char::from_u32(candidate).is_some() @@ -215,19 +189,9 @@ safety_comment! { /// `str` and `[u8]` have the same representation. [1] Neither `t` nor /// `r` contain `UnsafeCell`s because `[u8]` doesn't, and both `t` and /// `r` have that representation. - /// - Since the closure takes a `&[u8]` argument, given a `Maybe<'a, - /// str>` which satisfies the preconditions of - /// `TryFromBytes::::is_bit_valid`, it must be guaranteed that the - /// 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 - /// `Maybe` refers to a valid `str`. `str::from_utf8` - /// guarantees that it returns `Err` if its input is not a valid `str`. - /// [2] + /// `Maybe` 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/1.81.0/reference/types/textual.html: /// @@ -296,18 +260,9 @@ safety_comment! { /// because `NonZeroXxx` and `xxx` have the same size. [1] Neither `r` /// nor `t` refer to any `UnsafeCell`s because neither `NonZeroXxx` [2] /// nor `xxx` do. - /// - Since the closure takes a `&xxx` argument, given a `Maybe<'a, - /// NonZeroXxx>` which satisfies the preconditions of - /// `TryFromBytes::::is_bit_valid`, it must be guaranteed - /// that the memory referenced by that `MabyeValid` always contains a - /// valid `xxx`. Since `NonZeroXxx`'s bytes are always initialized [1], - /// `is_bit_valid`'s precondition requires that the same is true of its - /// argument. Since `xxx`'s only bit validity invariant is that its - /// bytes must be initialized, this memory is guaranteed to contain a - /// valid `xxx`. /// - The impl must only return `true` for its argument if the original - /// `Maybe` refers to a valid `NonZeroXxx`. The only - /// `xxx` which is not also a valid `NonZeroXxx` is 0. [1] + /// `Maybe` 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/1.81.0/core/num/type.NonZeroU16.html: /// diff --git a/src/lib.rs b/src/lib.rs index 00c154d293..71193d661b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2845,7 +2845,12 @@ unsafe fn try_read_from( let c_ptr = Ptr::from_mut(&mut candidate); let c_ptr = c_ptr.transparent_wrapper_into_inner(); // SAFETY: `c_ptr` has no uninitialized sub-ranges because it derived from - // `candidate`, which the caller promises is entirely initialized. + // `candidate`, which the caller promises is entirely initialized. Since + // `candidate` is a `MaybeUninit`, it has no validity requirements, and so + // no values written to `c_ptr` can violate its validity. Since `c_ptr` has + // `Exclusive` aliasing, no mutations may happen except via `c_ptr` so long + // as it is live, so we don't need to worry about the fact that `c_ptr` may + // have more restricted validity than `candidate`. let c_ptr = unsafe { c_ptr.assume_validity::() }; // This call may panic. If that happens, it doesn't cause any soundness @@ -4603,7 +4608,12 @@ pub unsafe trait FromBytes: FromZeros { let ptr = Ptr::from_mut(&mut buf); // SAFETY: After `buf.zero()`, `buf` consists entirely of initialized, - // zeroed bytes. + // zeroed bytes. Since `MaybeUninit` has no validity requirements, `ptr` + // cannot be used to write values which will violate `buf`'s bit + // validity. Since `ptr` has `Exclusive` aliasing, nothing other than + // `ptr` may be used to mutate `ptr`'s referent, and so its bit validity + // cannot be violated even though `buf` may have more permissive bit + // validity than `ptr`. let ptr = unsafe { ptr.assume_validity::() }; let ptr = ptr.as_bytes::(); src.read_exact(ptr.as_mut())?; diff --git a/src/pointer/invariant.rs b/src/pointer/invariant.rs index f8d184b02c..b5bb3fd6c7 100644 --- a/src/pointer/invariant.rs +++ b/src/pointer/invariant.rs @@ -42,7 +42,48 @@ pub trait Aliasing: Sealed { pub trait Alignment: Sealed {} /// The validity invariant of a [`Ptr`][super::Ptr]. -pub trait Validity: Sealed {} +/// +/// # Safety +/// +/// In this section, we will use `Ptr` as a shorthand for `Ptr>` for brevity. +/// +/// Each `V: Validity` defines a set of bit values which may appear in the +/// referent of a `Ptr`, denoted `S(T, V)`. Each `V: Validity`, in its +/// documentation, provides a definition of `S(T, V)` which must be valid for +/// all `T: ?Sized`. Any `V: Validity` must guarantee that this set is only a +/// function of the *bit validity* of the referent type, `T`, and not of any +/// other property of `T`. As a consequence, given `V: Validity`, `T`, and `U` +/// where `T` and `U` have the same bit validity, `S(V, T) = S(V, U)`. +/// +/// It is guaranteed that the referent of any `ptr: Ptr` is a member of +/// `S(T, V)`. Unsafe code must ensure that this guarantee will be upheld for +/// any existing `Ptr`s or any `Ptr`s that that code creates. +/// +/// An important implication of this guarantee is that it restricts what +/// transmutes are sound, where "transmute" is used in this context to refer to +/// changing the referent type or validity invariant of a `Ptr`, as either +/// change may change the set of bit values permitted to appear in the referent. +/// In particular, the following are necessary (but not sufficient) conditions +/// in order for a transmute from `src: Ptr` to `dst: Ptr` to be +/// sound: +/// - If `S(T, V) = S(U, W)`, then no restrictions apply; otherwise, +/// - If `dst` permits mutation of its referent (e.g. via `Exclusive` aliasing +/// or interior mutation under `Shared` aliasing), then it must hold that +/// `S(T, V) ⊇ S(U, W)` - in other words, the transmute must not expand the +/// set of allowed referent bit patterns. A violation of this requirement +/// would permit using `dst` to write `x` where `x ∈ S(U, W)` but `x ∉ S(T, +/// V)`, which would violate the guarantee that `src`'s referent may only +/// contain values in `S(T, V)`. +/// - If the referent may be mutated without going through `dst` while `dst` is +/// live (e.g. via interior mutation on a `Shared`-aliased `Ptr` or `&` +/// reference), then it must hold that `S(T, V) ⊆ S(U, W)` - in other words, +/// the transmute must not shrink the set of allowed referent bit patterns. A +/// violation of this requirement would permit using `src` or another +/// mechanism (e.g. a `&` reference used to derive `src`) to write `x` where +/// `x ∈ S(T, V)` but `x ∉ S(U, W)`, which would violate the guarantee that +/// `dst`'s referent may only contain values in `S(U, W)`. +pub unsafe trait Validity: Sealed {} /// An [`Aliasing`] invariant which is either [`Shared`] or [`Exclusive`]. /// @@ -90,9 +131,14 @@ impl Alignment for Aligned {} /// Any bit pattern is allowed in the `Ptr`'s referent, including uninitialized /// bytes. pub enum Uninit {} -impl Validity for Uninit {} - -/// The byte ranges initialized in `T` are also initialized in the referent. +// SAFETY: `Uninit`'s validity is well-defined for all `T: ?Sized`, and is not a +// function of any property of `T` other than its bit validity (in fact, it's +// not even a property of `T`'s bit validity, but this is more than we are +// required to uphold). +unsafe impl Validity for Uninit {} + +/// The byte ranges initialized in `T` are also initialized in the referent of a +/// `Ptr`. /// /// Formally: uninitialized bytes may only be present in `Ptr`'s referent /// where they are guaranteed to be present in `T`. This is a dynamic property: @@ -119,16 +165,24 @@ impl Validity for Uninit {} /// enum type, in which case the same rules apply depending on the state of /// its discriminant, and so on recursively). pub enum AsInitialized {} -impl Validity for AsInitialized {} +// SAFETY: `AsInitialized`'s validity is well-defined for all `T: ?Sized`, and +// is not a function of any property of `T` other than its bit validity. +unsafe impl Validity for AsInitialized {} /// The byte ranges in the referent are fully initialized. In other words, if /// the referent is `N` bytes long, then it contains a bit-valid `[u8; N]`. pub enum Initialized {} -impl Validity for Initialized {} +// SAFETY: `Initialized`'s validity is well-defined for all `T: ?Sized`, and is +// not a function of any property of `T` other than its bit validity (in fact, +// it's not even a property of `T`'s bit validity, but this is more than we are +// required to uphold). +unsafe impl Validity for Initialized {} -/// The referent is bit-valid for `T`. +/// The referent of a `Ptr` is bit-valid for `T`. pub enum Valid {} -impl Validity for Valid {} +// SAFETY: `Valid`'s validity is well-defined for all `T: ?Sized`, and is not a +// function of any property of `T` other than its bit validity. +unsafe impl Validity for Valid {} /// # Safety /// diff --git a/src/pointer/mod.rs b/src/pointer/mod.rs index 00fdcb0af4..d2bc8727ab 100644 --- a/src/pointer/mod.rs +++ b/src/pointer/mod.rs @@ -50,7 +50,7 @@ where T: Copy, T: invariant::Read, { - // SAFETY: By invariant on `MaybeAligned`, `raw` contains + // SAFETY: By invariant on `MaybeAligned`, `self` contains // validly-initialized data for `T`. By `T: Read`, we are // permitted to perform a read of `self`'s referent. unsafe { self.as_inner().read_unaligned() } diff --git a/src/pointer/ptr.rs b/src/pointer/ptr.rs index 9b1155b160..a3d487ce54 100644 --- a/src/pointer/ptr.rs +++ b/src/pointer/ptr.rs @@ -141,7 +141,21 @@ mod _external { /// SAFETY: Shared pointers are safely `Copy`. `Ptr`'s other invariants /// (besides aliasing) are unaffected by the number of references that exist - /// to `Ptr`'s referent. + /// to `Ptr`'s referent. The notable cases are: + /// - Alignment is a property of the referent type (`T`) and the address, + /// both of which are unchanged + /// - Let `S(T, V)` be the set of bit values permitted to appear in the + /// referent of a `Ptr>`. Since this copy + /// does not change `I::Validity` or `T`, `S(T, I::Validity)` is also + /// unchanged. + /// + /// We are required to guarantee that the referents of the original `Ptr` + /// and of the copy (which, of course, are actually the same since they + /// live in the same byte address range) both remain in the set `S(T, + /// I::Validity)`. Since this invariant holds on the original `Ptr`, it + /// cannot be violated by the original `Ptr`, and thus the original `Ptr` + /// cannot be used to violate this invariant on the copy. The inverse + /// holds as well. impl<'a, T, I> Copy for Ptr<'a, T, I> where T: 'a + ?Sized, @@ -149,9 +163,7 @@ mod _external { { } - /// SAFETY: Shared pointers are safely `Clone`. `Ptr`'s other invariants - /// (besides aliasing) are unaffected by the number of references that exist - /// to `Ptr`'s referent. + /// SAFETY: See the safety comment on `Copy`. impl<'a, T, I> Clone for Ptr<'a, T, I> where T: 'a + ?Sized, @@ -194,8 +206,15 @@ mod _conversions { // invariant of `Shared`. // 1. `ptr`, by invariant on `&'a T`, conforms to the alignment // invariant of `Aligned`. - // 2. `ptr`, by invariant on `&'a T`, conforms to the validity - // invariant of `Valid`. + // 2. `ptr`'s referent, by invariant on `&'a T`, is a bit-valid `T`. + // This satisfies the requirement that a `Ptr` + // point to a bit-valid `T`. Even if `T` permits interior + // mutation, this invariant guarantees that the returned `Ptr` + // can only ever be used to modify the referent to store + // bit-valid `T`s, which ensures that the returned `Ptr` cannot + // be used to violate the soundness of the original `ptr: &'a T` + // or of any other references that may exist to the same + // referent. unsafe { Self::from_inner(inner) } } } @@ -214,8 +233,13 @@ mod _conversions { // invariant of `Exclusive`. // 1. `ptr`, by invariant on `&'a mut T`, conforms to the alignment // invariant of `Aligned`. - // 2. `ptr`, by invariant on `&'a mut T`, conforms to the validity - // invariant of `Valid`. + // 2. `ptr`'s referent, by invariant on `&'a mut T`, is a bit-valid + // `T`. This satisfies the requirement that a `Ptr` point to a bit-valid `T`. This invariant guarantees + // that the returned `Ptr` can only ever be used to modify the + // referent to store bit-valid `T`s, which ensures that the + // returned `Ptr` cannot be used to violate the soundness of the + // original `ptr: &'a mut T`. unsafe { Self::from_inner(inner) } } } @@ -251,9 +275,9 @@ mod _conversions { // // This is ensured by contract on all `PtrInner`s. // - // 3. The pointer must point to an initialized instance of `T`. This - // is ensured by-contract on `Ptr`, because the `I::Validity` is - // `Valid`. + // 3. The pointer must point to a validly-initialized instance of + // `T`. This is ensured by-contract on `Ptr`, because the + // `I::Validity` is `Valid`. // // 4. You must enforce Rust’s aliasing rules. This is ensured by // contract on `Ptr`, because `I::Aliasing: Reference`. Either it @@ -289,11 +313,17 @@ mod _conversions { // hold of `ptr = self.as_inner()`: // 0. SEE BELOW. // 1. `ptr` conforms to the alignment invariant of - // [`I::Alignment`](invariant::Alignment). + // [`I::Alignment`](invariant::Alignment). // 2. `ptr` conforms to the validity invariant of - // [`I::Validity`](invariant::Validity). + // [`I::Validity`](invariant::Validity). `self` and the returned + // `Ptr` permit the same bit values in their referents since they + // have the same referent type (`T`) and the same validity + // (`I::Validity`). Thus, regardless of what mutation is + // permitted (`Exclusive` aliasing or `Shared`-aliased interior + // mutation), neither can be used to write a value to the + // referent which violates the other's validity invariant. // - // For aliasing (6 above), since `I::Aliasing: Reference`, + // For aliasing (0 above), since `I::Aliasing: Reference`, // there are two cases for `I::Aliasing`: // - For `invariant::Shared`: `'a` outlives `'b`, and so the // returned `Ptr` does not permit accessing the referent any @@ -339,9 +369,9 @@ mod _conversions { // // This is ensured by contract on all `PtrInner`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`. + // 3. The pointer must point to a validly-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 @@ -421,7 +451,7 @@ mod _conversions { /// soundness requirement that is a function of `T`, `U`, /// `I::Aliasing`, `I::Validity`, and `V`, and may depend upon the /// presence, absence, or specific location of `UnsafeCell`s in `T` - /// and/or `U`. + /// and/or `U`. See [`Validity`] for more details. #[doc(hidden)] #[inline] pub unsafe fn transmute_unchecked( @@ -510,7 +540,16 @@ mod _conversions { // the same byte range as `p`. // - By the same argument, the returned pointer refers to // `UnsafeCell`s at the same locations as `p`. - // - `Unalign` promises to have the same bit validity as `T` + // - `Unalign` promises to have the same bit validity as `T`. By + // invariant on `Validity`, the set of bit patterns allowed in the + // referent of a `Ptr` is only a function of the + // validity of `X` and of `V`. Thus, the set of bit patterns + // allowed in the referent of a `Ptr` is + // the same as the set of bit patterns allowed in the referent of + // a `Ptr, (_, _, I::Validity)>`. As a result, `self` + // and the returned `Ptr` permit the same set of bit patterns in + // their referents, and so neither can be used to violate the + // validity of the other. let ptr = unsafe { #[allow(clippy::as_conversions)] self.transmute_unchecked(|p: *mut T| p as *mut crate::Unalign) @@ -734,13 +773,12 @@ mod _transitions { T: crate::IntoBytes + crate::FromBytes, I: Invariants, { - // SAFETY: The `T: IntoBytes` bound ensures that any bit-valid `T` - // is entirely initialized. `I: Invariants` - // ensures that `self`'s referent is a bit-valid `T`. Producing an - // `Initialized` `Ptr` may permit the caller to write arbitrary - // initialized bytes to the referent (depending on aliasing mode and - // presence of `UnsafeCell`s). `T: FromBytes` ensures that any byte - // sequence written will remain a bit-valid `T`. + // SAFETY: The `T: IntoBytes + FromBytes` bound ensures that `T`'s + // bit validity is equivalent to `[u8]`. In other words, the set of + // allowed referents for a `Ptr` is the set of + // initialized bit patterns. The same is true of the set of allowed + // referents for any `Ptr<_, (_, _, Initialized)>`. Thus, this call + // does not change the set of allowed values in the referent. unsafe { self.assume_initialized() } } @@ -757,13 +795,21 @@ mod _transitions { T: crate::IntoBytes + crate::Immutable, I: Invariants, { - // SAFETY: The `T: IntoBytes` bound ensures that any bit-valid `T` - // is entirely initialized. `I: Invariants` - // ensures that `self`'s referent is a bit-valid `T`. Since `T: - // Immutable` and the aliasing is `Shared`, the resulting `Ptr` - // cannot be used to modify the referent, and so it's acceptable - // that going from `Valid` to `Initialized` may increase the set of - // values allowed in the referent. + // SAFETY: Let `O` (for "old") be the set of allowed bit patterns in + // `self`'s referent, and let `N` (for "new") be the set of allowed + // bit patterns in the referent of the returned `Ptr`. `T: + // IntoBytes` and `I: Invariants` ensures that `O` + // cannot contain any uninitialized bit patterns. Since the returned + // `Ptr` has validity `Initialized`, `N` is equal to the set of all + // initialized bit patterns. Thus, `O` is a subset of `N`, and so + // the returned `Ptr`'s validity invariant is upheld. + // + // Since `T: Immutable` and aliasing is `Shared`, the returned `Ptr` + // cannot be used to modify the referent. Before this call, `self`'s + // referent is guaranteed by invariant on `Ptr` to satisfy `self`'s + // validity invariant. Since the returned `Ptr` cannot be used to + // modify the referent, this guarantee cannot be violated by the + // returned `Ptr` (even if `O` is a strict subset of `N`). unsafe { self.assume_initialized() } } @@ -778,10 +824,9 @@ mod _transitions { T: crate::FromBytes, I: Invariants, { - // SAFETY: The bound `T: FromBytes` ensures that any initialized - // sequence of bytes is bit-valid for `T`. `I: Invariants` ensures that all of the referent bytes - // are initialized. + // TODO(#1866): Fix this unsoundness. + + // SAFETY: This is unsound! unsafe { self.assume_valid() } } @@ -812,6 +857,10 @@ mod _transitions { if T::is_bit_valid(self.reborrow().forget_aligned()) { // SAFETY: If `T::is_bit_valid`, code may assume that `self` // contains a bit-valid instance of `Self`. + // + // TODO(#1866): This is unsound! The returned `Ptr` may permit + // writing referents which do not satisfy the `Initialized` + // validity invariant of `self`. Ok(unsafe { self.assume_valid() }) } else { Err(ValidityError::new(self)) @@ -875,7 +924,9 @@ mod _casts { // cases, the bit validity `I::Validity` has the same semantics // regardless of referent type. In other words, the set of allowed // referent values for `Ptr` and `Ptr` are identical. + // (_, _, I::Validity)>` are identical. As a consequence, neither + // `self` nor the returned `Ptr` can be used to write values which + // are invalid for the other. unsafe { self.transmute_unchecked(cast) } } @@ -944,13 +995,7 @@ mod _casts { }; let ptr = ptr.bikeshed_recall_aligned(); - - // SAFETY: `ptr`'s referent begins as `Initialized`, denoting that - // all bytes of the referent are initialized bytes. The referent - // type is then casted to `[u8]`, whose only validity invariant is - // that its bytes are initialized. This validity invariant is - // satisfied by the `Initialized` invariant on the starting `ptr`. - unsafe { ptr.assume_validity::() } + ptr.bikeshed_recall_valid() } } @@ -970,8 +1015,27 @@ mod _casts { // `I::Aliasing` because `self` does. // 7. By the above lemma, `slice` conforms to the alignment // invariant of `I::Alignment` because `self` does. - // 8. By the above lemma, `slice` conforms to the validity invariant - // of `I::Validity` because `self` does. + // 8. Since `[T; N]` and `[T]` have the same bit validity [1][2], + // and since `self` and the returned `Ptr` have the same validity + // invariant, neither `self` nor the returned `Ptr` can be used + // to write a value to the referent which violates the other's + // validity invariant. + // + // [1] Per https://doc.rust-lang.org/1.81.0/reference/type-layout.html#array-layout: + // + // 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 `nth` element of the array is offset from the start + // of the array by `n * size_of::()` bytes. + // + // ... + // + // Slices have the same layout as the section of the array they + // slice. + // + // [2] Per https://doc.rust-lang.org/1.81.0/reference/types/array.html#array-types: + // + // All elements of arrays are always initialized unsafe { Ptr::from_inner(slice) } } } @@ -1040,9 +1104,10 @@ mod _casts { // it is derived from `try_cast_into`, which promises that the // object described by `target` is validly aligned for `U`. // 2. By trait bound, `self` - and thus `target` - is a bit-valid - // `[u8]`. All bit-valid `[u8]`s have all of their bytes - // initialized, so `ptr` conforms to the validity invariant of - // `Initialized`. + // `[u8]`. `Ptr<[u8], (_, _, Valid)>` and `Ptr<_, (_, _, + // Initialized)>` have the same bit validity, and so neither + // `self` nor `res` can be used to write a value to the referent + // which violates the other's validity invariant. let res = unsafe { Ptr::from_inner(inner) }; // SAFETY: @@ -1052,7 +1117,9 @@ mod _casts { // 1. `[u8]` has no alignment requirement. // 2. `self` has validity `Valid` and has type `[u8]`. Since // `remainder` references a subset of `self`'s referent, it is - // also bit-valid. + // also a bit-valid `[u8]`. Thus, neither `self` nor `remainder` + // can be used to write a value to the referent which violates + // the other's validity invariant. let remainder = unsafe { Ptr::from_inner(remainder) }; Ok((res, remainder)) @@ -1124,15 +1191,22 @@ mod _casts { #[inline(always)] pub fn get_mut(self) -> Ptr<'a, T, I> { // SAFETY: - // - The closure uses an `as` cast, which preserves address - // range and provenance. + // - The closure uses an `as` cast, which preserves address range + // and provenance. // - Aliasing is `Exclusive`, and so we are not required to promise // anything about the locations of `UnsafeCell`s. - // - `UnsafeCell` has the same bit validity as `T` [1], and so if - // `self` has a particular validity invariant, then the same holds - // of the returned `Ptr`. Technically the term "representation" - // doesn't guarantee this, but the subsequent sentence in the - // documentation makes it clear that this is the intention. + // - `UnsafeCell` has the same bit validity as `T` [1]. + // Technically the term "representation" doesn't guarantee this, + // but the subsequent sentence in the documentation makes it clear + // that this is the intention. + // + // By invariant on `Validity`, since `T` and `UnsafeCell` have + // the same bit validity, then the set of values which may appear + // in the referent of a `Ptr` is the same as the set + // which may appear in the referent of a `Ptr, (_, + // _, V)>`. Thus, neither `self` nor `ptr` may be used to write a + // value to the referent which would violate the other's validity + // invariant. // // [1] Per https://doc.rust-lang.org/1.81.0/core/cell/struct.UnsafeCell.html#memory-layout: // @@ -1190,9 +1264,8 @@ mod _project { // 1. `elem`, conditionally, conforms to the validity invariant of // `I::Alignment`. If `elem` is projected from data well-aligned // for `[T]`, `elem` will be valid for `T`. - // 2. `elem`, conditionally, conforms to the validity invariant of - // `I::Validity`. If `elem` is projected from data valid for - // `[T]`, `elem` will be valid for `T`. + // 2. TODO: Need to cite facts about `[T]`'s layout (same for the + // preceding points) self.as_inner().iter().map(|elem| unsafe { Ptr::from_inner(elem) }) } } diff --git a/src/util/macros.rs b/src/util/macros.rs index e5c6ce8246..c98711926e 100644 --- a/src/util/macros.rs +++ b/src/util/macros.rs @@ -52,10 +52,6 @@ macro_rules! safety_comment { /// referred to by `t`. /// - `r` refers to an object with `UnsafeCell`s at the same byte ranges as /// the object referred to by `t`. -/// - If the provided closure takes a `&$repr` argument, then given a `Ptr<'a, -/// $ty>` which satisfies the preconditions of -/// `TryFromBytes::<$ty>::is_bit_valid`, it must be guaranteed that the -/// memory referenced by that `Ptr` always contains a valid `$repr`. /// - The impl of `is_bit_valid` must only return `true` for its argument /// `Ptr<$repr>` if the original `Ptr<$ty>` refers to a valid `$ty`. macro_rules! unsafe_impl { @@ -180,9 +176,15 @@ macro_rules! unsafe_impl { #[allow(clippy::as_conversions)] let candidate = unsafe { candidate.cast_unsized_unchecked::<$repr, _>(|p| p as *mut _) }; - // SAFETY: The caller has promised that the referenced memory region - // will contain a valid `$repr`. - let $candidate = unsafe { candidate.assume_validity::() }; + // TODO(#1866): Currently, `bikeshed_recall_valid` has a known + // soundness hole. Eventually this will need to be fixed by + // requiring that `T: FromBytes + IntoBytes`. This bound ensures + // that this already holds of `$repr` so that we're guaranteed to be + // forwards-compatible with that change. + #[inline(always)] fn is_from_bytes_into_bytes() {} + is_from_bytes_into_bytes::<$repr>(); + + let $candidate = candidate.bikeshed_recall_valid(); $is_bit_valid } }; @@ -204,11 +206,6 @@ macro_rules! unsafe_impl { #[allow(clippy::as_conversions)] let $candidate = unsafe { candidate.cast_unsized_unchecked::<$repr, _>(|p| p as *mut _) }; - // Restore the invariant that the referent bytes are initialized. - // SAFETY: The above cast does not uninitialize any referent bytes; - // they remain initialized. - let $candidate = unsafe { $candidate.assume_validity::() }; - $is_bit_valid } };