[bevy_core/bytes] Fix UB with accessing memory with incorrect alignment (#1966)

After running `bevy_core` through `miri`, errors were reported surrounding incorrect memory accesses within the `bytes` test suit. 

Specifically:
```
test bytes::tests::test_array_round_trip ... error: Undefined Behavior: accessing memory with alignment 1, but alignment 4 is required
   --> crates/bevy_core/src/bytes.rs:55:13
    |
55  |             (*ptr).clone()
    |             ^^^^^^ accessing memory with alignment 1, but alignment 4 is required
    |
```

and 

```
test bytes::tests::test_vec_bytes_round_trip ... error: Undefined Behavior: accessing memory with alignment 2, but alignment 4 is required
   --> /home/nward/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/slice/raw.rs:95:14
    |
95  |     unsafe { &*ptr::slice_from_raw_parts(data, len) }
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ accessing memory with alignment 2, but alignment 4 is required
    |
```

Solution:

The solution is to use `slice::align_to` method to ensure correct alignment.
This commit is contained in:
Nathan Ward 2021-04-20 21:04:08 +00:00
parent c74994ba69
commit cbfb456847

View File

@ -46,14 +46,16 @@ pub trait FromBytes {
impl<T> FromBytes for T impl<T> FromBytes for T
where where
T: Byteable + Clone, T: Byteable + Copy,
{ {
fn from_bytes(bytes: &[u8]) -> Self { fn from_bytes(bytes: &[u8]) -> Self {
unsafe { assert_eq!(
let byte_ptr = bytes.as_ptr(); bytes.len(),
let ptr = byte_ptr as *const Self; std::mem::size_of::<T>(),
(*ptr).clone() "Cannot convert byte slice `&[u8]` to type `{}`. They are not the same size.",
} std::any::type_name::<T>()
);
unsafe { bytes.as_ptr().cast::<T>().read_unaligned() }
} }
} }
@ -62,7 +64,7 @@ where
T: Byteable, T: Byteable,
{ {
fn as_bytes(&self) -> &[u8] { fn as_bytes(&self) -> &[u8] {
let len = std::mem::size_of_val(self); let len = std::mem::size_of::<T>();
unsafe { core::slice::from_raw_parts(self as *const Self as *const u8, len) } unsafe { core::slice::from_raw_parts(self as *const Self as *const u8, len) }
} }
} }
@ -166,15 +168,23 @@ where
impl<T> FromBytes for Vec<T> impl<T> FromBytes for Vec<T>
where where
T: Sized + Clone + Byteable, T: Sized + Copy + Byteable,
{ {
fn from_bytes(bytes: &[u8]) -> Self { fn from_bytes(bytes: &[u8]) -> Self {
unsafe { assert_eq!(
let byte_ptr = bytes.as_ptr() as *const T; bytes.len() % std::mem::size_of::<T>(),
0,
"Cannot convert byte slice `&[u8]` to type `Vec<{0}>`. Slice length is not a multiple of std::mem::size_of::<{0}>.",
std::any::type_name::<T>(),
);
let len = bytes.len() / std::mem::size_of::<T>(); let len = bytes.len() / std::mem::size_of::<T>();
let slice = core::slice::from_raw_parts::<T>(byte_ptr, len); let mut vec = Vec::<T>::with_capacity(len);
slice.to_vec() unsafe {
std::ptr::copy_nonoverlapping(bytes.as_ptr(), vec.as_mut_ptr() as *mut u8, bytes.len());
vec.set_len(len);
} }
vec
} }
} }