Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unsound implementation of DataBuffer::extend and extend_from_slice #108

Open
shinmao opened this issue Sep 1, 2023 · 1 comment
Open

Comments

@shinmao
Copy link

shinmao commented Sep 1, 2023

The source of unsoundness

pub fn extend<T>(&mut self, value: &T) -> DataBufferPtr<T>
where
T: Copy,
{
let data =
unsafe { slice::from_raw_parts(value as *const T as *const u8, mem::size_of::<T>()) };

Hi, I think these two functions might include unsound implementation. They both allow users to pass arbitrary types and cast to u8 type. To guarantee the safety of using slice::from_raw_parts, callers need to make sure the u8 pointer points to consecutive initialized memory. If users pass the arbitrary types with padding bytes, then it could break the guarantee. I think T should be limited to primitive types. Same issue could happen in extend_from_slice.

@shinmao
Copy link
Author

shinmao commented Sep 1, 2023

Similar issue could be found in video::assets::mesh::IndexFormat::encode

pub fn encode<T>(values: &[T]) -> &[u8]
where
T: Copy,
{
let len = values.len() * ::std::mem::size_of::<T>();
unsafe { ::std::slice::from_raw_parts(values.as_ptr() as *const u8, len) }
}

At line 168, users could also pass arbitrary types and cast to u8 type.

To reproduce the bug

use crayon::video::assets::mesh::IndexFormat;

#[repr(align(64))]
#[derive(Copy, Clone, Debug)]
struct Padding {
    a: u8,
    b: u16,
    c: u8,
}

fn main() {
    let pd = Padding { a: 10, b: 11, c: 12 };
    let pd_arr: [Padding; 1] = [pd; 1];
    let db = IndexFormat::encode(&pd_arr);
    println!("{:?}", db);
}

to run with miri,

error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
   --> /home/rafael/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/fmt/num.rs:466:5
    |
466 | /     impl_Display!(
467 | |         i8, u8, i16, u16, i32, u32, i64, u64, usize, isize
468 | |             as u64 via to_u64 named fmt_u64
469 | |     );
    | |_____^ using uninitialized data, but this operation requires initialized memory
    |
    = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior

Note

In extend and extend_from_slice, even though library didn't read/write the slice memory, it still broke the safety guarantee of from_slice_parts. Library should avoid such kind of usage.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant