Skip to content

Commit 824d7dc

Browse files
joshlfdorryspears
authored andcommitted
Add TryFromBytes::try_from_mut (google#892)
1 parent 8dbc9f8 commit 824d7dc

File tree

3 files changed

+217
-36
lines changed

3 files changed

+217
-36
lines changed

src/lib.rs

+41-7
Original file line numberDiff line numberDiff line change
@@ -1269,11 +1269,43 @@ pub unsafe trait TryFromBytes {
12691269
// This call may panic. If that happens, it doesn't cause any soundness
12701270
// issues, as we have not generated any invalid state which we need to
12711271
// fix before returning.
1272-
let candidate = candidate.check_valid();
1272+
let candidate = candidate.try_into_valid();
12731273

12741274
candidate.map(MaybeAligned::as_ref)
12751275
}
12761276

1277+
/// Attempts to interpret a mutable byte slice as a `Self`.
1278+
///
1279+
/// `try_from_mut` validates that `bytes` contains a valid `Self`, and that
1280+
/// it satisfies `Self`'s alignment requirement. If it does, then `bytes` is
1281+
/// reinterpreted as a `Self`.
1282+
///
1283+
/// Note that Rust's bit validity rules are still being decided. As such,
1284+
/// there exist types whose bit validity is ambiguous. See
1285+
/// [here][TryFromBytes#what-is-a-valid-instance] for a discussion of how
1286+
/// these cases are handled.
1287+
// TODO(#251): Require `Self: NoCell` and allow `TryFromBytes` types to
1288+
// contain `UnsafeCell`s.
1289+
#[inline]
1290+
#[doc(hidden)] // TODO(#5): Finalize name before remove this attribute.
1291+
fn try_from_mut(bytes: &mut [u8]) -> Option<&mut Self>
1292+
where
1293+
Self: KnownLayout + NoCell, // TODO(#251): Remove the `NoCell` bound.
1294+
{
1295+
let candidate = Ptr::from_mut(bytes).try_cast_into_no_leftover::<Self>()?;
1296+
1297+
// SAFETY: `candidate` has no uninitialized sub-ranges because it
1298+
// derived from `bytes: &mut [u8]`.
1299+
let candidate = unsafe { candidate.assume_validity::<invariant::Initialized>() };
1300+
1301+
// This call may panic. If that happens, it doesn't cause any soundness
1302+
// issues, as we have not generated any invalid state which we need to
1303+
// fix before returning.
1304+
let candidate = candidate.try_into_valid();
1305+
1306+
candidate.map(Ptr::as_mut)
1307+
}
1308+
12771309
/// Attempts to read a `Self` from a byte slice.
12781310
///
12791311
/// `try_read_from` validates that `bytes` contains a valid `Self`. If it
@@ -7985,7 +8017,7 @@ mod tests {
79858017
// `impl_try_from_bytes_testable!`).
79868018
trait TryFromBytesTestable {
79878019
fn with_passing_test_cases<F: Fn(&Self)>(f: F);
7988-
fn with_failing_test_cases<F: Fn(&[u8])>(f: F);
8020+
fn with_failing_test_cases<F: Fn(&mut [u8])>(f: F);
79898021
}
79908022

79918023
impl<T: FromBytes> TryFromBytesTestable for T {
@@ -8005,7 +8037,7 @@ mod tests {
80058037
f(&ffs);
80068038
}
80078039

8008-
fn with_failing_test_cases<F: Fn(&[u8])>(_f: F) {}
8040+
fn with_failing_test_cases<F: Fn(&mut [u8])>(_f: F) {}
80098041
}
80108042

80118043
// Implements `TryFromBytesTestable`.
@@ -8037,13 +8069,13 @@ mod tests {
80378069
)*
80388070
}
80398071

8040-
fn with_failing_test_cases<F: Fn(&[u8])>(_f: F) {
8072+
fn with_failing_test_cases<F: Fn(&mut [u8])>(_f: F) {
80418073
$($(
80428074
// `unused_qualifications` is spuriously triggered on
80438075
// `Option::<Self>::None`.
80448076
#[allow(unused_qualifications)]
8045-
let case = $failure_case.as_bytes();
8046-
_f(case.as_bytes());
8077+
let mut case = $failure_case;//.as_mut_bytes();
8078+
_f(case.as_mut_bytes());
80478079
)*)?
80488080
}
80498081
};
@@ -8135,7 +8167,9 @@ mod tests {
81358167
#[allow(clippy::as_conversions)]
81368168
<$ty as TryFromBytesTestable>::with_failing_test_cases(|c| {
81378169
let res = <$ty as TryFromBytes>::try_from_ref(c);
8138-
assert!(res.is_none(), "{}::is_bit_valid({:?}): got true, expected false", stringify!($ty), c);
8170+
assert!(res.is_none(), "{}::try_from_ref({:?}): got Some, expected None", stringify!($ty), c);
8171+
let res = <$ty as TryFromBytes>::try_from_mut(c);
8172+
assert!(res.is_none(), "{}::try_from_mut({:?}): got Some, expected None", stringify!($ty), c);
81398173
});
81408174

81418175
#[allow(dead_code)]

src/pointer/mod.rs

+5-29
Original file line numberDiff line numberDiff line change
@@ -12,43 +12,19 @@ mod ptr;
1212

1313
pub use ptr::{invariant, Ptr};
1414

15-
use crate::{TryFromBytes, Unaligned};
15+
use crate::Unaligned;
1616

1717
/// A shorthand for a maybe-valid, maybe-aligned reference. Used as the argument
1818
/// to [`TryFromBytes::is_bit_valid`].
19+
///
20+
/// [`TryFromBytes::is_bit_valid`]: crate::TryFromBytes::is_bit_valid
1921
pub type Maybe<'a, T, Alignment = invariant::AnyAlignment> =
2022
Ptr<'a, T, (invariant::Shared, Alignment, invariant::Initialized)>;
2123

22-
// These methods are defined on the type alias, `Maybe`, so as to bring them to
23-
// the forefront of the rendered rustdoc.
24-
impl<'a, T, Alignment> Maybe<'a, T, Alignment>
25-
where
26-
T: 'a + ?Sized + TryFromBytes,
27-
Alignment: invariant::Alignment,
28-
{
29-
/// Checks that `Ptr`'s referent is validly initialized for `T`.
30-
///
31-
/// # Panics
32-
///
33-
/// This method will panic if
34-
/// [`T::is_bit_valid`][TryFromBytes::is_bit_valid] panics.
35-
#[inline]
36-
pub(crate) fn check_valid(self) -> Option<MaybeAligned<'a, T, Alignment>> {
37-
// This call may panic. If that happens, it doesn't cause any soundness
38-
// issues, as we have not generated any invalid state which we need to
39-
// fix before returning.
40-
if T::is_bit_valid(self.forget_aligned()) {
41-
// SAFETY: If `T::is_bit_valid`, code may assume that `self`
42-
// contains a bit-valid instance of `Self`.
43-
Some(unsafe { self.assume_validity::<invariant::Valid>() })
44-
} else {
45-
None
46-
}
47-
}
48-
}
49-
5024
/// A semi-user-facing wrapper type representing a maybe-aligned reference, for
5125
/// use in [`TryFromBytes::is_bit_valid`].
26+
///
27+
/// [`TryFromBytes::is_bit_valid`]: crate::TryFromBytes::is_bit_valid
5228
pub type MaybeAligned<'a, T, Alignment = invariant::AnyAlignment> =
5329
Ptr<'a, T, (invariant::Shared, Alignment, invariant::Valid)>;
5430

src/pointer/ptr.rs

+171
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,38 @@ mod _conversions {
419419
}
420420
}
421421

