Skip to content

Commit bdf6203

Browse files
authored
Add IntoByteSlice trait, use as bound for into_ref (#966)
Add `IntoByteSlice` and `IntoByteSliceMut` traits, which extend `ByteSlice` and `ByteSliceMut`, adding `Into<&[u8]>` and `Into<&mut [u8]>` bounds respectively. Use these new traits as the bounds in the `Ref` methods `into_ref`, `into_mut`, `into_slice`, and `into_mut_slice`. This allows us to remove the post-monomorphization error which was originally added to patch the soundness hole in #716 in a backwards-compatible way. Closes #758
1 parent f857fbc commit bdf6203

File tree

2 files changed

+137
-100
lines changed

2 files changed

+137
-100
lines changed

src/lib.rs

Lines changed: 77 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -955,7 +955,7 @@ pub unsafe trait KnownLayout {
955955
}
956956

957957
// SAFETY: Delegates safety to `DstLayout::for_slice`.
958-
unsafe impl<T: KnownLayout> KnownLayout for [T] {
958+
unsafe impl<T> KnownLayout for [T] {
959959
#[allow(clippy::missing_inline_in_public_items)]
960960
fn only_derive_is_allowed_to_implement_this_trait()
961961
where
@@ -4989,84 +4989,94 @@ where
49894989

49904990
impl<'a, B, T> Ref<B, T>
49914991
where
4992-
B: 'a + ByteSlice,
4992+
B: 'a + IntoByteSlice<'a>,
49934993
T: FromBytes + NoCell,
49944994
{
49954995
/// Converts this `Ref` into a reference.
49964996
///
49974997
/// `into_ref` consumes the `Ref`, and returns a reference to `T`.
49984998
#[inline(always)]
49994999
pub fn into_ref(self) -> &'a T {
5000-
assert!(B::INTO_REF_INTO_MUT_ARE_SOUND);
5001-
5002-
// SAFETY: According to the safety preconditions on
5003-
// `ByteSlice::INTO_REF_INTO_MUT_ARE_SOUND`, the preceding assert
5004-
// ensures that, given `B: 'a`, it is sound to drop `self` and still
5005-
// access the underlying memory using reads for `'a`.
5006-
unsafe { self.deref_helper() }
5000+
let ptr = Ptr::from_ref(self.0.into());
5001+
// SAFETY: By invariant on `Ref`, `self.0`'s size is equal to
5002+
// `size_of::<T>()`.
5003+
let ptr = unsafe { ptr.cast::<T>() };
5004+
// SAFETY: By invariant on `Ref`, `self.0` is aligned to `T`'s
5005+
// alignment.
5006+
let ptr = unsafe { ptr.assume_aligned() };
5007+
// SAFETY: `ptr` is derived from `self.0.into()`, which is of type
5008+
// `[u8]`, whose bit validity requires that all of its bytes are
5009+
// initialized.
5010+
let ptr = unsafe { ptr.assume_initialized() };
5011+
let ptr = ptr.bikeshed_recall_valid();
5012+
ptr.as_ref()
50075013
}
50085014
}
50095015

50105016
impl<'a, B, T> Ref<B, T>
50115017
where
5012-
B: 'a + ByteSliceMut,
5018+
B: 'a + IntoByteSliceMut<'a>,
50135019
T: FromBytes + IntoBytes + NoCell,
50145020
{
50155021
/// Converts this `Ref` into a mutable reference.
50165022
///
50175023
/// `into_mut` consumes the `Ref`, and returns a mutable reference to `T`.
50185024
#[inline(always)]
5019-
pub fn into_mut(mut self) -> &'a mut T {
5020-
assert!(B::INTO_REF_INTO_MUT_ARE_SOUND);
5021-
5022-
// SAFETY: According to the safety preconditions on
5023-
// `ByteSlice::INTO_REF_INTO_MUT_ARE_SOUND`, the preceding assert
5024-
// ensures that, given `B: 'a + ByteSliceMut`, it is sound to drop
5025-
// `self` and still access the underlying memory using both reads and
5026-
// writes for `'a`.
5027-
unsafe { self.deref_mut_helper() }
5025+
pub fn into_mut(self) -> &'a mut T {
5026+
let ptr = Ptr::from_mut(self.0.into());
5027+
// SAFETY: By invariant on `Ref`, `self.0`'s size is equal to
5028+
// `size_of::<T>()`.
5029+
let ptr = unsafe { ptr.cast::<T>() };
5030+
// SAFETY: By invariant on `Ref`, `self.0` is aligned to `T`'s
5031+
// alignment.
5032+
let ptr = unsafe { ptr.assume_aligned() };
5033+
// SAFETY: `ptr` is derived from `self.0.into()`, which is of type
5034+
// `[u8]`, whose bit validity requires that all of its bytes are
5035+
// initialized.
5036+
let ptr = unsafe { ptr.assume_initialized() };
5037+
let ptr = ptr.bikeshed_recall_valid();
5038+
ptr.as_mut()
50285039
}
50295040
}
50305041

50315042
impl<'a, B, T> Ref<B, [T]>
50325043
where
5033-
B: 'a + ByteSlice,
5044+
B: 'a + IntoByteSlice<'a>,
50345045
T: FromBytes + NoCell,
50355046
{
50365047
/// Converts this `Ref` into a slice reference.
50375048
///
50385049
/// `into_slice` consumes the `Ref`, and returns a reference to `[T]`.
50395050
#[inline(always)]
50405051
pub fn into_slice(self) -> &'a [T] {
5041-
assert!(B::INTO_REF_INTO_MUT_ARE_SOUND);
5042-
5043-
// SAFETY: According to the safety preconditions on
5044-
// `ByteSlice::INTO_REF_INTO_MUT_ARE_SOUND`, the preceding assert
5045-
// ensures that, given `B: 'a`, it is sound to drop `self` and still
5046-
// access the underlying memory using reads for `'a`.
5047-
unsafe { self.deref_slice_helper() }
5052+
// PANICS: By invariant on `Ref`, `self.0`'s size and alignment are
5053+
// valid for `[T]`, and so this `unwrap` will not panic.
5054+
let ptr = Ptr::from_ref(self.0.into())
5055+
.try_cast_into_no_leftover::<[T]>()
5056+
.expect("zerocopy internal error: into_slice should be infallible");
5057+
let ptr = ptr.bikeshed_recall_valid();
5058+
ptr.as_ref()
50485059
}
50495060
}
50505061

