Skip to content

Commit 3115668

Browse files
committed
Add MaybeValid type
`MaybeValid<T>` is a `T` which might not be valid. It is similar to `MaybeUninit<T>`, but it is slightly more strict: any byte in `T` which is guaranteed to be initialized is also guaranteed to be initialized in `MaybeValid<T>` (see the doc comment for a more precise definition). `MaybeValid` is a building block of the `TryFromBytes` design outlined in #5. Makes progress on #5
1 parent fabeea2 commit 3115668

File tree

2 files changed

+284
-0
lines changed

2 files changed

+284
-0
lines changed

src/lib.rs

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3069,6 +3069,91 @@ mod tests {
30693069
assert_eq!(s, "\0\0\0\0\0");
30703070
}
30713071

3072+
#[test]
3073+
fn test_maybe_valid() {
3074+
let m = MaybeValid::<usize>::default();
3075+
// SAFETY: all bit patterns are valid `usize`s, and `m` is initialized.
3076+
let u = unsafe { m.assume_valid() };
3077+
// This ensures that Miri can see whether `u` (and thus `m`) has been
3078+
// properly initialized.
3079+
assert_eq!(u, u);
3080+
3081+
fn bytes_to_maybe_valid(bytes: &mut [u8]) -> &mut MaybeValid<[u8]> {
3082+
// SAFETY: `MaybeValid<[u8]>` has the same layout as `[u8]`, and
3083+
// `bytes` is initialized.
3084+
unsafe {
3085+
#[allow(clippy::as_conversions)]
3086+
let m = &mut *(bytes as *mut [u8] as *mut MaybeValid<[u8]>);
3087+
m
3088+
}
3089+
}
3090+
3091+
let mut bytes = [0u8, 1, 2];
3092+
let m = bytes_to_maybe_valid(&mut bytes[..]);
3093+
3094+
// SAFETY: `m` was created from a valid `[u8]`.
3095+
let r = unsafe { m.assume_valid_ref() };
3096+
assert_eq!(r.len(), 3);
3097+
assert_eq!(r, [0, 1, 2]);
3098+
3099+
// SAFETY: `m` was created from a valid `[u8]`.
3100+
let r = unsafe { m.assume_valid_mut() };
3101+
assert_eq!(r.len(), 3);
3102+
assert_eq!(r, [0, 1, 2]);
3103+
3104+
r[0] = 1;
3105+
assert_eq!(bytes, [1, 1, 2]);
3106+
3107+
let mut bytes = [0u8, 1, 2];
3108+
let m = bytes_to_maybe_valid(&mut bytes[..]);
3109+
let slc = m.as_slice_of_maybe_valids();
3110+
assert_eq!(slc.len(), 3);
3111+
for i in 0u8..3 {
3112+
// SAFETY: `m` was created from a valid `[u8]`.
3113+
let u = unsafe { slc[usize::from(i)].assume_valid_ref() };
3114+
assert_eq!(u, &i);
3115+
}
3116+
}
3117+
3118+
#[test]
3119+
fn test_maybe_valid_as_slice() {
3120+
let mut m = MaybeValid::<[u8; 3]>::default();
3121+
// SAFETY: all bit patterns are valid `[u8; 3]`s, and `m` is
3122+
// initialized.
3123+
unsafe { *m.assume_valid_mut() = [0, 1, 2] };
3124+
3125+
let slc = m.as_slice().as_slice_of_maybe_valids();
3126+
assert_eq!(slc.len(), 3);
3127+
3128+
for i in 0u8..3 {
3129+
// SAFETY: `m` was initialized as a valid `[u8; 3]`.
3130+
let u = unsafe { slc[usize::from(i)].assume_valid_ref() };
3131+
assert_eq!(u, &i);
3132+
}
3133+
}
3134+
3135+
#[test]
3136+
fn test_maybe_valid_from_ref() {
3137+
use core::cell::Cell;
3138+
3139+
let u = 1usize;
3140+
let m = MaybeValid::from_ref(&u);
3141+
// SAFETY: `m` was constructed from a valid `&usize`.
3142+
assert_eq!(unsafe { m.assume_valid_ref() }, &1usize);
3143+
3144+
// Test that interior mutability doesn't affect correctness or
3145+
// soundness.
3146+
3147+
let c = Cell::new(1usize);
3148+
let m = MaybeValid::from_ref(&c);
3149+
// SAFETY: `m` was constructed from a valid `&usize`.
3150+
assert_eq!(unsafe { m.assume_valid_ref() }, &Cell::new(1));
3151+
3152+
c.set(2);
3153+
// SAFETY: `m` was constructed from a valid `&usize`.
3154+
assert_eq!(unsafe { m.assume_valid_ref() }, &Cell::new(2));
3155+
}
3156+
30723157
#[test]
30733158
fn test_read_write() {
30743159
const VAL: u64 = 0x12345678;
@@ -3906,6 +3991,12 @@ mod tests {
39063991
assert_impls!(MaybeUninit<NotZerocopy>: !FromZeroes, !FromBytes, !AsBytes, !Unaligned);
39073992
assert_impls!(MaybeUninit<MaybeUninit<NotZerocopy>>: !FromZeroes, !FromBytes, !AsBytes, !Unaligned);
39083993

3994+
assert_impls!(MaybeValid<u8>: Unaligned, AsBytes, !FromZeroes, !FromBytes);
3995+
assert_impls!(MaybeValid<MaybeValid<u8>>: Unaligned, AsBytes, !FromZeroes, !FromBytes);
3996+
assert_impls!(MaybeValid<[u8]>: Unaligned, AsBytes, !FromZeroes, !FromBytes);
3997+
assert_impls!(MaybeValid<NotZerocopy>: !FromZeroes, !FromBytes, !AsBytes, !Unaligned);
3998+
assert_impls!(MaybeValid<MaybeValid<NotZerocopy>>: !FromZeroes, !FromBytes, !AsBytes, !Unaligned);
3999+
39094000
assert_impls!(Wrapping<u8>: FromZeroes, FromBytes, AsBytes, Unaligned);
39104001
assert_impls!(Wrapping<NotZerocopy>: !FromZeroes, !FromBytes, !AsBytes, !Unaligned);
39114002

src/wrappers.rs

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,199 @@ safety_comment! {
198198
assert_unaligned!(mem::MaybeUninit<()>, MaybeUninit<u8>);
199199
}
200200

201+
/// A value which might or might not constitute a valid instance of `T`.
202+
///
203+
/// `MaybeValid<T>` has the same layout (size and alignment) and field offsets
204+
/// as `T`. Unlike `T`, it may contain any bit pattern, except that
205+
/// uninitialized bytes may only appear in `MaybeValid<T>` at byte offsets where
206+
/// they may appear in `T`. This is a dynamic property: if, at a particular byte
207+
/// offset, a valid enum discriminant is set, the subsequent bytes may only have
208+
/// uninitialized bytes as specified by the corresponding enum variant.
209+
///
210+
/// Formally, given `m: MaybeValid<T>` and a byte offset, `b` in the range `[0,
211+
/// size_of_val(m))`:
212+
/// - If, in all valid instances `t: T`, the byte at offset `b` in `t` is
213+
/// initialized, then the byte at offset `b` within `m` is guaranteed to be
214+
/// initialized.
215+
/// - Let `c` be the contents of the byte range `[0, b)` in `m`. Let `TT` be the
216+
/// subset of valid instances of `T` which contain `c` in the offset range
217+
/// `[0, b)`. If, for all instances of `t: T` in `TT`, the byte at offset `b`
218+
/// in `t` is initialized, then the byte at offset `b` in `m` is guaranteed to
219+
/// be initialized.
220+
///
221+
/// Pragmatically, this means that if `m` is guaranteed to contain an enum
222+
/// type at a particular offset, and the enum discriminant stored in `m`
223+
/// corresponds to a valid variant of that enum type, then it is guaranteed
224+
/// that the appropriate bytes of `m` are initialized as defined by that
225+
/// variant's bit validity (although note that the variant may contain another
226+
/// enum type, in which case the same rules apply depending on the state of
227+
/// its discriminant, and so on recursively).
228+
///
229+
/// # Safety
230+
///
231+
/// Unsafe code may assume that an instance of `MaybeValid` satisfies the
232+
/// constraints described above. Unsafe code may produce a `MaybeValid` or
233+
/// modify the bytes of an existing `MaybeValid` so long as these constraints
234+
/// are upheld. It is unsound to produce a `MaybeValid` which fails to uphold
235+
/// these constraints.
236+
#[repr(transparent)]
237+
pub struct MaybeValid<T: ?Sized + KnownLayout> {
238+
inner: MaybeUninit<T>,
239+
}
240+
241+
safety_comment! {
242+
/// SAFETY:
243+
/// - `AsBytes`: `MaybeValid` requires that, if a byte in `T` is always
244+
/// initialized, the equivalent byte in `MaybeValid<T>` must be
245+
/// initialized. `T: AsBytes` implies that all bytes in `T` must always be
246+
/// initialized, and so all bytes in `MaybeValid<T>` must always be
247+
/// initialized, and so `MaybeValid<T>` satisfies `AsBytes`.
248+
/// - `Unaligned`: `MaybeValid<T>` has the same alignment as `T`.
249+
/// - `KnownLayout`: Since `MaybeUninit<T>` is a `repr(transparent)` wrapper
250+
/// around `T::MaybeUninit`:
251+
/// - They have the same prefix size, alignment, and trailing slice
252+
/// element size
253+
/// - It is valid to perform an `as` cast in either direction, and this
254+
/// operation preserves referent size
255+
///
256+
/// TODO(#5): Implement `FromZeroes` and `FromBytes` for `MaybeValid<T>` and
257+
/// `MaybeValid<[T]>`.
258+
unsafe_impl!(T: ?Sized + KnownLayout + AsBytes => AsBytes for MaybeValid<T>);
259+
unsafe_impl!(T: ?Sized + KnownLayout + Unaligned => Unaligned for MaybeValid<T>);
260+
unsafe_impl_known_layout!(T: ?Sized + KnownLayout => #[repr(MaybeUninit<T>)] MaybeValid<T>);
261+
}
262+
263+
impl<T: KnownLayout<MaybeUninit = mem::MaybeUninit<T>>> Default for MaybeValid<T> {
264+
fn default() -> MaybeValid<T> {
265+
// SAFETY: All of the bytes of `inner` are initialized to 0, and so the
266+
// safety invariant on `MaybeValid` is upheld.
267+
MaybeValid { inner: MaybeUninit::zeroed() }
268+
}
269+
}
270+
271+
impl<T: KnownLayout + ?Sized> MaybeValid<T> {
272+
/// Converts this `&MaybeValid<T>` to a `&T`.
273+
///
274+
/// # Safety
275+
///
276+
/// `self` must contain a valid `T`.
277+
pub unsafe fn assume_valid_ref(&self) -> &T {
278+
// SAFETY: The caller has promised that `self` contains a valid `T`.
279+
// Since `Self` is `repr(transparent)`, it has the same layout as
280+
// `MaybeUninit<T>`, which in turn is guaranteed to have the same layout
281+
// as `T`. Thus, it is sound to treat `self.inner` as containing a valid
282+
// `T`.
283+
unsafe { self.inner.assume_init_ref() }
284+
}
285+
286+
/// Converts this `&mut MaybeValid<T>` to a `&mut T`.
287+
///
288+
/// # Safety
289+
///
290+
/// `self` must contain a valid `T`.
291+
pub unsafe fn assume_valid_mut(&mut self) -> &mut T {
292+
// SAFETY: The caller has promised that `self` contains a valid `T`.
293+
// Since `Self` is `repr(transparent)`, it has the same layout as
294+
// `MaybeUninit<T>`, which in turn is guaranteed to have the same layout
295+
// as `T`. Thus, it is sound to treat `self.inner` as containing a valid
296+
// `T`.
297+
unsafe { self.inner.assume_init_mut() }
298+
}
299+
300+
/// Gets a view of this `&T` as a `&MaybeValid<T>`.
301+
///
302+
/// There is no mutable equivalent to this function, as producing a `&mut
303+
/// MaybeValid<T>` from a `&mut T` would allow safe code to write invalid
304+
/// values which would be accessible through `&mut T`.
305+
pub fn from_ref(r: &T) -> &MaybeValid<T> {
306+
let m: *const MaybeUninit<T> = MaybeUninit::from_ref(r);
307+
#[allow(clippy::as_conversions)]
308+
let ptr = m as *const MaybeValid<T>;
309+
// SAFETY: Since `Self` is `repr(transparent)`, it has the same layout
310+
// as `MaybeUninit<T>`, so the size and alignment here are valid.
311+
//
312+
// `MaybeValid<T>`'s bit validity constraints are weaker than those of
313+
// `T`, so this is guaranteed not to produce an invalid `MaybeValid<T>`.
314+
// If it were possible to write a different value for `MaybeValid<T>`
315+
// through the returned reference, it could result in an invalid value
316+
// being exposed via the `&T`. Luckily, the only way for mutation to
317+
// happen is if `T` contains an `UnsafeCell` and the caller uses it to
318+
// perform interior mutation. Importantly, `T` containing an
319+
// `UnsafeCell` does not permit interior mutation through
320+
// `MaybeValid<T>`, so it doesn't permit writing uninitialized or
321+
// otherwise invalid values which would be visible through the original
322+
// `&T`.
323+
unsafe { &*ptr }
324+
}
325+
}
326+
327+
impl<T: KnownLayout<MaybeUninit = mem::MaybeUninit<T>>> MaybeValid<T> {
328+
/// Converts this `MaybeValid<T>` to a `T`.
329+
///
330+
/// # Safety
331+
///
332+
/// `self` must contain a valid `T`.
333+
pub const unsafe fn assume_valid(self) -> T {
334+
// SAFETY: The caller has promised that `self` contains a valid `T`.
335+
// Since `Self` is `repr(transparent)`, it has the same layout as
336+
// `MaybeUninit<T>`, which in turn is guaranteed to have the same layout
337+
// as `T`. Thus, it is sound to treat `self.inner` as containing a valid
338+
// `T`.
339+
unsafe { self.inner.assume_init() }
340+
}
341+
}
342+
343+
impl<T: KnownLayout<MaybeUninit = mem::MaybeUninit<T>>> MaybeValid<[T]> {
344+
/// Converts a `MaybeValid<[T]>` to a `[MaybeValid<T>]`.
345+
///
346+
/// `MaybeValid<T>` has the same layout as `T`, so these layouts are
347+
/// equivalent.
348+
pub const fn as_slice_of_maybe_valids(&self) -> &[MaybeValid<T>] {
349+
let inner: &[<T as KnownLayout>::MaybeUninit] = &self.inner.inner;
350+
let inner_ptr: *const [<T as KnownLayout>::MaybeUninit] = inner;
351+
// Note: this Clippy warning is only emitted on our MSRV (1.61), but not
352+
// on later versions of Clippy. Thus, we consider it spurious.
353+
#[allow(clippy::as_conversions)]
354+
let ret_ptr = inner_ptr as *const [MaybeValid<T>];
355+
// SAFETY: Since `inner` is a `&[MaybeUninit<T>]`, and `MaybeValid<T>`
356+
// is a `repr(transparent)` struct around `MaybeUninit<T>`, `inner` has
357+
// the same layout as `&[MaybeValid<T>]`.
358+
unsafe { &*ret_ptr }
359+
}
360+
}
361+
362+
impl<const N: usize, T: KnownLayout> MaybeValid<[T; N]> {
363+
/// Converts a `MaybeValid<[T; N]>` to a `MaybeValid<[T]>`.
364+
// TODO(#64): Make this `const` once our MSRV is >= 1.64.0 (when
365+
// `slice_from_raw_parts` was stabilized as `const`).
366+
pub fn as_slice(&self) -> &MaybeValid<[T]> {
367+
let base: *const MaybeValid<[T; N]> = self;
368+
let slice_of_t: *const [T] = ptr::slice_from_raw_parts(base.cast::<T>(), N);
369+
// Note: this Clippy warning is only emitted on our MSRV (1.61), but not
370+
// on later versions of Clippy. Thus, we consider it spurious.
371+
#[allow(clippy::as_conversions)]
372+
let mv_of_slice = slice_of_t as *const MaybeValid<[T]>;
373+
// SAFETY: `MaybeValid<T>` is a `repr(transparent)` wrapper around
374+
// `MaybeUninit<T>`, which in turn has the same layout as `T`. Thus, the
375+
// trailing slices of `[T]` and of `MaybeValid<[T]>` both have element
376+
// type `T`. Since the number of elements is preserved during an `as`
377+
// cast of slice/DST pointers, the resulting `*const MaybeValid<[T]>`
378+
// has the same number of elements - and thus the same length - as the
379+
// original `*const [T]`.
380+
//
381+
// Thanks to their layouts, `MaybeValid<[T; N]>` and `MaybeValid<[T]>`
382+
// have the same alignment, so `mv_of_slice` is guaranteed to be
383+
// aligned.
384+
unsafe { &*mv_of_slice }
385+
}
386+
}
387+
388+
impl<T: ?Sized + KnownLayout> Debug for MaybeValid<T> {
389+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
390+
f.pad(core::any::type_name::<Self>())
391+
}
392+
}
393+
201394
/// A type with no alignment requirement.
202395
///
203396
/// An `Unalign` wraps a `T`, removing any alignment requirement. `Unalign<T>`

0 commit comments

Comments
 (0)