[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
where
T: Byteable + Clone,
T: Byteable + Copy,
{
fn from_bytes(bytes: &[u8]) -> Self {
unsafe {
let byte_ptr = bytes.as_ptr();
let ptr = byte_ptr as *const Self;
(*ptr).clone()
}
assert_eq!(
bytes.len(),
std::mem::size_of::<T>(),
"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,
{
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) }
}
}
@ -166,15 +168,23 @@ where
impl<T> FromBytes for Vec<T>
where
T: Sized + Clone + Byteable,
T: Sized + Copy + Byteable,
{
fn from_bytes(bytes: &[u8]) -> Self {
assert_eq!(
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 mut vec = Vec::<T>::with_capacity(len);
unsafe {
let byte_ptr = bytes.as_ptr() as *const T;
let len = bytes.len() / std::mem::size_of::<T>();
let slice = core::slice::from_raw_parts::<T>(byte_ptr, len);
slice.to_vec()
std::ptr::copy_nonoverlapping(bytes.as_ptr(), vec.as_mut_ptr() as *mut u8, bytes.len());
vec.set_len(len);
}
vec
}
}