Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 15 additions & 7 deletions src/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1080,16 +1080,24 @@ mod tuples {
// The types and indices at and after the current index.
[$CurrT:ident $CurrV:ident $CurrI:tt $($AfterT:ident $AfterV:ident $AfterI:tt)*]
) => {
impl<Replacement, $($AllT),+> crate::invariant::Map<Replacement, crate::Idx<{ $CurrI }>> for ($($AllT,)+)
where
Replacement: crate::invariant::Validity,
$($AllT: crate::invariant::Validity,)*
{
type Result = ($($BeforeT,)* Replacement, $($AfterT,)*);
}

// SAFETY:
// - `Self` is a struct (albeit anonymous), so `VARIANT_ID` is
// `STRUCT_VARIANT_ID`.
// - `$CurrI` is the field at index `$CurrI`, so `FIELD_ID` is
// `zerocopy::ident_id!($CurrI)`
// - `()` has the same visibility as the `.$CurrI` field (ie, `.0`,
// `.1`, etc)
// - `crate::Idx` has the same visibility as the `.$CurrI` field
// (ie, `.0`, `.1`, etc)
// - `Type` has the same type as `$CurrI`; i.e., `$CurrT`.
unsafe impl<$($AllT),+> crate::HasField<
(),
crate::Idx<{ $CurrI }>,
{ crate::STRUCT_VARIANT_ID },
{ crate::ident_id!($CurrI)}
> for ($($AllT,)+) {
Expand All @@ -1115,7 +1123,7 @@ mod tuples {

// SAFETY: See comments on items.
unsafe impl<Aliasing, Alignment, $($AllT),+> crate::ProjectField<
(),
crate::Idx<{ $CurrI }>,
(Aliasing, Alignment, crate::invariant::Uninit),
{ crate::STRUCT_VARIANT_ID },
{ crate::ident_id!($CurrI)}
Expand All @@ -1141,7 +1149,7 @@ mod tuples {

// SAFETY: See comments on items.
unsafe impl<Aliasing, Alignment, $($AllT),+> crate::ProjectField<
(),
crate::Idx<{ $CurrI }>,
(Aliasing, Alignment, crate::invariant::Initialized),
{ crate::STRUCT_VARIANT_ID },
{ crate::ident_id!($CurrI)}
Expand All @@ -1167,7 +1175,7 @@ mod tuples {

// SAFETY: See comments on items.
unsafe impl<Aliasing, Alignment, $($AllT),+> crate::ProjectField<
(),
crate::Idx<{ $CurrI }>,
(Aliasing, Alignment, crate::invariant::Valid),
{ crate::STRUCT_VARIANT_ID },
{ crate::ident_id!($CurrI)}
Expand All @@ -1193,7 +1201,7 @@ mod tuples {

// SAFETY: See comments on items.
unsafe impl<Aliasing, Alignment, $($AllT,)+ $($AllV),+> crate::ProjectField<
(),
crate::Idx<{ $CurrI }>,
(Aliasing, Alignment, ($($AllV,)+)),
{ crate::STRUCT_VARIANT_ID },
{ crate::ident_id!($CurrI)}
Expand Down
5 changes: 5 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1123,6 +1123,11 @@ pub const UNION_VARIANT_ID: i128 = -2;
#[doc(hidden)]
pub const REPR_C_UNION_VARIANT_ID: i128 = -3;

/// A field visibility token for public tuple fields.
#[allow(missing_copy_implementations, missing_debug_implementations)]
#[doc(hidden)]
pub struct Idx<const INDEX: u16>;

/// # Safety
///
/// `Self::ProjectToTag` must satisfy its safety invariant.
Expand Down
10 changes: 10 additions & 0 deletions src/pointer/invariant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,16 @@ pub enum BecauseExclusive {}
#[doc(hidden)]
pub enum BecauseImmutable {}

/// Replaces a validity component.
///
/// The [`Self::Result`] is a validity identical `Self` except that the
/// component corresponding to `Field` component has been replaced with
/// `Replacement`.
#[doc(hidden)]
pub trait Map<Replacement: Validity, Field> {
type Result: Validity;
}

use sealed::Sealed;
pub(crate) mod sealed {
use super::*;
Expand Down
32 changes: 32 additions & 0 deletions src/pointer/ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -914,6 +914,38 @@ mod _casts {
tag.unify_invariants()
}

#[inline(always)]
pub fn project_map<F, V, const VARIANT_ID: i128, const FIELD_ID: i128>(
mut self,
map: impl for<'b> FnOnce(
Ptr<'b, T::Type, T::Invariants>,
) -> Ptr<
'b,
T::Type,
(
<T::Invariants as Invariants>::Aliasing,
<T::Invariants as Invariants>::Alignment,
V,
),
>,
) -> Result<
Ptr<'a, T, (I::Aliasing, I::Alignment, <I::Validity as Map<V, F>>::Result)>,
T::Error,
>
where
T: ProjectField<F, I, VARIANT_ID, FIELD_ID>,
I::Aliasing: Reference,
I::Validity: Map<V, F>,
V: Validity,
{
let _mapped = match self.reborrow().project() {
Ok(field) => map(field),
Err(err) => return Err(err),
};
// SAFETY: TODO
Ok(unsafe { self.assume_validity() })
}
Comment on lines +941 to +947
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

This implementation has a critical soundness issue.

The map closure is called on a reborrow of self, and its resulting Ptr is immediately discarded (assigned to _mapped). This means any type-level proof of a validity change is lost.

Subsequently, unsafe { self.assume_validity() } is called without justification. Since map operated on a temporary borrow, it cannot logically change the validity of the original self. This unsafe block is unsound as written and, as the // SAFETY: TODO comment indicates, lacks the required safety proof.

This could lead to Ptrs with incorrect validity invariants, causing undefined behavior. The map closure's returned Ptr must be used to prove that the operation is sound before assume_validity can be justified.


/// Attempts to transform the pointer, restoring the original on
/// failure.
///
Expand Down
Loading