Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce invariant::Initialized and migrate Maybe to it #853

Merged
merged 1 commit into from
Feb 8, 2024
Merged
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
10 changes: 4 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1253,10 +1253,9 @@ pub unsafe trait TryFromBytes {
let candidate = Ptr::from_ref(bytes).try_cast_into_no_leftover::<Self>()?;

// SAFETY: `candidate` has no uninitialized sub-ranges because it
// derived from `bytes: &[u8]`, and is therefore at least as-initialized
// as `Self`.
// derived from `bytes: &[u8]`.
let candidate =
unsafe { candidate.assume_validity::<crate::pointer::invariant::AsInitialized>() };
unsafe { candidate.assume_validity::<crate::pointer::invariant::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
Expand Down Expand Up @@ -1284,9 +1283,8 @@ pub unsafe trait TryFromBytes {
let candidate = MaybeUninit::<Self>::read_from(bytes)?;
let c_ptr = Ptr::from_maybe_uninit_ref(&candidate);
// SAFETY: `c_ptr` has no uninitialized sub-ranges because it derived
// from `candidate`, which in turn derives from `bytes: &[u8]`, and is
// therefore at least as-initialized as `Self`.
let c_ptr = unsafe { c_ptr.assume_as_initialized() };
// from `candidate`, which in turn derives from `bytes: &[u8]`.
let c_ptr = unsafe { c_ptr.assume_validity::<crate::pointer::invariant::Initialized>() };

if !Self::is_bit_valid(c_ptr.forget_aligned()) {
return None;
Expand Down
7 changes: 4 additions & 3 deletions src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,10 @@ macro_rules! unsafe_impl {
#[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_validity::<crate::pointer::invariant::AsInitialized>() };
// 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::<crate::pointer::invariant::Initialized>() };

$is_bit_valid
}
Expand Down
2 changes: 1 addition & 1 deletion src/pointer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use crate::{TryFromBytes, Unaligned};
/// A shorthand for a maybe-valid, maybe-aligned reference. Used as the argument
/// to [`TryFromBytes::is_bit_valid`].
pub type Maybe<'a, T, Alignment = invariant::AnyAlignment> =
Ptr<'a, T, (invariant::Shared, Alignment, invariant::AsInitialized)>;
Ptr<'a, T, (invariant::Shared, Alignment, invariant::Initialized)>;

// These methods are defined on the type alias, `Maybe`, so as to bring them to
// the forefront of the rendered rustdoc.
Expand Down
20 changes: 11 additions & 9 deletions src/pointer/ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,9 @@ pub mod invariant {
/// discriminant, and so on recursively).
AsInitialized,

/// The byte ranges in the referent are fully initialized.
Initialized,

/// The referent is bit-valid for `T`.
Valid,
}
Expand Down Expand Up @@ -553,20 +556,20 @@ mod _transitions {
unsafe { Ptr::from_ptr(self) }
}

/// A shorthand for `self.assume_validity<invariant::AsInitialized>()`.
/// A shorthand for `self.assume_validity<invariant::Initialized>()`.
///
/// # Safety
///
/// The caller promises to uphold the safety preconditions of
/// `self.assume_validity<invariant::AsInitialized>()`.
/// `self.assume_validity<invariant::Initialized>()`.
#[doc(hidden)]
#[inline]
pub unsafe fn assume_as_initialized(
pub unsafe fn assume_initialized(
self,
) -> Ptr<'a, T, (I::Aliasing, I::Alignment, invariant::AsInitialized)> {
) -> Ptr<'a, T, (I::Aliasing, I::Alignment, invariant::Initialized)> {
// SAFETY: The caller has promised to uphold the safety
// preconditions.
unsafe { self.assume_validity::<invariant::AsInitialized>() }
unsafe { self.assume_validity::<invariant::Initialized>() }
}

/// A shorthand for `self.assume_validity<invariant::Valid>()`.
Expand Down Expand Up @@ -600,7 +603,7 @@ mod _transitions {
#[inline]
pub fn forget_valid(
self,
) -> Ptr<'a, T, (I::Aliasing, I::Alignment, invariant::AsInitialized)> {
) -> Ptr<'a, T, (I::Aliasing, I::Alignment, invariant::Initialized)> {
// SAFETY: `AnyValidity` is less restrictive than `Valid`.
unsafe { Ptr::from_ptr(self) }
}
Expand Down Expand Up @@ -864,8 +867,7 @@ mod _project {
impl<'a, T, I> Ptr<'a, T, I>
where
T: 'a + ?Sized,
I: Invariants,
I::Validity: invariant::at_least::AsInitialized,
I: Invariants<Validity = invariant::Initialized>,
{
/// Projects a field from `self`.
///
Expand All @@ -888,7 +890,7 @@ mod _project {
pub unsafe fn project<U: 'a + ?Sized>(
self,
projector: impl FnOnce(*mut T) -> *mut U,
) -> Ptr<'a, U, (I::Aliasing, invariant::AnyAlignment, invariant::AsInitialized)> {
) -> Ptr<'a, U, (I::Aliasing, invariant::AnyAlignment, invariant::Initialized)> {
// SAFETY: `projector` is provided with `self` casted to a raw
// pointer.
let field = projector(self.as_non_null().as_ptr());
Expand Down
16 changes: 8 additions & 8 deletions zerocopy-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ fn derive_try_from_bytes_enum(ast: &DeriveInput, enm: &DataEnum) -> proc_macro2:
(
::zerocopy::pointer::invariant::Shared,
::zerocopy::pointer::invariant::AnyAlignment,
::zerocopy::pointer::invariant::AsInitialized,
::zerocopy::pointer::invariant::Initialized,
),
>,
) -> ::zerocopy::macro_util::core_reexport::primitive::bool {
Expand All @@ -430,14 +430,14 @@ fn derive_try_from_bytes_enum(ast: &DeriveInput, enm: &DataEnum) -> proc_macro2:
// - By definition, `*mut Self` and `*mut [u8; size_of::<Self>()]`
// are types of the same size.
let discriminant = unsafe { candidate.cast_unsized(|p: *mut Self| p as *mut [core_reexport::primitive::u8; core_reexport::mem::size_of::<Self>()]) };
// SAFETY: Since `candidate` has the invariant `AsInitialized`, we
// SAFETY: Since `candidate` has the invariant `Initialized`, we
// know that `candidate`'s referent (and thus `discriminant`'s
// referent) is as-initialized as `Self`. Since all of the allowed
// `repr`s are types for which all bytes are always initialized, we
// know that `discriminant`'s referent has all of its bytes
// initialized. Since `[u8; N]`'s validity invariant is just that
// all of its bytes are initialized, we know that `discriminant`'s
// referent is bit-valid.
// referent) are fully initialized. Since all of the allowed `repr`s
// are types for which all bytes are always initialized, we know
// that `discriminant`'s referent has all of its bytes initialized.
// Since `[u8; N]`'s validity invariant is just that all of its
// bytes are initialized, we know that `discriminant`'s referent is
// bit-valid.
let discriminant = unsafe { discriminant.assume_valid() };
let discriminant = discriminant.read_unaligned();

Expand Down
4 changes: 2 additions & 2 deletions zerocopy-derive/tests/struct_try_from_bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ fn two_bad() {
let candidate = unsafe { candidate.cast_unsized(|p| p as *mut Two) };

// SAFETY: `candidate`'s referent is as-initialized as `Two`.
let candidate = unsafe { candidate.assume_as_initialized() };
let candidate = unsafe { candidate.assume_initialized() };

let is_bit_valid = Two::is_bit_valid(candidate);
assert!(!is_bit_valid);
Expand Down Expand Up @@ -109,7 +109,7 @@ fn un_sized() {
let candidate = unsafe { candidate.cast_unsized(|p| p as *mut Unsized) };

// SAFETY: `candidate`'s referent is as-initialized as `Two`.
let candidate = unsafe { candidate.assume_as_initialized() };
let candidate = unsafe { candidate.assume_initialized() };
let is_bit_valid = Unsized::is_bit_valid(candidate);
assert!(is_bit_valid);
}
Expand Down
6 changes: 3 additions & 3 deletions zerocopy-derive/tests/union_try_from_bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ fn two_bad() {
let candidate = unsafe { candidate.cast_unsized(|p| p as *mut Two) };

// SAFETY: `candidate`'s referent is as-initialized as `Two`.
let candidate = unsafe { candidate.assume_as_initialized() };
let candidate = unsafe { candidate.assume_initialized() };

let is_bit_valid = Two::is_bit_valid(candidate);
assert!(!is_bit_valid);
Expand All @@ -101,8 +101,8 @@ fn bool_and_zst() {
// the size of the object referenced by `self`.
let candidate = unsafe { candidate.cast_unsized(|p| p as *mut BoolAndZst) };

// SAFETY: `candidate`'s referent is as-initialized as `BoolAndZst`.
let candidate = unsafe { candidate.assume_as_initialized() };
// SAFETY: `candidate`'s referent is fully initialized.
let candidate = unsafe { candidate.assume_initialized() };

let is_bit_valid = BoolAndZst::is_bit_valid(candidate);
assert!(is_bit_valid);
Expand Down