50515062
impl<'a, B, T> Ref<B, [T]>
50525063
where
5053-
B: 'a + ByteSliceMut,
5064+
B: 'a + IntoByteSliceMut<'a>,
50545065
T: FromBytes + IntoBytes + NoCell,
50555066
{
50565067
/// Converts this `Ref` into a mutable slice reference.
50575068
///
50585069
/// `into_mut_slice` consumes the `Ref`, and returns a mutable reference to
50595070
/// `[T]`.
50605071
#[inline(always)]
5061-
pub fn into_mut_slice(mut self) -> &'a mut [T] {
5062-
assert!(B::INTO_REF_INTO_MUT_ARE_SOUND);
5063-
5064-
// SAFETY: According to the safety preconditions on
5065-
// `ByteSlice::INTO_REF_INTO_MUT_ARE_SOUND`, the preceding assert
5066-
// ensures that, given `B: 'a + ByteSliceMut`, it is sound to drop
5067-
// `self` and still access the underlying memory using both reads and
5068-
// writes for `'a`.
5069-
unsafe { self.deref_mut_slice_helper() }
5072+
pub fn into_mut_slice(self) -> &'a mut [T] {
5073+
// PANICS: By invariant on `Ref`, `self.0`'s size and alignment are
5074+
// valid for `[T]`, and so this `unwrap` will not panic.
5075+
let ptr = Ptr::from_mut(self.0.into())
5076+
.try_cast_into_no_leftover::<[T]>()
5077+
.expect("zerocopy internal error: into_mut_slice should be infallible");
5078+
let ptr = ptr.bikeshed_recall_valid();
5079+
ptr.as_mut()
50705080
}
50715081
}
50725082