422+
/// `&'a mut T` → `Ptr<'a, T>`
423+
impl<'a, T> Ptr<'a, T, (invariant::Exclusive, invariant::Aligned, invariant::Valid)>
424+
where
425+
T: 'a + ?Sized,
426+
{
427+
/// Constructs a `Ptr` from an exclusive reference.
428+
#[inline]
429+
pub(crate) fn from_mut(ptr: &'a mut T) -> Self {
430+
let ptr = core::ptr::NonNull::from(ptr);
431+
// SAFETY:
432+
// 0. `ptr`, by invariant on `&'a mut T`, is derived from some valid
433+
// Rust allocation, `A`.
434+
// 1. `ptr`, by invariant on `&'a mut T`, has valid provenance for
435+
// `A`.
436+
// 2. `ptr`, by invariant on `&'a mut T`, addresses a byte range
437+
// which is entirely contained in `A`.
438+
// 3. `ptr`, by invariant on `&'a mut T`, addresses a byte range
439+
// whose length fits in an `isize`.
440+
// 4. `ptr`, by invariant on `&'a mut T`, addresses a byte range
441+
// which does not wrap around the address space.
442+
// 5. `A`, by invariant on `&'a mut T`, is guaranteed to live for at
443+
// least `'a`.
444+
// 6. `ptr`, by invariant on `&'a mut T`, conforms to the aliasing
445+
// invariant of `invariant::Exclusive`.
446+
// 7. `ptr`, by invariant on `&'a mut T`, conforms to the alignment
447+
// invariant of `invariant::Aligned`.
448+
// 8. `ptr`, by invariant on `&'a mut T`, conforms to the validity
449+
// invariant of `invariant::Valid`.
450+
unsafe { Self::new(ptr) }
451+
}
452+
}
453+
422454
/// `Ptr<'a, T>` → `&'a T`
423455
impl<'a, T> Ptr<'a, T, (invariant::Shared, invariant::Aligned, invariant::Valid)>
424456
where
@@ -460,6 +492,104 @@ mod _conversions {
460492
}
461493
}
462494

