Skip to content

Commit 225dbdd

Browse files
committed
[WIP] ReadOnly + is_bit_valid
gherrit-pr-id: I528d60de3bd7f8dcd69986f041f69aad1f890bd0
1 parent c1a3796 commit 225dbdd

File tree

13 files changed

+306
-163
lines changed

13 files changed

+306
-163
lines changed

src/impls.rs

Lines changed: 45 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ use core::{
1414
};
1515

1616
use super::*;
17+
use crate::pointer::{
18+
invariant::{Initialized, Shared},
19+
SizeEq,
20+
};
1721

1822
// SAFETY: Per the reference [1], "the unit tuple (`()`) ... is guaranteed as a
1923
// zero-sized type to have a size of 0 and an alignment of 1."
@@ -105,10 +109,11 @@ assert_unaligned!(bool);
105109
// pattern 0x01.
106110
const _: () = unsafe {
107111
unsafe_impl!(=> TryFromBytes for bool; |byte| {
108-
let byte = byte.transmute::<u8, invariant::Valid, _>();
112+
let byte = byte.transmute::<u8, invariant::Valid, BecauseImmutable>();
109113
*byte.unaligned_as_ref() < 2
110114
})
111115
};
116+
impl_size_eq!(ReadOnly<bool>, u8);
112117
impl_size_eq!(bool, u8);
113118