@@ -5481,29 +5491,6 @@ mod sealed {
54815491
pub unsafe trait ByteSlice:
54825492
Deref<Target = [u8]> + Sized + self::sealed::ByteSliceSealed
54835493
{
5484-
/// Are the [`Ref::into_ref`] and [`Ref::into_mut`] methods sound when used
5485-
/// with `Self`? If not, evaluating this constant must panic at compile
5486-
/// time.
5487-
///
5488-
/// This exists to work around #716 on versions of zerocopy prior to 0.8.
5489-
///
5490-
/// # Safety
5491-
///
5492-
/// This may only be set to true if the following holds: Given the
5493-
/// following:
5494-
/// - `Self: 'a`
5495-
/// - `bytes: Self`
5496-
/// - `let ptr = bytes.as_ptr()`
5497-
///
5498-
/// ...then:
5499-
/// - Using `ptr` to read the memory previously addressed by `bytes` is
5500-
/// sound for `'a` even after `bytes` has been dropped.
5501-
/// - If `Self: ByteSliceMut`, using `ptr` to write the memory previously
5502-
/// addressed by `bytes` is sound for `'a` even after `bytes` has been
5503-
/// dropped.
5504-
#[doc(hidden)]
5505-
const INTO_REF_INTO_MUT_ARE_SOUND: bool;
5506-
55075494
/// Gets a raw pointer to the first byte in the slice.
55085495
#[inline]
55095496
fn as_ptr(&self) -> *const u8 {
@@ -5534,48 +5521,58 @@ pub unsafe trait ByteSliceMut: ByteSlice + DerefMut {
55345521
}
55355522
}
55365523

5524+
/// A [`ByteSlice`] that conveys no ownership, and so can be converted into a
5525+
/// byte slice.
5526+
///
5527+
/// Some `ByteSlice` types (notably, the standard library's [`Ref`] type) convey
5528+
/// ownership, and so they cannot soundly be moved by-value into a byte slice
5529+
/// type (`&[u8]`). Some methods in this crate's API (such as [`Ref::into_ref`])
5530+
/// are only compatible with `ByteSlice` types without these ownership
5531+
/// semantics.
5532+
///
5533+
/// [`Ref`]: core::cell::Ref
5534+
pub trait IntoByteSlice<'a>: ByteSlice + Into<&'a [u8]> {}
5535+
5536+
/// A [`ByteSliceMut`] that conveys no ownership, and so can be converted into a
5537+
/// mutable byte slice.
5538+
///
5539+
/// Some `ByteSliceMut` types (notably, the standard library's [`RefMut`] type)
5540+
/// convey ownership, and so they cannot soundly be moved by-value into a byte
5541+
/// slice type (`&mut [u8]`). Some methods in this crate's API (such as
5542+
/// [`Ref::into_mut`]) are only compatible with `ByteSliceMut` types without
5543+
/// these ownership semantics.
5544+
///
5545+
/// [`RefMut`]: core::cell::RefMut
5546+
pub trait IntoByteSliceMut<'a>: ByteSliceMut + Into<&'a mut [u8]> {}
5547+
55375548
impl<'a> sealed::ByteSliceSealed for &'a [u8] {}
55385549
// TODO(#429): Add a "SAFETY" comment and remove this `allow`.
55395550
#[allow(clippy::undocumented_unsafe_blocks)]
55405551
unsafe impl<'a> ByteSlice for &'a [u8] {
5541-
// SAFETY: If `&'b [u8]: 'a`, then the underlying memory is treated as
5542-
// borrowed immutably for `'a` even if the slice itself is dropped.
5543-
const INTO_REF_INTO_MUT_ARE_SOUND: bool = true;
5544-
55455552
#[inline]
55465553
fn split_at(self, mid: usize) -> (Self, Self) {
55475554
<[u8]>::split_at(self, mid)
55485555
}
55495556
}
55505557

5558+
impl<'a> IntoByteSlice<'a> for &'a [u8] {}
5559+
55515560
impl<'a> sealed::ByteSliceSealed for &'a mut [u8] {}
55525561
// TODO(#429): Add a "SAFETY" comment and remove this `allow`.
55535562
#[allow(clippy::undocumented_unsafe_blocks)]
55545563
unsafe impl<'a> ByteSlice for &'a mut [u8] {
5555-
// SAFETY: If `&'b mut [u8]: 'a`, then the underlying memory is treated as
5556-
// borrowed mutably for `'a` even if the slice itself is dropped.
5557-
const INTO_REF_INTO_MUT_ARE_SOUND: bool = true;
5558-
55595564
#[inline]
55605565
fn split_at(self, mid: usize) -> (Self, Self) {
55615566
<[u8]>::split_at_mut(self, mid)
55625567
}
55635568
}
55645569

5570+
impl<'a> IntoByteSliceMut<'a> for &'a mut [u8] {}
5571+
55655572
impl<'a> sealed::ByteSliceSealed for cell::Ref<'a, [u8]> {}
55665573
// TODO(#429): Add a "SAFETY" comment and remove this `allow`.
55675574
#[allow(clippy::undocumented_unsafe_blocks)]
55685575
unsafe impl<'a> ByteSlice for cell::Ref<'a, [u8]> {
5569-
const INTO_REF_INTO_MUT_ARE_SOUND: bool = if !cfg!(doc) {
5570-
const_panic!("Ref::into_ref and Ref::into_mut are unsound when used with core::cell::Ref; see https://github.com/google/zerocopy/issues/716")
5571-
} else {
5572-
// When compiling documentation, allow the evaluation of this constant
5573-
// to succeed. This doesn't represent a soundness hole - it just delays
5574-
// any error to runtime. The reason we need this is that, otherwise,
5575-
// `rustdoc` will fail when trying to document this item.
5576-
false
5577-
};
5578-
55795576
#[inline]
55805577
fn split_at(self, mid: usize) -> (Self, Self) {
55815578
cell::Ref::map_split(self, |slice| <[u8]>::split_at(slice, mid))
@@ -5586,16 +5583,6 @@ impl<'a> sealed::ByteSliceSealed for RefMut<'a, [u8]> {}
55865583
// TODO(#429): Add a "SAFETY" comment and remove this `allow`.
55875584
#[allow(clippy::undocumented_unsafe_blocks)]
55885585
unsafe impl<'a> ByteSlice for RefMut<'a, [u8]> {
5589-
const INTO_REF_INTO_MUT_ARE_SOUND: bool = if !cfg!(doc) {
5590-
const_panic!("Ref::into_ref and Ref::into_mut are unsound when used with core::cell::RefMut; see https://github.com/google/zerocopy/issues/716")
5591-
} else {
5592-
// When compiling documentation, allow the evaluation of this constant
5593-
// to succeed. This doesn't represent a soundness hole - it just delays
5594-
// any error to runtime. The reason we need this is that, otherwise,
5595-
// `rustdoc` will fail when trying to document this item.
5596-
false
5597-
};
5598-
55995586
#[inline]
56005587
fn split_at(self, mid: usize) -> (Self, Self) {
56015588
RefMut::map_split(self, |slice| <[u8]>::split_at_mut(slice, mid))
@@ -8911,7 +8898,7 @@ mod tests {
89118898
// implementation of `<ManuallyDrop<T> as TryFromBytes>::is_bit_valid`.
89128899
assert_impls!(ManuallyDrop<[bool]>: KnownLayout, NoCell, TryFromBytes, FromZeros, IntoBytes, Unaligned, !FromBytes);
89138900
assert_impls!(ManuallyDrop<NotZerocopy>: !NoCell, !TryFromBytes, !KnownLayout, !FromZeros, !FromBytes, !IntoBytes, !Unaligned);
8914-
assert_impls!(ManuallyDrop<[NotZerocopy]>: !NoCell, !TryFromBytes, !KnownLayout, !FromZeros, !FromBytes, !IntoBytes, !Unaligned);
8901+
assert_impls!(ManuallyDrop<[NotZerocopy]>: KnownLayout, !NoCell, !TryFromBytes, !FromZeros, !FromBytes, !IntoBytes, !Unaligned);
89158902
assert_impls!(ManuallyDrop<UnsafeCell<()>>: KnownLayout, TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned, !NoCell);
89168903
assert_impls!(ManuallyDrop<[UnsafeCell<u8>]>: KnownLayout, TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned, !NoCell);
89178904
assert_impls!(ManuallyDrop<[UnsafeCell<bool>]>: KnownLayout, TryFromBytes, FromZeros, IntoBytes, Unaligned, !NoCell, !FromBytes);
@@ -8951,7 +8938,7 @@ mod tests {
89518938
Unaligned,
89528939
!FromBytes
89538940
);
8954-
assert_impls!([NotZerocopy]: !KnownLayout, !NoCell, !TryFromBytes, !FromZeros, !FromBytes, !IntoBytes, !Unaligned);
8941+
assert_impls!([NotZerocopy]: KnownLayout, !NoCell, !TryFromBytes, !FromZeros, !FromBytes, !IntoBytes, !Unaligned);
89558942
assert_impls!(
89568943
[u8; 0]: KnownLayout,
89578944
NoCell,

0 commit comments

Comments
 (0)