From 8343607a9c3ab1e2149051ec5f0be0d359de7618 Mon Sep 17 00:00:00 2001 From: Joshua Liebow-Feeser Date: Tue, 28 May 2024 18:54:08 -0700 Subject: [PATCH] [WIP] FromPtr/IntoPtr in internal scaffolding Experiment with #1183 --- src/lib.rs | 124 +++++++++++++++++++-------------------------- src/pointer/mod.rs | 91 +++++++++++++++++++++++++++++++++ src/pointer/ptr.rs | 12 ++--- 3 files changed, 149 insertions(+), 78 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index fbd838f125..e6e6a8f715 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -329,7 +329,10 @@ use core::{ }, }; -use crate::pointer::{invariant, BecauseExclusive, BecauseImmutable}; +use crate::pointer::{ + invariant, AliasingSafe, AliasingSafeReason, BecauseExclusive, BecauseImmutable, FromPtr, + IntoPtr, +}; #[cfg(any(feature = "alloc", test))] extern crate alloc; @@ -1420,7 +1423,7 @@ pub unsafe trait TryFromBytes { Self: KnownLayout + Immutable, { static_assert_dst_is_not_zst!(Self); - try_ref_from_prefix_suffix(candidate, CastType::Prefix, None) + try_from_prefix_suffix::<_, _, _, BecauseImmutable>(candidate, None, CastType::Prefix) } /// Attempts to interpret the suffix of the given `candidate` as a `&Self` @@ -1505,7 +1508,8 @@ pub unsafe trait TryFromBytes { Self: KnownLayout + Immutable, { static_assert_dst_is_not_zst!(Self); - try_ref_from_prefix_suffix(candidate, CastType::Suffix, None).map(swap) + try_from_prefix_suffix::<_, _, _, BecauseImmutable>(candidate, None, CastType::Suffix) + .map(swap) } /// Attempts to interpret the given `candidate` as a `&mut Self` without @@ -1697,7 +1701,7 @@ pub unsafe trait TryFromBytes { Self: KnownLayout, { static_assert_dst_is_not_zst!(Self); - try_mut_from_prefix_suffix(candidate, CastType::Prefix, None) + try_from_prefix_suffix::<_, _, _, BecauseExclusive>(candidate, None, CastType::Prefix) } /// Attempts to interpret the suffix of the given `candidate` as a `&mut @@ -1790,7 +1794,8 @@ pub unsafe trait TryFromBytes { Self: KnownLayout, { static_assert_dst_is_not_zst!(Self); - try_mut_from_prefix_suffix(candidate, CastType::Suffix, None).map(swap) + try_from_prefix_suffix::<_, _, _, BecauseExclusive>(candidate, None, CastType::Suffix) + .map(swap) } /// Attempts to read the given `candidate` as a `Self`. @@ -1874,37 +1879,21 @@ pub unsafe trait TryFromBytes { } #[inline(always)] -fn try_ref_from_prefix_suffix( - candidate: &[u8], - cast_type: CastType, +fn try_from_prefix_suffix<'a, T, S, D, R>( + candidate: S, meta: Option, -) -> Result<(&T, &[u8]), TryCastError<&[u8], T>> { - match Ptr::from_ref(candidate).try_cast_into::(cast_type, meta) { - Ok((candidate, prefix_suffix)) => { - // 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. - // - // Note that one panic or post-monomorphization error condition is - // calling `try_into_valid` (and thus `is_bit_valid`) with a shared - // pointer when `Self: !Immutable`. Since `Self: Immutable`, this panic - // condition will not happen. - match candidate.try_into_valid() { - Ok(valid) => Ok((valid.as_ref(), prefix_suffix.as_ref())), - Err(e) => Err(e.map_src(|src| src.as_bytes::().as_ref()).into()), - } - } - Err(e) => Err(e.map_src(Ptr::as_ref).into()), - } -} - -#[inline(always)] -fn try_mut_from_prefix_suffix( - candidate: &mut [u8], cast_type: CastType, - meta: Option, -) -> Result<(&mut T, &mut [u8]), TryCastError<&mut [u8], T>> { - match Ptr::from_mut(candidate).try_cast_into::(cast_type, meta) { +) -> Result<(D, S), TryCastError> +where + T: 'a + TryFromBytes + KnownLayout + ?Sized, + S: IntoPtr<'a, [u8], Alignment = invariant::Aligned, Validity = invariant::Valid>, + S: FromPtr<'a, [u8], (S::Aliasing, invariant::Aligned, invariant::Valid)>, + S::Aliasing: invariant::AtLeast, + D: FromPtr<'a, T, (S::Aliasing, invariant::Aligned, invariant::Valid)>, + R: AliasingSafeReason, + T: AliasingSafe<[u8], S::Aliasing, R>, +{ + match S::into_ptr(candidate).try_cast_into::(cast_type, meta) { Ok((candidate, prefix_suffix)) => { // 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 @@ -1915,11 +1904,11 @@ fn try_mut_from_prefix_suffix( // pointer when `Self: !Immutable`. Since `Self: Immutable`, this panic // condition will not happen. match candidate.try_into_valid() { - Ok(valid) => Ok((valid.as_mut(), prefix_suffix.as_mut())), - Err(e) => Err(e.map_src(|src| src.as_bytes::().as_mut()).into()), + Ok(valid) => Ok((D::from_ptr(valid), S::from_ptr(prefix_suffix))), + Err(e) => Err(e.map_src(|src| S::from_ptr(src.as_bytes::<(R,)>())).into()), } } - Err(e) => Err(e.map_src(Ptr::as_mut).into()), + Err(e) => Err(e.map_src(S::from_ptr).into()), } } @@ -2594,7 +2583,7 @@ pub unsafe trait FromBytes: FromZeros { Self: KnownLayout + Immutable, { static_assert_dst_is_not_zst!(Self); - ref_from_prefix_suffix(source, None, CastType::Prefix) + from_prefix_suffix::<_, _, _, BecauseImmutable>(source, None, CastType::Prefix) } /// Interprets the suffix of the given bytes as a `&Self` without copying. @@ -2660,7 +2649,7 @@ pub unsafe trait FromBytes: FromZeros { Self: Immutable + KnownLayout, { static_assert_dst_is_not_zst!(Self); - ref_from_prefix_suffix(source, None, CastType::Suffix).map(swap) + from_prefix_suffix::<_, _, _, BecauseImmutable>(source, None, CastType::Suffix).map(swap) } /// Interprets the given bytes as a `&mut Self` without copying. @@ -2819,7 +2808,7 @@ pub unsafe trait FromBytes: FromZeros { Self: IntoBytes + KnownLayout, { static_assert_dst_is_not_zst!(Self); - mut_from_prefix_suffix(source, None, CastType::Prefix) + from_prefix_suffix::<_, _, _, BecauseExclusive>(source, None, CastType::Prefix) } /// Interprets the suffix of the given bytes as a `&mut Self` without @@ -2891,7 +2880,7 @@ pub unsafe trait FromBytes: FromZeros { Self: IntoBytes + KnownLayout, { static_assert_dst_is_not_zst!(Self); - mut_from_prefix_suffix(source, None, CastType::Suffix).map(swap) + from_prefix_suffix::<_, _, _, BecauseExclusive>(source, None, CastType::Suffix).map(swap) } /// Interprets the given bytes as a `&Self` with a DST length equal to @@ -3034,7 +3023,7 @@ pub unsafe trait FromBytes: FromZeros { where Self: KnownLayout + Immutable, { - ref_from_prefix_suffix(source, Some(count), CastType::Prefix) + from_prefix_suffix::<_, _, _, BecauseImmutable>(source, Some(count), CastType::Prefix) } /// Interprets the suffix of the given bytes as a DST `&Self` with length @@ -3104,7 +3093,8 @@ pub unsafe trait FromBytes: FromZeros { where Self: KnownLayout + Immutable, { - ref_from_prefix_suffix(source, Some(count), CastType::Suffix).map(swap) + from_prefix_suffix::<_, _, _, BecauseImmutable>(source, Some(count), CastType::Suffix) + .map(swap) } /// Interprets the given bytes as a `&mut Self` with a DST length equal to @@ -3255,7 +3245,7 @@ pub unsafe trait FromBytes: FromZeros { where Self: IntoBytes + KnownLayout, { - mut_from_prefix_suffix(source, Some(count), CastType::Prefix) + from_prefix_suffix::<_, _, _, BecauseExclusive>(source, Some(count), CastType::Prefix) } /// Interprets the suffix of the given bytes as a `&mut [Self]` with length @@ -3330,7 +3320,8 @@ pub unsafe trait FromBytes: FromZeros { where Self: IntoBytes + KnownLayout, { - mut_from_prefix_suffix(source, Some(count), CastType::Suffix).map(swap) + from_prefix_suffix::<_, _, _, BecauseExclusive>(source, Some(count), CastType::Suffix) + .map(swap) } /// Reads a copy of `Self` from the given bytes. @@ -3562,26 +3553,7 @@ pub unsafe trait FromBytes: FromZeros { } } -/// Interprets the given affix of the given bytes as a `&Self` without copying. -/// -/// This method computes the largest possible size of `Self` that can fit in the -/// prefix or suffix bytes of `source`, then attempts to return both a reference -/// to those bytes interpreted as a `Self`, and a reference to the excess bytes. -/// If there are insufficient bytes, or if that affix of `source` is not -/// appropriately aligned, this returns `Err`. -#[inline(always)] -fn ref_from_prefix_suffix( - source: &[u8], - meta: Option, - cast_type: CastType, -) -> Result<(&T, &[u8]), CastError<&[u8], T>> { - let (slf, prefix_suffix) = Ptr::from_ref(source) - .try_cast_into::<_, BecauseImmutable>(cast_type, meta) - .map_err(|err| err.map_src(|s| s.as_ref()))?; - Ok((slf.bikeshed_recall_valid().as_ref(), prefix_suffix.as_ref())) -} - -/// Interprets the given affix of the given bytes as a `&mut Self` without +/// Interprets the given affix of the given bytes as a `Self` reference without /// copying. /// /// This method computes the largest possible size of `Self` that can fit in the @@ -3590,15 +3562,23 @@ fn ref_from_prefix_suffix( /// If there are insufficient bytes, or if that affix of `source` is not /// appropriately aligned, this returns `Err`. #[inline(always)] -fn mut_from_prefix_suffix( - source: &mut [u8], +fn from_prefix_suffix<'a, T, S, D, R>( + source: S, meta: Option, cast_type: CastType, -) -> Result<(&mut T, &mut [u8]), CastError<&mut [u8], T>> { - let (slf, prefix_suffix) = Ptr::from_mut(source) - .try_cast_into::<_, BecauseExclusive>(cast_type, meta) - .map_err(|err| err.map_src(|s| s.as_mut()))?; - Ok((slf.bikeshed_recall_valid().as_mut(), prefix_suffix.as_mut())) +) -> Result<(D, S), CastError> +where + T: 'a + FromBytes + KnownLayout + ?Sized, + S: IntoPtr<'a, [u8], Alignment = invariant::Aligned, Validity = invariant::Valid>, + S: FromPtr<'a, [u8], (S::Aliasing, invariant::Aligned, invariant::Valid)>, + D: FromPtr<'a, T, (S::Aliasing, invariant::Aligned, invariant::Valid)>, + R: AliasingSafeReason, + T: AliasingSafe<[u8], S::Aliasing, R>, +{ + let (slf, prefix_suffix) = IntoPtr::into_ptr(source) + .try_cast_into::<_, R>(cast_type, meta) + .map_err(|err| err.map_src(|s| S::from_ptr(s)))?; + Ok((D::from_ptr(slf.bikeshed_recall_valid()), S::from_ptr(prefix_suffix))) } /// Analyzes whether a type is [`IntoBytes`]. diff --git a/src/pointer/mod.rs b/src/pointer/mod.rs index 1533eb9efd..0a470be85a 100644 --- a/src/pointer/mod.rs +++ b/src/pointer/mod.rs @@ -14,6 +14,8 @@ mod ptr; pub use aliasing_safety::{AliasingSafe, AliasingSafeReason, BecauseExclusive, BecauseImmutable}; pub use ptr::{invariant, Ptr}; +use core::ptr::NonNull; + use crate::Unaligned; /// A shorthand for a maybe-valid, maybe-aligned reference. Used as the argument @@ -74,3 +76,92 @@ where { ptr.as_bytes::().as_ref().iter().all(|&byte| byte == 0) } + +pub(crate) unsafe trait IntoPtr<'a, T: ?Sized> { + type Aliasing: invariant::Aliasing; + type Alignment: invariant::Alignment; + type Validity: invariant::Validity; + + /// Converts `s` into a raw pointer to `s`'s referent. + /// + /// # Safety + /// + /// It is guaranteed to be sound to call `Ptr::new(IntoPtr::into_raw(...))`, + /// producing a `Ptr<'a, T, (Self::Aliasing, Self::Alignment, + /// Self::Validity)>`. In particular, all of the safety preconditions of + /// `Ptr::new` are satisfied. Given `ptr = IntoPtr::into_raw(...)`: + /// 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 `Self::Aliasing`. + /// 7. `ptr` conforms to the alignment invariant of `Self::Alignment`. + /// 8. `ptr` conforms to the validity invariant of `Self::Validity`. + /// 9. During the lifetime 'a, no code will load or store this memory region + /// treating `UnsafeCell`s as existing at different ranges than they + /// exist in `T`. + /// 10. During the lifetime 'a, no reference will exist to this memory which + /// treats `UnsafeCell`s as existing at different ranges than they exist + /// in `T`. + fn into_raw(s: Self) -> NonNull; + + fn into_ptr(ptr: Self) -> Ptr<'a, T, (Self::Aliasing, Self::Alignment, Self::Validity)> + where + Self: Sized, + { + let ptr = Self::into_raw(ptr); + // SAFETY: `Self::into_raw` promises to uphold the safety preconditions + // of `Ptr::new`. + unsafe { Ptr::new(ptr) } + } +} + +pub(crate) trait FromPtr<'a, T: ?Sized, I: invariant::Invariants> { + fn from_ptr(ptr: Ptr<'a, T, I>) -> Self; +} + +unsafe impl<'a, T: ?Sized> IntoPtr<'a, T> for &'a T { + type Aliasing = invariant::Shared; + type Alignment = invariant::Aligned; + type Validity = invariant::Valid; + + fn into_raw(ptr: Self) -> NonNull { + NonNull::from(ptr) + } +} + +unsafe impl<'a, T: ?Sized> IntoPtr<'a, T> for &'a mut T { + type Aliasing = invariant::Exclusive; + type Alignment = invariant::Aligned; + type Validity = invariant::Valid; + + fn into_raw(ptr: Self) -> NonNull { + NonNull::from(ptr) + } +} + +impl<'a, T: ?Sized, I> FromPtr<'a, T, I> for &'a T +where + I: invariant::Invariants, + I::Aliasing: invariant::AtLeast, +{ + fn from_ptr(ptr: Ptr<'a, T, I>) -> Self { + ptr.as_ref() + } +} + +impl<'a, T: ?Sized, I> FromPtr<'a, T, I> for &'a mut T +where + I: invariant::Invariants< + Aliasing = invariant::Exclusive, + Alignment = invariant::Aligned, + Validity = invariant::Valid, + >, +{ + fn from_ptr(ptr: Ptr<'a, T, I>) -> Self { + ptr.as_mut() + } +} diff --git a/src/pointer/ptr.rs b/src/pointer/ptr.rs index 7459b79371..50443a693e 100644 --- a/src/pointer/ptr.rs +++ b/src/pointer/ptr.rs @@ -94,7 +94,7 @@ mod def { /// [`I::Alignment`](invariant::Alignment). /// 8. `ptr` conforms to the validity invariant of /// [`I::Validity`](invariant::Validity). - pub(super) unsafe fn new(ptr: NonNull) -> Ptr<'a, T, I> { + pub(crate) 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 } @@ -512,9 +512,10 @@ mod _conversions { } /// `Ptr<'a, T>` → `&'a mut T` - impl<'a, T> Ptr<'a, T, (Exclusive, Aligned, Valid)> + impl<'a, T, I> Ptr<'a, T, I> where T: 'a + ?Sized, + I: Invariants, { /// Converts `self` to a mutable reference. #[allow(clippy::wrong_self_convention)] @@ -524,7 +525,7 @@ mod _conversions { // documented safety preconditions: // // 1. The pointer is properly aligned. This is ensured by-contract - // on `Ptr`, because the `ALIGNMENT_INVARIANT` is `Aligned`. + // on `Ptr`, because `Alignment = Aligned`. // // 2. It must be “dereferenceable” in the sense defined in the // module documentation; i.e.: @@ -537,11 +538,10 @@ mod _conversions { // // 3. The pointer must point to an initialized instance of `T`. This // is ensured by-contract on `Ptr`, because the - // `VALIDITY_INVARIANT` is `Valid`. + // `Validity = Valid`. // // 4. You must enforce Rust’s aliasing rules. This is ensured by - // contract on `Ptr`, because the `ALIASING_INVARIANT` is - // `Exclusive`. + // contract on `Ptr`, because the `Aliasing = Exclusive`. // // [1]: https://doc.rust-lang.org/std/ptr/struct.NonNull.html#method.as_mut // [2]: https://doc.rust-lang.org/std/ptr/index.html#safety