114119
// SAFETY:
@@ -134,7 +139,8 @@ const _: () = unsafe { unsafe_impl!(char: Immutable, FromZeros, IntoBytes) };
134139
// `char`.
135140
const _: () = unsafe {
136141
unsafe_impl!(=> TryFromBytes for char; |c| {
137-
let c = c.transmute::<Unalign<u32>, invariant::Valid, _>();
142+
impl_size_eq!(ReadOnly<char>, Unalign<u32>);
143+
let c = c.transmute::<Unalign<u32>, invariant::Valid, BecauseImmutable>();
138144
let c = c.read_unaligned().into_inner();
139145
char::from_u32(c).is_some()
140146
});
@@ -167,7 +173,8 @@ const _: () = unsafe { unsafe_impl!(str: Immutable, FromZeros, IntoBytes, Unalig
167173
// Returns `Err` if the slice is not UTF-8.
168174
const _: () = unsafe {
169175
unsafe_impl!(=> TryFromBytes for str; |c| {
170-
let c = c.transmute::<[u8], invariant::Valid, _>();
176+
impl_size_eq!(ReadOnly<str>, [u8]);
177+
let c = c.transmute::<[u8], invariant::Valid, BecauseImmutable>();
171178
let c = c.unaligned_as_ref();
172179
core::str::from_utf8(c).is_ok()
173180
})
@@ -179,9 +186,10 @@ macro_rules! unsafe_impl_try_from_bytes_for_nonzero {
179186
($($nonzero:ident[$prim:ty]),*) => {
180187
$(
181188
unsafe_impl!(=> TryFromBytes for $nonzero; |n| {
182-
impl_size_eq!($nonzero, Unalign<$prim>);
189+
// impl_size_eq!($nonzero, Unalign<$prim>);
190+
impl_size_eq!(ReadOnly<$nonzero>, Unalign<$prim>);
183191

184-
let n = n.transmute::<Unalign<$prim>, invariant::Valid, _>();
192+
let n = n.transmute::<Unalign<$prim>, invariant::Valid, BecauseImmutable>();
185193
$nonzero::new(n.read_unaligned().into_inner()).is_some()
186194
});
187195
)*
@@ -390,6 +398,9 @@ mod atomics {
390398
macro_rules! impl_traits_for_atomics {
391399
($($atomics:ident [$primitives:ident]),* $(,)?) => {
392400
$(
401+
// impl_transitive_transmute_from!(T => ReadOnly<T> => T => Wrapping<T> => ReadOnly<Wrapping<T>>);
402+
// impl_size_eq!($atomics, ReadOnly<$atomics>);
403+
impl_transitive_transmute_from!(=> ReadOnly<$atomics> => $atomics => $primitives => ReadOnly<$primitives>);
393404
impl_known_layout!($atomics);
394405
impl_for_transmute_from!(=> TryFromBytes for $atomics [UnsafeCell<$primitives>]);
395406
impl_for_transmute_from!(=> FromZeros for $atomics [UnsafeCell<$primitives>]);
@@ -490,6 +501,7 @@ mod atomics {
490501

491502
impl_known_layout!(AtomicBool);
492503

504+
impl_transitive_transmute_from!(=> ReadOnly<AtomicBool> => AtomicBool => bool => ReadOnly<bool>);
493505
impl_for_transmute_from!(=> TryFromBytes for AtomicBool [UnsafeCell<bool>]);
494506
impl_for_transmute_from!(=> FromZeros for AtomicBool [UnsafeCell<bool>]);
495507
impl_for_transmute_from!(=> IntoBytes for AtomicBool [UnsafeCell<bool>]);
@@ -635,6 +647,7 @@ mod atomics {
635647

636648
// FIXME(#170): Implement `FromBytes` and `IntoBytes` once we implement
637649
// those traits for `*mut T`.
650+
impl_transitive_transmute_from!(T => ReadOnly<AtomicPtr<T>> => AtomicPtr<T> => *mut T => ReadOnly<*mut T>);
638651
impl_for_transmute_from!(T => TryFromBytes for AtomicPtr<T> [UnsafeCell<*mut T>]);
639652
impl_for_transmute_from!(T => FromZeros for AtomicPtr<T> [UnsafeCell<*mut T>]);
640653

@@ -682,6 +695,7 @@ const _: () = unsafe {
682695
assert_unaligned!(PhantomData<()>, PhantomData<u8>, PhantomData<u64>);
683696
};
684697

698+
impl_transitive_transmute_from!(T => ReadOnly<T> => T => Wrapping<T> => ReadOnly<Wrapping<T>>);
685699
impl_for_transmute_from!(T: TryFromBytes => TryFromBytes for Wrapping<T>[<T>]);
686700
impl_for_transmute_from!(T: FromZeros => FromZeros for Wrapping<T>[<T>]);
687701
impl_for_transmute_from!(T: FromBytes => FromBytes for Wrapping<T>[<T>]);
@@ -755,6 +769,7 @@ assert_unaligned!(CoreMaybeUninit<()>, CoreMaybeUninit<u8>);
755769
// [2] https://doc.rust-lang.org/1.85.0/std/mem/struct.ManuallyDrop.html#impl-Deref-for-ManuallyDrop%3CT%3E
756770
const _: () = unsafe { unsafe_impl!(T: ?Sized + Immutable => Immutable for ManuallyDrop<T>) };
757771

772+
impl_transitive_transmute_from!(T: ?Sized => ReadOnly<T> => T => ManuallyDrop<T> => ReadOnly<ManuallyDrop<T>>);
758773
impl_for_transmute_from!(T: ?Sized + TryFromBytes => TryFromBytes for ManuallyDrop<T>[<T>]);
759774
impl_for_transmute_from!(T: ?Sized + FromZeros => FromZeros for ManuallyDrop<T>[<T>]);
760775
impl_for_transmute_from!(T: ?Sized + FromBytes => FromBytes for ManuallyDrop<T>[<T>]);
@@ -769,6 +784,7 @@ impl_for_transmute_from!(T: ?Sized + IntoBytes => IntoBytes for ManuallyDrop<T>[
769784
const _: () = unsafe { unsafe_impl!(T: ?Sized + Unaligned => Unaligned for ManuallyDrop<T>) };
770785
assert_unaligned!(ManuallyDrop<()>, ManuallyDrop<u8>);
771786

787+
impl_transitive_transmute_from!(T: ?Sized => ReadOnly<T> => T => Cell<T> => ReadOnly<Cell<T>>);
772788
impl_for_transmute_from!(T: ?Sized + TryFromBytes => TryFromBytes for Cell<T>[UnsafeCell<T>]);
773789
impl_for_transmute_from!(T: ?Sized + FromZeros => FromZeros for Cell<T>[UnsafeCell<T>]);
774790
impl_for_transmute_from!(T: ?Sized + FromBytes => FromBytes for Cell<T>[UnsafeCell<T>]);
@@ -804,29 +820,9 @@ unsafe impl<T: TryFromBytes + ?Sized> TryFromBytes for UnsafeCell<T> {
804820
}
805821

806822
#[inline]
807-
fn is_bit_valid<A: invariant::Reference>(candidate: Maybe<'_, Self, A>) -> bool {
808-
// The only way to implement this function is using an exclusive-aliased
809-
// pointer. `UnsafeCell`s cannot be read via shared-aliased pointers
810-
// (other than by using `unsafe` code, which we can't use since we can't
811-
// guarantee how our users are accessing or modifying the `UnsafeCell`).
812-
//
813-
// `is_bit_valid` is documented as panicking or failing to monomorphize
814-
// if called with a shared-aliased pointer on a type containing an
815-
// `UnsafeCell`. In practice, it will always be a monorphization error.
816-
// Since `is_bit_valid` is `#[doc(hidden)]` and only called directly
817-
// from this crate, we only need to worry about our own code incorrectly
818-
// calling `UnsafeCell::is_bit_valid`. The post-monomorphization error
819-
// makes it easier to test that this is truly the case, and also means
820-
// that if we make a mistake, it will cause downstream code to fail to
821-
// compile, which will immediately surface the mistake and give us a
822-
// chance to fix it quickly.
823-
let c = candidate.into_exclusive_or_pme();
824-
825-
// SAFETY: Since `UnsafeCell<T>` and `T` have the same layout and bit
826-
// validity, `UnsafeCell<T>` is bit-valid exactly when its wrapped `T`
827-
// is. Thus, this is a sound implementation of
828-
// `UnsafeCell::is_bit_valid`.
829-
T::is_bit_valid(c.get_mut())
823+
fn is_bit_valid(candidate: Maybe<'_, Self>) -> bool {
824+
impl_transitive_transmute_from!(T: ?Sized => ReadOnly<UnsafeCell<T>> => UnsafeCell<T> => T => ReadOnly<T>);
825+
T::is_bit_valid(candidate.transmute::<_, _, BecauseImmutable>())
830826
}
831827
}
832828

@@ -854,11 +850,14 @@ unsafe impl<T: TryFromBytes + ?Sized> TryFromBytes for UnsafeCell<T> {
854850
const _: () = unsafe {
855851
unsafe_impl!(const N: usize, T: Immutable => Immutable for [T; N]);
856852
unsafe_impl!(const N: usize, T: TryFromBytes => TryFromBytes for [T; N]; |c| {
853+
unsafe { impl_size_eq!(T => ReadOnly<[T]>, [ReadOnly<T>]) };
854+
unsafe { impl_size_eq!(const N: usize, T => ReadOnly<[T; N]>, [ReadOnly<T>; N]) };
857855
// Note that this call may panic, but it would still be sound even if it
858856
// did. `is_bit_valid` does not promise that it will not panic (in fact,
859857
// it explicitly warns that it's a possibility), and we have not
860858
// violated any safety invariants that we must fix before returning.
861-
<[T] as TryFromBytes>::is_bit_valid(c.as_slice())
859+
let c: Ptr<'_, [ReadOnly<T>; N], _> = c.transmute::<_, _, BecauseImmutable>();
860+
<[T] as TryFromBytes>::is_bit_valid(c.as_slice().transmute::<_, _, BecauseImmutable>())
862861
});
863862
unsafe_impl!(const N: usize, T: FromZeros => FromZeros for [T; N]);
864863
unsafe_impl!(const N: usize, T: FromBytes => FromBytes for [T; N]);
@@ -887,6 +886,7 @@ const _: () = unsafe {
887886
// not panic (in fact, it explicitly warns that it's a possibility), and
888887
// we have not violated any safety invariants that we must fix before
889888
// returning.
889+
let c: Ptr<'_, [ReadOnly<T>], _> = c.transmute::<_, _, BecauseImmutable>();
890890
c.iter().all(<T as TryFromBytes>::is_bit_valid)
891891
});
892892
unsafe_impl!(T: FromZeros => FromZeros for [T]);
@@ -1096,12 +1096,12 @@ mod tests {
10961096
// types must implement `TryFromBytesTestable` directly (ie using
10971097
// `impl_try_from_bytes_testable!`).
10981098
trait TryFromBytesTestable {
1099-
fn with_passing_test_cases<F: Fn(Box<Self>)>(f: F);
1099+
fn with_passing_test_cases<F: Fn(Box<ReadOnly<Self>>)>(f: F);
11001100
fn with_failing_test_cases<F: Fn(&mut [u8])>(f: F);
11011101
}
11021102

11031103
impl<T: FromBytes> TryFromBytesTestable for T {
1104-
fn with_passing_test_cases<F: Fn(Box<Self>)>(f: F) {
1104+
fn with_passing_test_cases<F: Fn(Box<ReadOnly<Self>>)>(f: F) {
11051105
// Test with a zeroed value.
11061106
f(Self::new_box_zeroed().unwrap());
11071107

@@ -1124,9 +1124,9 @@ mod tests {
11241124
($($tys:ty),*) => {
11251125
$(
11261126
impl TryFromBytesTestable for Option<$tys> {
1127-
fn with_passing_test_cases<F: Fn(Box<Self>)>(f: F) {
1127+
fn with_passing_test_cases<F: Fn(Box<ReadOnly<Self>>)>(f: F) {
11281128
// Test with a zeroed value.
1129-
f(Box::new(None));
1129+
f(Box::new(ReadOnly::new(None)));
11301130
}
11311131

11321132
fn with_failing_test_cases<F: Fn(&mut [u8])>(f: F) {
@@ -1164,7 +1164,7 @@ mod tests {
11641164
// Implements only the methods; caller must invoke this from inside
11651165
// an impl block.
11661166
(@methods @success $($success_case:expr),* $(, @failure $($failure_case:expr),*)?) => {
1167-
fn with_passing_test_cases<F: Fn(Box<Self>)>(_f: F) {
1167+
fn with_passing_test_cases<F: Fn(Box<ReadOnly<Self>>)>(_f: F) {
11681168
$(
11691169
_f(Box::<Self>::from($success_case));
11701170
)*
@@ -1266,17 +1266,15 @@ mod tests {
12661266

12671267
pub(super) trait TestIsBitValidShared<T: ?Sized> {
12681268
#[allow(clippy::needless_lifetimes)]
1269-
fn test_is_bit_valid_shared<'ptr, A: invariant::Reference>(
1270-
&self,
1271-
candidate: Maybe<'ptr, T, A>,
1272-
) -> Option<bool>;
1269+
fn test_is_bit_valid_shared<'ptr>(&self, candidate: Maybe<'ptr, T>)
1270+
-> Option<bool>;
12731271
}
12741272

12751273
impl<T: TryFromBytes + Immutable + ?Sized> TestIsBitValidShared<T> for AutorefWrapper<T> {
12761274
#[allow(clippy::needless_lifetimes)]
1277-
fn test_is_bit_valid_shared<'ptr, A: invariant::Reference>(
1275+
fn test_is_bit_valid_shared<'ptr>(
12781276
&self,
1279-
candidate: Maybe<'ptr, T, A>,
1277+
candidate: Maybe<'ptr, T>,
12801278
) -> Option<bool> {
12811279
Some(T::is_bit_valid(candidate))
12821280
}
@@ -1330,12 +1328,12 @@ mod tests {
13301328

13311329
pub(super) trait TestAsBytes<T: ?Sized> {
13321330
#[allow(clippy::needless_lifetimes)]
1333-
fn test_as_bytes<'slf, 't>(&'slf self, t: &'t T) -> Option<&'t [u8]>;
1331+
fn test_as_bytes<'slf, 't>(&'slf self, t: &'t ReadOnly<T>) -> Option<&'t [u8]>;
13341332
}
13351333

13361334
impl<T: IntoBytes + Immutable + ?Sized> TestAsBytes<T> for AutorefWrapper<T> {
13371335
#[allow(clippy::needless_lifetimes)]
1338-
fn test_as_bytes<'slf, 't>(&'slf self, t: &'t T) -> Option<&'t [u8]> {
1336+
fn test_as_bytes<'slf, 't>(&'slf self, t: &'t ReadOnly<T>) -> Option<&'t [u8]> {
13391337
Some(t.as_bytes())
13401338
}
13411339
}
@@ -1382,9 +1380,9 @@ mod tests {
13821380
#[allow(unused, non_local_definitions)]
13831381
impl AutorefWrapper<$ty> {
13841382
#[allow(clippy::needless_lifetimes)]
1385-
fn test_is_bit_valid_shared<'ptr, A: invariant::Reference>(
1383+
fn test_is_bit_valid_shared<'ptr>(
13861384
&mut self,
1387-
candidate: Maybe<'ptr, $ty, A>,
1385+
candidate: Maybe<'ptr, $ty>,
13881386
) -> Option<bool> {
13891387
assert_on_allowlist!(
13901388
test_is_bit_valid_shared($ty):
@@ -1442,7 +1440,7 @@ mod tests {
14421440
None
14431441
}
14441442

1445-
fn test_as_bytes(&mut self, _t: &$ty) -> Option<&[u8]> {
1443+
fn test_as_bytes(&mut self, _t: &ReadOnly<$ty>) -> Option<&[u8]> {
14461444
assert_on_allowlist!(
14471445
test_as_bytes($ty):
14481446
Option<&'static UnsafeCell<NotZerocopy>>,
@@ -1491,6 +1489,7 @@ mod tests {
14911489
// necessarily `IntoBytes`, but that's the corner we've
14921490
// backed ourselves into by using `Ptr::from_ref`.
14931491
let c = unsafe { c.assume_initialized() };
1492+
let c = unsafe { c.assume_aliasing::<Shared>() };
14941493
let res = w.test_is_bit_valid_shared(c);
14951494
if let Some(res) = res {
14961495
assert!(res, "{}::is_bit_valid({:?}) (shared `Ptr`): got false, expected true", stringify!($ty), val);
@@ -1502,6 +1501,7 @@ mod tests {
15021501
// necessarily `IntoBytes`, but that's the corner we've
15031502
// backed ourselves into by using `Ptr::from_ref`.
15041503
let c = unsafe { c.assume_initialized() };
1504+
let c = unsafe { c.assume_aliasing() };
15051505
let res = <$ty as TryFromBytes>::is_bit_valid(c);
15061506
assert!(res, "{}::is_bit_valid({:?}) (exclusive `Ptr`): got false, expected true", stringify!($ty), val);
15071507

src/lib.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ use core::{
386386
#[cfg(feature = "std")]
387387
use std::io;
388388

389-
use crate::pointer::invariant::{self, BecauseExclusive};
389+
use crate::pointer::invariant::{self, BecauseExclusive, Initialized};
390390
pub use crate::{
391391
byte_slice::*,
392392
byteorder::*,
@@ -1520,7 +1520,7 @@ pub unsafe trait TryFromBytes {
15201520
/// [`UnsafeCell`]: core::cell::UnsafeCell
15211521
/// [`Shared`]: invariant::Shared
15221522
#[doc(hidden)]
1523-
fn is_bit_valid<A: invariant::Reference>(candidate: Maybe<'_, Self, A>) -> bool;
1523+
fn is_bit_valid(candidate: Maybe<'_, Self>) -> bool;
15241524

15251525
/// Attempts to interpret the given `source` as a `&Self`.
15261526
///
@@ -2928,7 +2928,12 @@ unsafe fn try_read_from<S, T: TryFromBytes>(
29282928
// via `c_ptr` so long as it is live, so we don't need to worry about the
29292929
// fact that `c_ptr` may have more restricted validity than `candidate`.
29302930
let c_ptr = unsafe { c_ptr.assume_validity::<invariant::Initialized>() };
2931-
let c_ptr = c_ptr.transmute();
2931+
let c_ptr = c_ptr.transmute::<T, _, _>();
2932+
// unsafe { impl_size_eq!(T => T, Wrapping<T>) };
2933+
impl_transitive_transmute_from!(T => T => Wrapping<T> => ReadOnly<Wrapping<T>>);
2934+
let c_ptr: Ptr<'_, _, (invariant::Exclusive, invariant::Aligned, Initialized)> = todo!();
2935+
// let c_ptr = c_ptr.transmute::<ReadOnly<Wrapping<T>>, Initialized, BecauseExclusive>();
2936+
// let c_ptr = c_ptr.transmute();
29322937

29332938
// Since we don't have `T: KnownLayout`, we hack around that by using
29342939
// `Wrapping<T>`, which implements `KnownLayout` even if `T` doesn't.
@@ -2941,7 +2946,8 @@ unsafe fn try_read_from<S, T: TryFromBytes>(
29412946
// `try_into_valid` (and thus `is_bit_valid`) with a shared pointer when
29422947
// `Self: !Immutable`. Since `Self: Immutable`, this panic condition will
29432948
// not happen.
2944-
if !Wrapping::<T>::is_bit_valid(c_ptr.forget_aligned()) {
2949+
let shared = unsafe { c_ptr.assume_aliasing::<invariant::Shared>() };
2950+
if !Wrapping::<T>::is_bit_valid(shared.forget_aligned()) {
29452951
return Err(ValidityError::new(source).into());
29462952
}
29472953

src/macros.rs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -895,10 +895,7 @@ macro_rules! cryptocorrosion_derive_traits {
895895
$($field_ty: $crate::FromBytes,)*
896896
)?
897897
{
898-
fn is_bit_valid<A>(_c: $crate::Maybe<'_, Self, A>) -> bool
899-
where
900-
A: $crate::pointer::invariant::Reference
901-
{
898+
fn is_bit_valid(_c: $crate::Maybe<'_, Self>) -> bool {
902899
// SAFETY: This macro only accepts `#[repr(C)]` and
903900
// `#[repr(transparent)]` structs, and this `impl` block
904901
// requires all field types to be `FromBytes`. Thus, all
@@ -1038,10 +1035,7 @@ macro_rules! cryptocorrosion_derive_traits {
10381035
$field_ty: $crate::FromBytes,
10391036
)*
10401037
{
1041-
fn is_bit_valid<A>(_c: $crate::Maybe<'_, Self, A>) -> bool
1042-
where
1043-
A: $crate::pointer::invariant::Reference
1044-
{
1038+
fn is_bit_valid(_c: $crate::Maybe<'_, Self>) -> bool {
10451039
// SAFETY: This macro only accepts `#[repr(C)]` unions, and this
10461040
// `impl` block requires all field types to be `FromBytes`.
10471041
// Thus, all initialized byte sequences constitutes valid

src/pointer/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ pub use {
2727
///
2828
/// [`TryFromBytes::is_bit_valid`]: crate::TryFromBytes::is_bit_valid
2929
pub type Maybe<'a, T, Aliasing = invariant::Shared, Alignment = invariant::Unaligned> =
30-
Ptr<'a, T, (Aliasing, Alignment, invariant::Initialized)>;
30+
Ptr<'a, crate::wrappers::ReadOnly<T>, (Aliasing, Alignment, invariant::Initialized)>;
3131

3232
/// Checks if the referent is zeroed.
3333
pub(crate) fn is_zeroed<T, I>(ptr: Ptr<'_, T, I>) -> bool

src/pointer/ptr.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,7 @@ mod _conversions {
532532
/// State transitions between invariants.
533533
mod _transitions {
534534
use super::*;
535-
use crate::pointer::transmute::TryTransmuteFromPtr;
535+
use crate::{pointer::transmute::TryTransmuteFromPtr, ReadOnly};
536536

537537
impl<'a, T, I> Ptr<'a, T, I>
538538
where
@@ -807,10 +807,12 @@ mod _transitions {
807807
I::Aliasing: Reference,
808808
I: Invariants<Validity = Initialized>,
809809
{
810+
let ro: Ptr<'_, ReadOnly<T>, _> = self.reborrow().transmute();
811+
let shared = unsafe { ro.assume_aliasing::<Shared>() };
810812
// This call may panic. If that happens, it doesn't cause any soundness
811813
// issues, as we have not generated any invalid state which we need to
812814
// fix before returning.
813-
if T::is_bit_valid(self.reborrow().forget_aligned()) {
815+
if T::is_bit_valid(shared) {
814816
// SAFETY: If `T::is_bit_valid`, code may assume that `self`
815817
// contains a bit-valid instance of `T`. By `T:
816818
// TryTransmuteFromPtr<T, I::Aliasing, I::Validity, Valid>`, so

0 commit comments

Comments
 (0)