495+
impl<'a, T, I> Ptr<'a, T, I>
496+
where
497+
T: 'a + ?Sized,
498+
I: Invariants,
499+
I::Aliasing: invariant::at_least::Shared,
500+
{
501+
/// Reborrows this `Ptr`, producing another `Ptr`.
502+
///
503+
/// Since `self` is borrowed immutably, this prevents any mutable
504+
/// methods from being called on `self` as long as the returned `Ptr`
505+
/// exists.
506+
#[allow(clippy::needless_lifetimes)] // Allows us to name the lifetime in the safety comment below.
507+
pub(crate) fn reborrow<'b>(&'b mut self) -> Ptr<'b, T, I>
508+
where
509+
'a: 'b,
510+
{
511+
// SAFETY: The following all hold by invariant on `self`, and thus
512+
// hold of `ptr = self.as_non_null()`:
513+
// 0. `ptr` is derived from some valid Rust allocation, `A`.
514+
// 1. `ptr` has valid provenance for `A`.
515+
// 2. `ptr` addresses a byte range which is entirely contained in
516+
// `A`.
517+
// 3. `ptr` addresses a byte range whose length fits in an `isize`.
518+
// 4. `ptr` addresses a byte range which does not wrap around the
519+
// address space.
520+
// 5. `A` is guaranteed to live for at least `'a`.
521+
// 6. SEE BELOW.
522+
// 7. `ptr` conforms to the alignment invariant of
523+
// [`I::Alignment`](invariant::Alignment).
524+
// 8. `ptr` conforms to the validity invariant of
525+
// [`I::Validity`](invariant::Validity).
526+
// 9. During the lifetime 'a, no code will load or store this memory
527+
// region treating `UnsafeCell`s as existing at different ranges
528+
// than they exist in `T`.
529+
// 10. During the lifetime 'a, no reference will exist to this
530+
// memory which treats `UnsafeCell`s as existing at different
531+
// ranges than they exist in `T`.
532+
//
533+
// For aliasing (6 above), since `I::Aliasing:
534+
// invariant::at_least::Shared`, there are two cases for
535+
// `I::Aliasing`:
536+
// - For `invariant::Shared`: `'a` outlives `'b`, and so the
537+
// returned `Ptr` does not permit accessing the referent any
538+
// longer than is possible via `self`. For shared aliasing, it is
539+
// sound for multiple `Ptr`s to exist simultaneously which
540+
// reference the same memory, so creating a new one is not
541+
// problematic.
542+
// - For `invariant::Exclusive`: Since `self` is `&'b mut` and we
543+
// return a `Ptr` with lifetime `'b`, `self` is inaccessible to
544+
// the caller for the lifetime `'b` - in other words, `self` is
545+
// inaccessible to the caller as long as the returned `Ptr`
546+
// exists. Since `self` is an exclusive `Ptr`, no other live
547+
// references or `Ptr`s may exist which refer to the same memory
548+
// while `self` is live. Thus, as long as the returned `Ptr`
549+
// exists, no other references or `Ptr`s which refer to the same
550+
// memory may be live.
551+
unsafe { Ptr::new(self.as_non_null()) }
552+
}
553+
}
554+
555+
/// `Ptr<'a, T>` → `&'a mut T`
556+
impl<'a, T> Ptr<'a, T, (invariant::Exclusive, invariant::Aligned, invariant::Valid)>
557+
where
558+
T: 'a + ?Sized,
559+
{
560+
/// Converts the `Ptr` to a mutable reference.
561+
#[allow(clippy::wrong_self_convention)]
562+
pub(crate) fn as_mut(self) -> &'a mut T {
563+
let mut raw = self.as_non_null();
564+
// SAFETY: This invocation of `NonNull::as_mut` satisfies its
565+
// documented safety preconditions:
566+
//
567+
// 1. The pointer is properly aligned. This is ensured by-contract
568+
// on `Ptr`, because the `ALIGNMENT_INVARIANT` is `Aligned`.
569+
//
570+
// 2. It must be “dereferenceable” in the sense defined in the
571+
// module documentation; i.e.:
572+
//
573+
// > The memory range of the given size starting at the pointer
574+
// > must all be within the bounds of a single allocated object.
575+
// > [2]
576+
//
577+
// This is ensured by contract on all `Ptr`s.
578+
//
579+
// 3. The pointer must point to an initialized instance of `T`. This
580+
// is ensured by-contract on `Ptr`, because the
581+
// `VALIDITY_INVARIANT` is `Valid`.
582+
//
583+
// 4. You must enforce Rust’s aliasing rules. This is ensured by
584+
// contract on `Ptr`, because the `ALIASING_INVARIANT` is
585+
// `Exclusive`.
586+
//
587+
// [1]: https://doc.rust-lang.org/std/ptr/struct.NonNull.html#method.as_mut
588+
// [2]: https://doc.rust-lang.org/std/ptr/index.html#safety
589+
unsafe { raw.as_mut() }
590+
}
591+
}
592+
463593
/// `&'a MaybeUninit<T>` → `Ptr<'a, T>`
464594
impl<'a, T> Ptr<'a, T, (invariant::Shared, invariant::Aligned, invariant::AnyValidity)>
465595
where
@@ -506,6 +636,7 @@ mod _conversions {
506636
/// State transitions between invariants.
507637
mod _transitions {
508638
use super::*;
639+
use crate::TryFromBytes;
509640

510641
impl<'a, T, I> Ptr<'a, T, I>
511642
where
@@ -592,6 +723,46 @@ mod _transitions {
592723
unsafe { self.assume_validity::<invariant::Valid>() }
593724
}
594725

726+
/// Checks that `Ptr`'s referent is validly initialized for `T`,
727+
/// returning a `Ptr` with `invariant::Valid` on success.
728+
///
729+
/// # Panics
730+
///
731+
/// This method will panic if
732+
/// [`T::is_bit_valid`][TryFromBytes::is_bit_valid] panics.
733+
#[inline]
734+
pub(crate) fn try_into_valid(
735+
mut self,
736+
) -> Option<Ptr<'a, T, (I::Aliasing, I::Alignment, invariant::Valid)>>
737+
where
738+
T: TryFromBytes,
739+
I::Aliasing: invariant::at_least::Shared,
740+
I: Invariants<Validity = invariant::Initialized>,
741+
{
742+
// This call may panic. If that happens, it doesn't cause any soundness
743+
// issues, as we have not generated any invalid state which we need to
744+
// fix before returning.
745+
if T::is_bit_valid(self.reborrow().forget_exclusive().forget_aligned()) {
746+
// SAFETY: If `T::is_bit_valid`, code may assume that `self`
747+
// contains a bit-valid instance of `Self`.
748+
Some(unsafe { self.assume_validity::<invariant::Valid>() })
749+
} else {
750+
None
751+
}
752+
}
753+
754+
/// Forgets that `Ptr`'s referent exclusively references `T`,
755+
/// downgrading to a shared reference.
756+
#[doc(hidden)]
757+
#[inline]
758+
pub fn forget_exclusive(self) -> Ptr<'a, T, (invariant::Shared, I::Alignment, I::Validity)>
759+
where
760+
I::Aliasing: invariant::at_least::Shared,
761+
{
762+
// SAFETY: `I::Aliasing` is at least as restrictive as `Shared`.
763+
unsafe { Ptr::from_ptr(self) }
764+
}
765+
595766
/// Forgets that `Ptr`'s referent is validly-aligned for `T`.
596767
#[doc(hidden)]
597768
#[inline]

0 commit comments

Comments
 (0)