From 1ad997795ea09707b22ec552a0eb8e7a07bae1da Mon Sep 17 00:00:00 2001 From: Zachary S Date: Sun, 28 Jul 2024 23:31:32 -0500 Subject: [PATCH] Fix UB when dropping BoxBytes owning a zero-sized layout --- src/allocation.rs | 25 +++++++++++++++++-------- tests/cast_slice_tests.rs | 13 +++++++++++++ 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/allocation.rs b/src/allocation.rs index ae0dca1..b7057df 100644 --- a/src/allocation.rs +++ b/src/allocation.rs @@ -742,8 +742,9 @@ impl> TransparentWrapperAlloc /// 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, layout: Layout, } @@ -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) }; + } } } @@ -846,13 +850,18 @@ impl sealed::FromBoxBytes for [T] { let single_layout = Layout::new::(); 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. diff --git a/tests/cast_slice_tests.rs b/tests/cast_slice_tests.rs index 71bc4df..b9fb3f2 100644 --- a/tests/cast_slice_tests.rs +++ b/tests/cast_slice_tests.rs @@ -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); +}