mirror of
https://github.com/Lokathor/bytemuck.git
synced 2024-11-21 22:32:23 +00:00
Fix panics in try_cast_slice_box
etc (#254)
* Fix panics in try_cast_slice_{box,rc,arc},cast_vec. Casting from non-empty non-ZST slices to ZST slices now returns Err(SizeMismatch) (instead of panicking). Casting from empty non-ZST slices to ZST slices is allowed and returns an empty slice (instead of panicking). Casting from ZST slices to non-ZST slices is allowed and returns an empty slice (status quo). * Add tests for cast_slice_box,rc,arc.
This commit is contained in:
parent
02ffd53a4e
commit
005ee3254f
@ -21,7 +21,7 @@ use alloc::{
|
|||||||
vec::Vec,
|
vec::Vec,
|
||||||
};
|
};
|
||||||
use core::{
|
use core::{
|
||||||
mem::ManuallyDrop,
|
mem::{size_of_val, ManuallyDrop},
|
||||||
ops::{Deref, DerefMut},
|
ops::{Deref, DerefMut},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -172,7 +172,10 @@ pub fn try_cast_slice_box<A: NoUninit, B: AnyBitPattern>(
|
|||||||
if align_of::<A>() != align_of::<B>() {
|
if align_of::<A>() != align_of::<B>() {
|
||||||
Err((PodCastError::AlignmentMismatch, input))
|
Err((PodCastError::AlignmentMismatch, input))
|
||||||
} else if size_of::<A>() != size_of::<B>() {
|
} else if size_of::<A>() != size_of::<B>() {
|
||||||
if size_of::<A>() * input.len() % size_of::<B>() != 0 {
|
let input_bytes = size_of_val::<[A]>(&*input);
|
||||||
|
if (size_of::<B>() == 0 && input_bytes != 0)
|
||||||
|
|| (size_of::<B>() != 0 && input_bytes % size_of::<B>() != 0)
|
||||||
|
{
|
||||||
// If the size in bytes of the underlying buffer does not match an exact
|
// If the size in bytes of the underlying buffer does not match an exact
|
||||||
// multiple of the size of B, we cannot cast between them.
|
// multiple of the size of B, we cannot cast between them.
|
||||||
Err((PodCastError::SizeMismatch, input))
|
Err((PodCastError::SizeMismatch, input))
|
||||||
@ -185,7 +188,8 @@ pub fn try_cast_slice_box<A: NoUninit, B: AnyBitPattern>(
|
|||||||
// Luckily, Layout only stores two things, the alignment, and the size in
|
// Luckily, Layout only stores two things, the alignment, and the size in
|
||||||
// bytes. So as long as both of those stay the same, the Layout will
|
// bytes. So as long as both of those stay the same, the Layout will
|
||||||
// remain a valid input to dealloc.
|
// remain a valid input to dealloc.
|
||||||
let length = size_of::<A>() * input.len() / size_of::<B>();
|
let length =
|
||||||
|
if size_of::<B>() != 0 { input_bytes / size_of::<B>() } else { 0 };
|
||||||
let box_ptr: *mut A = Box::into_raw(input) as *mut A;
|
let box_ptr: *mut A = Box::into_raw(input) as *mut A;
|
||||||
let ptr: *mut [B] =
|
let ptr: *mut [B] =
|
||||||
unsafe { core::slice::from_raw_parts_mut(box_ptr as *mut B, length) };
|
unsafe { core::slice::from_raw_parts_mut(box_ptr as *mut B, length) };
|
||||||
@ -222,8 +226,12 @@ pub fn try_cast_vec<A: NoUninit, B: AnyBitPattern>(
|
|||||||
if align_of::<A>() != align_of::<B>() {
|
if align_of::<A>() != align_of::<B>() {
|
||||||
Err((PodCastError::AlignmentMismatch, input))
|
Err((PodCastError::AlignmentMismatch, input))
|
||||||
} else if size_of::<A>() != size_of::<B>() {
|
} else if size_of::<A>() != size_of::<B>() {
|
||||||
if size_of::<A>() * input.len() % size_of::<B>() != 0
|
let input_size = size_of_val::<[A]>(&*input);
|
||||||
|| size_of::<A>() * input.capacity() % size_of::<B>() != 0
|
let input_capacity = input.capacity() * size_of::<A>();
|
||||||
|
if (size_of::<B>() == 0 && input_capacity != 0)
|
||||||
|
|| (size_of::<B>() != 0
|
||||||
|
&& (input_size % size_of::<B>() != 0
|
||||||
|
|| input_capacity % size_of::<B>() != 0))
|
||||||
{
|
{
|
||||||
// If the size in bytes of the underlying buffer does not match an exact
|
// If the size in bytes of the underlying buffer does not match an exact
|
||||||
// multiple of the size of B, we cannot cast between them.
|
// multiple of the size of B, we cannot cast between them.
|
||||||
@ -244,8 +252,10 @@ pub fn try_cast_vec<A: NoUninit, B: AnyBitPattern>(
|
|||||||
|
|
||||||
// Note(Lokathor): First we record the length and capacity, which don't
|
// Note(Lokathor): First we record the length and capacity, which don't
|
||||||
// have any secret provenance metadata.
|
// have any secret provenance metadata.
|
||||||
let length: usize = size_of::<A>() * input.len() / size_of::<B>();
|
let length: usize =
|
||||||
let capacity: usize = size_of::<A>() * input.capacity() / size_of::<B>();
|
if size_of::<B>() != 0 { input_size / size_of::<B>() } else { 0 };
|
||||||
|
let capacity: usize =
|
||||||
|
if size_of::<B>() != 0 { input_capacity / size_of::<B>() } else { 0 };
|
||||||
// Note(Lokathor): Next we "pre-forget" the old Vec by wrapping with
|
// Note(Lokathor): Next we "pre-forget" the old Vec by wrapping with
|
||||||
// ManuallyDrop, because if we used `core::mem::forget` after taking the
|
// ManuallyDrop, because if we used `core::mem::forget` after taking the
|
||||||
// pointer then that would invalidate our pointer. In nightly there's a
|
// pointer then that would invalidate our pointer. In nightly there's a
|
||||||
@ -415,7 +425,10 @@ pub fn try_cast_slice_rc<
|
|||||||
if align_of::<A>() != align_of::<B>() {
|
if align_of::<A>() != align_of::<B>() {
|
||||||
Err((PodCastError::AlignmentMismatch, input))
|
Err((PodCastError::AlignmentMismatch, input))
|
||||||
} else if size_of::<A>() != size_of::<B>() {
|
} else if size_of::<A>() != size_of::<B>() {
|
||||||
if size_of::<A>() * input.len() % size_of::<B>() != 0 {
|
let input_bytes = size_of_val::<[A]>(&*input);
|
||||||
|
if (size_of::<B>() == 0 && input_bytes != 0)
|
||||||
|
|| (size_of::<B>() != 0 && input_bytes % size_of::<B>() != 0)
|
||||||
|
{
|
||||||
// If the size in bytes of the underlying buffer does not match an exact
|
// If the size in bytes of the underlying buffer does not match an exact
|
||||||
// multiple of the size of B, we cannot cast between them.
|
// multiple of the size of B, we cannot cast between them.
|
||||||
Err((PodCastError::SizeMismatch, input))
|
Err((PodCastError::SizeMismatch, input))
|
||||||
@ -427,7 +440,8 @@ pub fn try_cast_slice_rc<
|
|||||||
// acquired from Rc::into_raw() must have the same size alignment and
|
// acquired from Rc::into_raw() must have the same size alignment and
|
||||||
// size of the type T in the new Rc<T>. So as long as both the size
|
// size of the type T in the new Rc<T>. So as long as both the size
|
||||||
// and alignment stay the same, the Rc will remain a valid Rc.
|
// and alignment stay the same, the Rc will remain a valid Rc.
|
||||||
let length = size_of::<A>() * input.len() / size_of::<B>();
|
let length =
|
||||||
|
if size_of::<B>() != 0 { input_bytes / size_of::<B>() } else { 0 };
|
||||||
let rc_ptr: *const A = Rc::into_raw(input) as *const A;
|
let rc_ptr: *const A = Rc::into_raw(input) as *const A;
|
||||||
// Must use ptr::slice_from_raw_parts, because we cannot make an
|
// Must use ptr::slice_from_raw_parts, because we cannot make an
|
||||||
// intermediate const reference, because it has mutable provenance,
|
// intermediate const reference, because it has mutable provenance,
|
||||||
@ -479,7 +493,10 @@ pub fn try_cast_slice_arc<
|
|||||||
if align_of::<A>() != align_of::<B>() {
|
if align_of::<A>() != align_of::<B>() {
|
||||||
Err((PodCastError::AlignmentMismatch, input))
|
Err((PodCastError::AlignmentMismatch, input))
|
||||||
} else if size_of::<A>() != size_of::<B>() {
|
} else if size_of::<A>() != size_of::<B>() {
|
||||||
if size_of::<A>() * input.len() % size_of::<B>() != 0 {
|
let input_bytes = size_of_val::<[A]>(&*input);
|
||||||
|
if (size_of::<B>() == 0 && input_bytes != 0)
|
||||||
|
|| (size_of::<B>() != 0 && input_bytes % size_of::<B>() != 0)
|
||||||
|
{
|
||||||
// If the size in bytes of the underlying buffer does not match an exact
|
// If the size in bytes of the underlying buffer does not match an exact
|
||||||
// multiple of the size of B, we cannot cast between them.
|
// multiple of the size of B, we cannot cast between them.
|
||||||
Err((PodCastError::SizeMismatch, input))
|
Err((PodCastError::SizeMismatch, input))
|
||||||
@ -491,7 +508,8 @@ pub fn try_cast_slice_arc<
|
|||||||
// acquired from Arc::into_raw() must have the same size alignment and
|
// acquired from Arc::into_raw() must have the same size alignment and
|
||||||
// size of the type T in the new Arc<T>. So as long as both the size
|
// size of the type T in the new Arc<T>. So as long as both the size
|
||||||
// and alignment stay the same, the Arc will remain a valid Arc.
|
// and alignment stay the same, the Arc will remain a valid Arc.
|
||||||
let length = size_of::<A>() * input.len() / size_of::<B>();
|
let length =
|
||||||
|
if size_of::<B>() != 0 { input_bytes / size_of::<B>() } else { 0 };
|
||||||
let arc_ptr: *const A = Arc::into_raw(input) as *const A;
|
let arc_ptr: *const A = Arc::into_raw(input) as *const A;
|
||||||
// Must use ptr::slice_from_raw_parts, because we cannot make an
|
// Must use ptr::slice_from_raw_parts, because we cannot make an
|
||||||
// intermediate const reference, because it has mutable provenance,
|
// intermediate const reference, because it has mutable provenance,
|
||||||
|
@ -195,3 +195,136 @@ fn test_panics() {
|
|||||||
let aligned_bytes = bytemuck::cast_slice::<u32, u8>(&[0, 0]);
|
let aligned_bytes = bytemuck::cast_slice::<u32, u8>(&[0, 0]);
|
||||||
should_panic!(from_bytes::<u32>(&aligned_bytes[1..5]));
|
should_panic!(from_bytes::<u32>(&aligned_bytes[1..5]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "extern_crate_alloc")]
|
||||||
|
#[test]
|
||||||
|
fn test_boxed_slices() {
|
||||||
|
let boxed_u8_slice: Box<[u8]> = Box::new([0, 1, u8::MAX, i8::MAX as u8]);
|
||||||
|
let boxed_i8_slice: Box<[i8]> = cast_slice_box::<u8, i8>(boxed_u8_slice);
|
||||||
|
assert_eq!(&*boxed_i8_slice, [0, 1, -1, i8::MAX]);
|
||||||
|
|
||||||
|
let result: Result<Box<[u16]>, (PodCastError, Box<[i8]>)> =
|
||||||
|
try_cast_slice_box(boxed_i8_slice);
|
||||||
|
let (error, boxed_i8_slice) =
|
||||||
|
result.expect_err("u16 and i8 have different alignment");
|
||||||
|
assert_eq!(error, PodCastError::AlignmentMismatch);
|
||||||
|
|
||||||
|
// FIXME(#253): Should these next two casts' errors be consistent?
|
||||||
|
let result: Result<&[[i8; 3]], PodCastError> =
|
||||||
|
try_cast_slice(&*boxed_i8_slice);
|
||||||
|
let error =
|
||||||
|
result.expect_err("slice of [i8; 3] cannot be made from slice of 4 i8s");
|
||||||
|
assert_eq!(error, PodCastError::OutputSliceWouldHaveSlop);
|
||||||
|
|
||||||
|
let result: Result<Box<[[i8; 3]]>, (PodCastError, Box<[i8]>)> =
|
||||||
|
try_cast_slice_box(boxed_i8_slice);
|
||||||
|
let (error, boxed_i8_slice) =
|
||||||
|
result.expect_err("slice of [i8; 3] cannot be made from slice of 4 i8s");
|
||||||
|
assert_eq!(error, PodCastError::SizeMismatch);
|
||||||
|
|
||||||
|
let empty: Box<[()]> = cast_slice_box::<u8, ()>(Box::new([]));
|
||||||
|
assert!(empty.is_empty());
|
||||||
|
|
||||||
|
let result: Result<Box<[()]>, (PodCastError, Box<[i8]>)> =
|
||||||
|
try_cast_slice_box(boxed_i8_slice);
|
||||||
|
let (error, boxed_i8_slice) =
|
||||||
|
result.expect_err("slice of ZST cannot be made from slice of 4 u8s");
|
||||||
|
assert_eq!(error, PodCastError::SizeMismatch);
|
||||||
|
|
||||||
|
drop(boxed_i8_slice);
|
||||||
|
|
||||||
|
let empty: Box<[i8]> = cast_slice_box::<(), i8>(Box::new([]));
|
||||||
|
assert!(empty.is_empty());
|
||||||
|
|
||||||
|
let empty: Box<[i8]> = cast_slice_box::<(), i8>(Box::new([(); 42]));
|
||||||
|
assert!(empty.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "extern_crate_alloc")]
|
||||||
|
#[test]
|
||||||
|
fn test_rc_slices() {
|
||||||
|
use std::rc::Rc;
|
||||||
|
let rc_u8_slice: Rc<[u8]> = Rc::new([0, 1, u8::MAX, i8::MAX as u8]);
|
||||||
|
let rc_i8_slice: Rc<[i8]> = cast_slice_rc::<u8, i8>(rc_u8_slice);
|
||||||
|
assert_eq!(&*rc_i8_slice, [0, 1, -1, i8::MAX]);
|
||||||
|
|
||||||
|
let result: Result<Rc<[u16]>, (PodCastError, Rc<[i8]>)> =
|
||||||
|
try_cast_slice_rc(rc_i8_slice);
|
||||||
|
let (error, rc_i8_slice) =
|
||||||
|
result.expect_err("u16 and i8 have different alignment");
|
||||||
|
assert_eq!(error, PodCastError::AlignmentMismatch);
|
||||||
|
|
||||||
|
// FIXME(#253): Should these next two casts' errors be consistent?
|
||||||
|
let result: Result<&[[i8; 3]], PodCastError> = try_cast_slice(&*rc_i8_slice);
|
||||||
|
let error =
|
||||||
|
result.expect_err("slice of [i8; 3] cannot be made from slice of 4 i8s");
|
||||||
|
assert_eq!(error, PodCastError::OutputSliceWouldHaveSlop);
|
||||||
|
|
||||||
|
let result: Result<Rc<[[i8; 3]]>, (PodCastError, Rc<[i8]>)> =
|
||||||
|
try_cast_slice_rc(rc_i8_slice);
|
||||||
|
let (error, rc_i8_slice) =
|
||||||
|
result.expect_err("slice of [i8; 3] cannot be made from slice of 4 i8s");
|
||||||
|
assert_eq!(error, PodCastError::SizeMismatch);
|
||||||
|
|
||||||
|
let empty: Rc<[()]> = cast_slice_rc::<u8, ()>(Rc::new([]));
|
||||||
|
assert!(empty.is_empty());
|
||||||
|
|
||||||
|
let result: Result<Rc<[()]>, (PodCastError, Rc<[i8]>)> =
|
||||||
|
try_cast_slice_rc(rc_i8_slice);
|
||||||
|
let (error, rc_i8_slice) =
|
||||||
|
result.expect_err("slice of ZST cannot be made from slice of 4 u8s");
|
||||||
|
assert_eq!(error, PodCastError::SizeMismatch);
|
||||||
|
|
||||||
|
drop(rc_i8_slice);
|
||||||
|
|
||||||
|
let empty: Rc<[i8]> = cast_slice_rc::<(), i8>(Rc::new([]));
|
||||||
|
assert!(empty.is_empty());
|
||||||
|
|
||||||
|
let empty: Rc<[i8]> = cast_slice_rc::<(), i8>(Rc::new([(); 42]));
|
||||||
|
assert!(empty.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "extern_crate_alloc")]
|
||||||
|
#[cfg(target_has_atomic = "ptr")]
|
||||||
|
#[test]
|
||||||
|
fn test_arc_slices() {
|
||||||
|
use std::sync::Arc;
|
||||||
|
let arc_u8_slice: Arc<[u8]> = Arc::new([0, 1, u8::MAX, i8::MAX as u8]);
|
||||||
|
let arc_i8_slice: Arc<[i8]> = cast_slice_arc::<u8, i8>(arc_u8_slice);
|
||||||
|
assert_eq!(&*arc_i8_slice, [0, 1, -1, i8::MAX]);
|
||||||
|
|
||||||
|
let result: Result<Arc<[u16]>, (PodCastError, Arc<[i8]>)> =
|
||||||
|
try_cast_slice_arc(arc_i8_slice);
|
||||||
|
let (error, arc_i8_slice) =
|
||||||
|
result.expect_err("u16 and i8 have different alignment");
|
||||||
|
assert_eq!(error, PodCastError::AlignmentMismatch);
|
||||||
|
|
||||||
|
// FIXME(#253): Should these next two casts' errors be consistent?
|
||||||
|
let result: Result<&[[i8; 3]], PodCastError> = try_cast_slice(&*arc_i8_slice);
|
||||||
|
let error =
|
||||||
|
result.expect_err("slice of [i8; 3] cannot be made from slice of 4 i8s");
|
||||||
|
assert_eq!(error, PodCastError::OutputSliceWouldHaveSlop);
|
||||||
|
|
||||||
|
let result: Result<Arc<[[i8; 3]]>, (PodCastError, Arc<[i8]>)> =
|
||||||
|
try_cast_slice_arc(arc_i8_slice);
|
||||||
|
let (error, arc_i8_slice) =
|
||||||
|
result.expect_err("slice of [i8; 3] cannot be made from slice of 4 i8s");
|
||||||
|
assert_eq!(error, PodCastError::SizeMismatch);
|
||||||
|
|
||||||
|
let empty: Arc<[()]> = cast_slice_arc::<u8, ()>(Arc::new([]));
|
||||||
|
assert!(empty.is_empty());
|
||||||
|
|
||||||
|
let result: Result<Arc<[()]>, (PodCastError, Arc<[i8]>)> =
|
||||||
|
try_cast_slice_arc(arc_i8_slice);
|
||||||
|
let (error, arc_i8_slice) =
|
||||||
|
result.expect_err("slice of ZST cannot be made from slice of 4 u8s");
|
||||||
|
assert_eq!(error, PodCastError::SizeMismatch);
|
||||||
|
|
||||||
|
drop(arc_i8_slice);
|
||||||
|
|
||||||
|
let empty: Arc<[i8]> = cast_slice_arc::<(), i8>(Arc::new([]));
|
||||||
|
assert!(empty.is_empty());
|
||||||
|
|
||||||
|
let empty: Arc<[i8]> = cast_slice_arc::<(), i8>(Arc::new([(); 42]));
|
||||||
|
assert!(empty.is_empty());
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user