Skip to content

Commit fd32ded

Browse files
NathanSWardostwilkens
authored andcommitted
[bevy_core/bytes] Fix UB with accessing memory with incorrect alignment (bevyengine#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.
1 parent 13b2fe6 commit fd32ded

File tree

1 file changed

+22
-12
lines changed

1 file changed

+22
-12
lines changed

crates/bevy_core/src/bytes.rs

+22-12
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,16 @@ pub trait FromBytes {
4646

4747
impl<T> FromBytes for T
4848
where
49-
T: Byteable + Clone,
49+
T: Byteable + Copy,
5050
{
5151
fn from_bytes(bytes: &[u8]) -> Self {
52-
unsafe {
53-
let byte_ptr = bytes.as_ptr();
54-
let ptr = byte_ptr as *const Self;
55-
(*ptr).clone()
56-
}
52+
assert_eq!(
53+
bytes.len(),
54+
std::mem::size_of::<T>(),
55+
"Cannot convert byte slice `&[u8]` to type `{}`. They are not the same size.",
56+
std::any::type_name::<T>()
57+
);
58+
unsafe { bytes.as_ptr().cast::<T>().read_unaligned() }
5759
}
5860
}
5961

@@ -62,7 +64,7 @@ where
6264
T: Byteable,
6365
{
6466
fn as_bytes(&self) -> &[u8] {
65-
let len = std::mem::size_of_val(self);
67+
let len = std::mem::size_of::<T>();
6668
unsafe { core::slice::from_raw_parts(self as *const Self as *const u8, len) }
6769
}
6870
}
@@ -166,15 +168,23 @@ where
166168

167169
impl<T> FromBytes for Vec<T>
168170
where
169-
T: Sized + Clone + Byteable,
171+
T: Sized + Copy + Byteable,
170172
{
171173
fn from_bytes(bytes: &[u8]) -> Self {
174+
assert_eq!(
175+
bytes.len() % std::mem::size_of::<T>(),
176+
0,
177+
"Cannot convert byte slice `&[u8]` to type `Vec<{0}>`. Slice length is not a multiple of std::mem::size_of::<{0}>.",
178+
std::any::type_name::<T>(),
179+
);
180+
181+
let len = bytes.len() / std::mem::size_of::<T>();
182+
let mut vec = Vec::<T>::with_capacity(len);
172183
unsafe {
173-
let byte_ptr = bytes.as_ptr() as *const T;
174-
let len = bytes.len() / std::mem::size_of::<T>();
175-
let slice = core::slice::from_raw_parts::<T>(byte_ptr, len);
176-
slice.to_vec()
184+
std::ptr::copy_nonoverlapping(bytes.as_ptr(), vec.as_mut_ptr() as *mut u8, bytes.len());
185+
vec.set_len(len);
177186
}
187+
vec
178188
}
179189
}
180190

0 commit comments

Comments
 (0)