Skip to content

Commit

Permalink
Fix UB when dropping BoxBytes owning a zero-sized layout
Browse files Browse the repository at this point in the history
  • Loading branch information
zachs18 committed Jul 29, 2024
1 parent 005ee32 commit 1ad9977
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 8 deletions.
25 changes: 17 additions & 8 deletions src/allocation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -742,8 +742,9 @@ impl<I: ?Sized, T: ?Sized + TransparentWrapper<I>> TransparentWrapperAlloc<I>

/// As `Box<[u8]>`, but remembers the original alignment.
pub struct BoxBytes {
// SAFETY: `ptr` is owned, was allocated with `layout`, and points to
// `layout.size()` initialized bytes.
// SAFETY: `ptr` is owned, points to `layout.size()` initialized bytes, and
// was allocated with `layout` (unless `layout.size() == 0` in which case it
// is dangling).
ptr: NonNull<u8>,
layout: Layout,
}
Expand All @@ -770,8 +771,11 @@ impl DerefMut for BoxBytes {

impl Drop for BoxBytes {
fn drop(&mut self) {
// SAFETY: See type invariant.
unsafe { alloc::alloc::dealloc(self.ptr.as_ptr(), self.layout) };
if self.layout.size() != 0 {
// SAFETY: See type invariant: if `self.layout.size() != 0`, then
// `self.ptr` is owned and was allocated with `self.layout`.
unsafe { alloc::alloc::dealloc(self.ptr.as_ptr(), self.layout) };
}
}
}

Expand Down Expand Up @@ -846,13 +850,18 @@ impl<T: AnyBitPattern> sealed::FromBoxBytes for [T] {
let single_layout = Layout::new::<T>();
if bytes.layout.align() != single_layout.align() {
Err((PodCastError::AlignmentMismatch, bytes))
} else if single_layout.size() == 0 {
Err((PodCastError::SizeMismatch, bytes))
} else if bytes.layout.size() % single_layout.size() != 0 {
} else if (single_layout.size() == 0 && bytes.layout.size() != 0)
|| (single_layout.size() != 0
&& bytes.layout.size() % single_layout.size() != 0)
{
Err((PodCastError::OutputSliceWouldHaveSlop, bytes))
} else {
let (ptr, layout) = bytes.into_raw_parts();
let length = layout.size() / single_layout.size();
let length = if single_layout.size() != 0 {
layout.size() / single_layout.size()
} else {
0
};
let ptr =
core::ptr::slice_from_raw_parts_mut(ptr.as_ptr() as *mut T, length);
// SAFETY: See BoxBytes type invariant.
Expand Down
13 changes: 13 additions & 0 deletions tests/cast_slice_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -328,3 +328,16 @@ fn test_arc_slices() {
let empty: Arc<[i8]> = cast_slice_arc::<(), i8>(Arc::new([(); 42]));
assert!(empty.is_empty());
}

#[cfg(feature = "extern_crate_alloc")]
#[test]
fn box_bytes_zst() {
let x: BoxBytes = box_bytes_of(Box::new([0u8; 0]));
let _: Box<[u8]> = from_box_bytes(x);

let x: BoxBytes = box_bytes_of(Box::new([0u8; 0]));
let _: Box<[()]> = from_box_bytes(x);

let x: BoxBytes = box_bytes_of(Box::new([(); 0]));
let _: Box<[u8]> = from_box_bytes(x);
}

0 comments on commit 1ad9977

Please sign in to comment.