summaryrefslogtreecommitdiff
path: root/rust/zerocopy/src/pointer/ptr.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rust/zerocopy/src/pointer/ptr.rs')
-rw-r--r--rust/zerocopy/src/pointer/ptr.rs177
1 files changed, 115 insertions, 62 deletions
diff --git a/rust/zerocopy/src/pointer/ptr.rs b/rust/zerocopy/src/pointer/ptr.rs
index 7213f6f4a04e..0fcd2ed11d37 100644
--- a/rust/zerocopy/src/pointer/ptr.rs
+++ b/rust/zerocopy/src/pointer/ptr.rs
@@ -169,7 +169,7 @@ mod _conversions {
use crate::pointer::cast::{CastExact, CastSized, IdCast};
/// `&'a T` → `Ptr<'a, T>`
- impl<'a, T> Ptr<'a, T, (Shared, Aligned, Valid)>
+ impl<'a, T> Ptr<'a, T, (Shared, Aligned, Safe)>
where
T: 'a + ?Sized,
{
@@ -183,20 +183,19 @@ mod _conversions {
// 1. `ptr`, by invariant on `&'a T`, conforms to the alignment
// invariant of `Aligned`.
// 2. `ptr`'s referent, by invariant on `&'a T`, is a bit-valid `T`.
- // This satisfies the requirement that a `Ptr<T, (_, _, Valid)>`
+ // This satisfies the requirement that a `Ptr<T, (_, _, Safe)>`
// 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.
+ // 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) }
}
}
/// `&'a mut T` → `Ptr<'a, T>`
- impl<'a, T> Ptr<'a, T, (Exclusive, Aligned, Valid)>
+ impl<'a, T> Ptr<'a, T, (Exclusive, Aligned, Safe)>
where
T: 'a + ?Sized,
{
@@ -211,7 +210,7 @@ mod _conversions {
// invariant of `Aligned`.
// 2. `ptr`'s referent, by invariant on `&'a mut T`, is a bit-valid
// `T`. This satisfies the requirement that a `Ptr<T, (_, _,
- // Valid)>` point to a bit-valid `T`. This invariant guarantees
+ // Safe)>` 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
@@ -224,7 +223,7 @@ mod _conversions {
impl<'a, T, I> Ptr<'a, T, I>
where
T: 'a + ?Sized,
- I: Invariants<Alignment = Aligned, Validity = Valid>,
+ I: Invariants<Alignment = Aligned, Validity = Safe>,
I::Aliasing: Reference,
{
/// Converts `self` to a shared reference.
@@ -239,7 +238,7 @@ mod _conversions {
let raw = self.as_inner().as_non_null();
// SAFETY: `self` satisfies the `Aligned` invariant, so we know that
// `raw` is validly-aligned for `T`.
- #[cfg(miri)]
+ #[cfg(all(miri, __ZEROCOPY_INTERNAL_USE_ONLY_DEV_MODE))]
unsafe {
crate::util::miri_promise_symbolic_alignment(
raw.as_ptr().cast(),
@@ -264,7 +263,7 @@ mod _conversions {
//
// 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`.
+ // `I::Validity` is `Safe`.
//
// 4. You must enforce Rust’s aliasing rules. This is ensured by
// contract on `Ptr`, because `I::Aliasing: Reference`. Either it
@@ -377,7 +376,7 @@ mod _conversions {
}
/// `Ptr<'a, T>` → `&'a mut T`
- impl<'a, T> Ptr<'a, T, (Exclusive, Aligned, Valid)>
+ impl<'a, T> Ptr<'a, T, (Exclusive, Aligned, Safe)>
where
T: 'a + ?Sized,
{
@@ -389,7 +388,7 @@ mod _conversions {
let mut raw = self.as_inner().as_non_null();
// SAFETY: `self` satisfies the `Aligned` invariant, so we know that
// `raw` is validly-aligned for `T`.
- #[cfg(miri)]
+ #[cfg(all(miri, __ZEROCOPY_INTERNAL_USE_ONLY_DEV_MODE))]
unsafe {
crate::util::miri_promise_symbolic_alignment(
raw.as_ptr().cast(),
@@ -413,8 +412,8 @@ mod _conversions {
// This is ensured by contract on all `PtrInner`s.
//
// 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`.
+ // `T`. This is ensured by-contract on `Ptr`, because the validity
+ // invariant is `Safe`.
//
// 4. You must enforce Rust’s aliasing rules. This is ensured by
// contract on `Ptr`, because the `ALIASING_INVARIANT` is
@@ -431,6 +430,10 @@ mod _conversions {
where
I: Invariants,
{
+ /// Reinterprets the same byte region as `U` with validity `V`.
+ ///
+ /// This preserves the aliasing invariant, uses [`SizeEq`] to select a
+ /// [`CastExact`] implementation, and conservatively forgets alignment.
#[must_use]
#[inline(always)]
pub fn transmute<U, V, R>(self) -> Ptr<'a, U, (I::Aliasing, Unaligned, V)>
@@ -443,6 +446,12 @@ mod _conversions {
self.transmute_with::<U, V, <U as SizeEq<T>>::CastFrom, R>()
}
+ /// Reinterprets the same byte region as `U` with validity `V` using
+ /// `C`.
+ ///
+ /// `C: CastExact` preserves the byte region. The aliasing invariant is
+ /// preserved, while alignment is conservatively forgotten because the
+ /// destination type may have a different alignment requirement.
#[inline]
#[must_use]
pub fn transmute_with<U, V, C, R>(self) -> Ptr<'a, U, (I::Aliasing, Unaligned, V)>
@@ -462,11 +471,15 @@ mod _conversions {
// at the same time, as neither can perform interior mutation
// - It is directly guaranteed that it is sound for shared code to
// operate on these references simultaneously
- // - By `U: TransmuteFromPtr<T, I::Aliasing, I::Validity, C, V>`, it
+ // - By `U: TransmuteFromPtr<T, I::Aliasing, I::Validity, V, C>`, it
// is sound to perform this transmute using `C`.
unsafe { self.project_transmute_unchecked::<_, _, C>() }
}
+ /// Changes only the validity invariant to `V`.
+ ///
+ /// The referent type and byte region are unchanged, so this preserves
+ /// the existing aliasing and alignment invariants.
#[inline]
#[must_use]
pub fn recall_validity<V, R>(self) -> Ptr<'a, T, (I::Aliasing, I::Alignment, V)>
@@ -562,7 +575,7 @@ mod _conversions {
// FIXME(#1359): This should be a `transmute_with` call.
// Unfortunately, to avoid blanket impl conflicts, we only implement
// `TransmuteFrom<T>` for `Unalign<T>` (and vice versa) specifically
- // for `Valid` validity, not for all validity types.
+ // for `Safe` validity, not for all validity types.
// SAFETY:
// - By `CastSized: Cast`, `CastSized` preserves referent address,
@@ -589,7 +602,7 @@ mod _conversions {
impl<'a, T, I> Ptr<'a, T, I>
where
T: ?Sized,
- I: Invariants<Validity = Valid>,
+ I: Invariants<Validity = Safe>,
I::Aliasing: Reference,
{
/// Reads the referent.
@@ -656,6 +669,22 @@ mod _transitions {
unsafe { self.assume_invariants::<H>() }
}
+ /// Downgrades `self` to shared aliasing.
+ #[inline]
+ #[must_use]
+ pub(super) fn into_shared(self) -> Ptr<'a, T, (Shared, I::Alignment, I::Validity)>
+ where
+ I::Aliasing: Reference,
+ {
+ // SAFETY: `I::Aliasing: Reference` guarantees that the source
+ // aliasing is either `Shared` or `Exclusive`. `Shared` requires no
+ // transition. If it is `Exclusive`, consuming `self` ends the only
+ // permitted access to the referent, so the returned pointer can
+ // soundly carry `Shared` aliasing. The referent type, alignment,
+ // and validity invariants are unchanged.
+ unsafe { self.assume_invariants() }
+ }
+
/// Assumes that `self`'s referent is validly-aligned for `T` if
/// required by `A`.
///
@@ -738,40 +767,40 @@ mod _transitions {
unsafe { self.assume_validity::<Initialized>() }
}
- /// A shorthand for `self.assume_validity<Valid>()`.
+ /// A shorthand for `self.assume_validity<Safe>()`.
///
/// # Safety
///
/// The caller promises to uphold the safety preconditions of
- /// `self.assume_validity<Valid>()`.
+ /// `self.assume_validity<Safe>()`.
#[must_use]
#[inline]
- pub unsafe fn assume_valid(self) -> Ptr<'a, T, (I::Aliasing, I::Alignment, Valid)> {
+ pub unsafe fn assume_safe(self) -> Ptr<'a, T, (I::Aliasing, I::Alignment, Safe)> {
// SAFETY: The caller has promised to uphold the safety
// preconditions.
- unsafe { self.assume_validity::<Valid>() }
+ unsafe { self.assume_validity::<Safe>() }
}
/// Checks that `self`'s referent is validly initialized for `T`,
- /// returning a `Ptr` with `Valid` on success.
+ /// returning a `Ptr` with `Safe` on success.
///
/// # Panics
///
/// This method will panic if
- /// [`T::is_bit_valid`][TryFromBytes::is_bit_valid] panics.
+ /// [`T::is_safe`][TryFromBytes::is_safe] panics.
///
/// # Safety
///
/// On error, unsafe code may rely on this method's returned
/// `ValidityError` containing `self`.
#[inline]
- pub fn try_into_valid<R, S>(
+ pub fn try_into_safe<R, S>(
mut self,
- ) -> Result<Ptr<'a, T, (I::Aliasing, I::Alignment, Valid)>, ValidityError<Self, T>>
+ ) -> Result<Ptr<'a, T, (I::Aliasing, I::Alignment, Safe)>, ValidityError<Self, T>>
where
T: TryFromBytes
+ Read<I::Aliasing, R>
- + TryTransmuteFromPtr<T, I::Aliasing, I::Validity, Valid, IdCast, S>,
+ + TryTransmuteFromPtr<T, I::Aliasing, I::Validity, Safe, IdCast, S>,
ReadOnly<T>: Read<I::Aliasing, R>,
I::Aliasing: Reference,
I: Invariants<Validity = Initialized>,
@@ -779,14 +808,13 @@ mod _transitions {
// 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 T::is_bit_valid(self.reborrow().transmute::<_, _, _>().reborrow_shared()) {
- // SAFETY: If `T::is_bit_valid`, code may assume that `self`
- // contains a bit-valid instance of `T`. By `T:
- // TryTransmuteFromPtr<T, I::Aliasing, I::Validity, Valid>`, so
- // long as `self`'s referent conforms to the `Valid` validity
- // for `T` (which we just confirmed), then this transmute is
- // sound.
- Ok(unsafe { self.assume_valid() })
+ if T::is_safe(self.reborrow().transmute::<_, _, _>().reborrow_shared()) {
+ // SAFETY: If `T::is_safe` returns true, code may assume that
+ // `self` contains a valid `T`, so its referent conforms to
+ // `Safe` for `T`. By `T: TryTransmuteFromPtr<T, I::Aliasing,
+ // I::Validity, Safe>`, given that condition, changing `self`'s
+ // validity to `Safe` is sound.
+ Ok(unsafe { self.assume_safe() })
} else {
Err(ValidityError::new(self))
}
@@ -803,7 +831,7 @@ mod _transitions {
}
/// Casts of the referent type.
-#[cfg_attr(not(zerocopy_unstable_ptr), allow(unreachable_pub))]
+#[allow(unreachable_pub)] // False positive on MSRV
pub use _casts::TryWithError;
mod _casts {
use core::cell::UnsafeCell;
@@ -875,19 +903,22 @@ mod _casts {
}
#[inline(always)]
- pub fn project<F, const VARIANT_ID: i128, const FIELD_ID: i128>(
+ pub fn project<Client, F, const VARIANT_ID: i128, const FIELD_ID: i128>(
mut self,
) -> Result<Ptr<'a, T::Type, T::Invariants>, T::Error>
where
- T: ProjectField<F, I, VARIANT_ID, FIELD_ID>,
+ T: ProjectField<Client, F, I, VARIANT_ID, FIELD_ID>,
I::Aliasing: Reference,
{
use crate::pointer::cast::Projection;
- match T::is_projectable(self.reborrow().project_tag()) {
+ match <T as ProjectField<Client, F, I, VARIANT_ID, FIELD_ID>>::is_projectable(
+ self.reborrow().project_tag::<Client>(),
+ ) {
Ok(()) => {
let inner = self.as_inner();
- let projected = inner.project::<_, Projection<F, VARIANT_ID, FIELD_ID>>();
- // SAFETY: By `T: ProjectField<F, I, VARIANT_ID, FIELD_ID>`,
+ let projected =
+ inner.project::<_, Projection<Client, F, VARIANT_ID, FIELD_ID>>();
+ // SAFETY: By `T: ProjectField<Client, F, I, VARIANT_ID, FIELD_ID>`,
// for `self: Ptr<'_, T, I>` such that `T::is_projectable`
// (which we've verified in this match arm),
// `T::project(self.as_inner())` conforms to
@@ -908,17 +939,22 @@ mod _casts {
#[must_use]
#[inline(always)]
- pub(crate) fn project_tag(self) -> Ptr<'a, T::Tag, I>
+ pub fn project_tag<Client>(
+ self,
+ ) -> Ptr<'a, <T as HasTag<Client>>::Tag, (Shared, I::Alignment, I::Validity)>
where
- T: HasTag,
+ T: HasTag<Client>,
+ I::Aliasing: Reference,
{
- // SAFETY: By invariant on `Self::ProjectToTag`, this is a sound
- // projection.
- let tag = unsafe { self.project_transmute_unchecked::<_, _, T::ProjectToTag>() };
- // SAFETY: By invariant on `Self::ProjectToTag`, the projected
- // pointer has the same alignment as `ptr`.
- let tag = unsafe { tag.assume_alignment() };
- tag.unify_invariants()
+ let ptr = self.into_shared();
+ // SAFETY: By invariant on `ProjectToTag`, it is sound to project
+ // `ptr` to a shared tag pointer with the same validity invariant.
+ let ptr = unsafe {
+ ptr.project_transmute_unchecked::<_, _, <T as HasTag<Client>>::ProjectToTag>()
+ };
+ // SAFETY: By invariant on `ProjectToTag`, the projected pointer has
+ // the same alignment as `self`.
+ unsafe { ptr.assume_alignment() }
}
/// Attempts to transform the pointer, restoring the original on
@@ -1015,11 +1051,11 @@ mod _casts {
#[allow(clippy::wrong_self_convention)]
#[must_use]
#[inline]
- pub fn as_bytes<R>(self) -> Ptr<'a, [u8], (I::Aliasing, Aligned, Valid)>
+ pub fn as_bytes<R>(self) -> Ptr<'a, [u8], (I::Aliasing, Aligned, Safe)>
where
- [u8]: TransmuteFromPtr<T, I::Aliasing, I::Validity, Valid, AsBytesCast, R>,
+ [u8]: TransmuteFromPtr<T, I::Aliasing, I::Validity, Safe, AsBytesCast, R>,
{
- self.transmute_with::<[u8], Valid, AsBytesCast, _>().bikeshed_recall_aligned()
+ self.transmute_with::<[u8], Safe, AsBytesCast, _>().bikeshed_recall_aligned()
}
}
@@ -1071,7 +1107,7 @@ mod _casts {
/// alignment of `[u8]` is 1.
impl<'a, I> Ptr<'a, [u8], I>
where
- I: Invariants<Validity = Valid>,
+ I: Invariants<Validity = Safe>,
{
/// Attempts to cast `self` to a `U` using the given cast type.
///
@@ -1135,7 +1171,7 @@ 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]`. `Ptr<[u8], (_, _, Valid)>` and `Ptr<_, (_, _,
+ // `[u8]`. `Ptr<[u8], (_, _, Safe)>` 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.
@@ -1143,14 +1179,14 @@ mod _casts {
// SAFETY:
// 0. `self` and `remainder` both have the type `[u8]`. Thus, they
- // have `UnsafeCell`s at the same locations. Type casting does
- // not affect aliasing.
+ // have `UnsafeCell`s at the same locations. Type casting does not
+ // affect aliasing.
// 1. `[u8]` has no alignment requirement.
- // 2. `self` has validity `Valid` and has type `[u8]`. Since
+ // 2. `self` has validity `Safe` and has type `[u8]`. Since
// `remainder` references a subset of `self`'s referent, it is
// 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.
+ // 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))
@@ -1333,6 +1369,23 @@ mod tests {
use crate::util::AsAddress;
use crate::{pointer::BecauseImmutable, util::testutil::AU64, FromBytes, Immutable};
+ #[test]
+ fn test_project_tag_downgrades_aliasing() {
+ #[allow(dead_code)]
+ #[derive(zerocopy_derive::Project)]
+ #[repr(u8)]
+ enum Enum {
+ Variant(u8),
+ }
+
+ let mut value = Enum::Variant(0);
+ let _: Ptr<
+ '_,
+ <Enum as crate::HasTag<crate::project_clients::ProjectDerive>>::Tag,
+ (Shared, Aligned, Safe),
+ > = Ptr::from_mut(&mut value).project_tag::<crate::project_clients::ProjectDerive>();
+ }
+
mod test_ptr_try_cast_into_soundness {
use super::*;