granne / granne

Graph-based Approximate Nearest Neighbor Search

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Unsound implementation in `write_as_bytes`

shinmao opened this issue · comments

commented

The source of unsoundness

Hi, we are the researchers from Sun Security Lab. When we run our internal bug detectors through the crates.io, we found that your crate might include an unsound implementation. Please see the following code:

granne/src/io.rs

Lines 11 to 18 in 5f3ea2e

pub fn write_as_bytes<T, B: Write>(elements: &[T], buffer: &mut B) -> Result<usize> {
let size = elements.len() * ::std::mem::size_of::<T>();
let data = unsafe { ::std::slice::from_raw_parts(elements.as_ptr() as *const u8, size) };
buffer.write_all(data)?;
Ok(size)
}

At line 13, callers could pass arbitrary types and cast it to u8 type. However, slice::from_raw_parts requires callers to guarantee the pointer point to consecutive initialized memory. If users pass struct with padding bytes into the function, then it would cause to undefined behavior.

Reproduce the bug in rust-playground

Even though the io module was private, we consider that safe function should still avoid undefined behavior in any situation. Here is the same code to be reproduced in rust-playground1.

use std::io::{Write, Result};
use std::fs::File;

fn write_as_bytes<T, B: Write>(elements: &[T], buffer: &mut B) -> Result<usize> {
    let size = elements.len() * ::std::mem::size_of::<T>();
    let data = unsafe { ::std::slice::from_raw_parts(elements.as_ptr() as *const u8, size) };

    buffer.write_all(data)?;

    Ok(size)
}

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

fn main() -> std::io::Result<()> {
    let mut buffer = File::create("foo.txt")?;
    let pd = Padding { a: 10, b: 11, c: 12 };
    let pd_arr: [Padding; 1] = [pd; 1];
    println!("{:?}", write_as_bytes(&pd_arr, &mut buffer));
    Ok(())
}

to run with miri,

error: Undefined Behavior: reading memory at alloc940[0x0..0x40], but memory is uninitialized at [0x4..0x40], and this operation requires initialized memory
   --> /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/sys/unix/fd.rs:252:13
    |
252 | /             libc::write(
253 | |                 self.as_raw_fd(),
254 | |                 buf.as_ptr() as *const libc::c_void,
255 | |                 cmp::min(buf.len(), READ_LIMIT),
256 | |             )
    | |_____________^ reading memory at alloc940[0x0..0x40], but memory is uninitialized at [0x4..0x40], and this operation requires initialized memory
    |
    = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior

Footnotes

  1. https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=2687ac995f5c576bb6e25d6cd7db4742