Skip to content

Commit

Permalink
Support msan; Android/Linux: unpoison output of getrandom syscall.
Browse files Browse the repository at this point in the history
See the added comment for details.
  • Loading branch information
briansmith committed Jun 7, 2024
1 parent 8686806 commit f4a95fe
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 4 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ rustc-dep-of-std = [
"libc/rustc-dep-of-std",
"wasi/rustc-dep-of-std",
]
# Enable support for sanitizers; Requires Rust feature `cfg_sanitize`.
unstable-sanitize = []
# Unstable/test-only feature to run wasm-bindgen tests in a browser
test-in-browser = []

Expand Down
3 changes: 3 additions & 0 deletions src/custom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
// compatibility with implementations that rely on that (e.g. Rust
// implementations that construct a `&mut [u8]` slice from `dest` and
// `len`).
// XXX: Because we do this, memory sanitizer isn't able to detect when
// `__getrandom_custom` fails to fill `dest`, but we can't poison `dest`
// here either, for the same reason we have to fill it in the first place.
let dest = uninit_slice_fill_zero(dest);
let ret = unsafe { __getrandom_custom(dest.as_mut_ptr(), dest.len()) };
match NonZeroU32::new(ret) {
Expand Down
14 changes: 14 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,19 @@
//!
//! Pull Requests that add support for new targets to `getrandom` are always welcome.
//!
//! ## Memory Sanitizer (msan) support
//!
//! The `unstable-sanitize` feature adds Memory Sanitizer support. You must use
//! Rust Nightly, e.g.
//! ```sh
//! RUSTFLAGS="-Zsanitizer=memory" \
//! cargo +nightly test \
//! -Zbuild-std --target=x86_64-unknown-linux-gnu --features=unstable-sanitize
//! ```
//! It is assumed that libstd/libc have had their APis instrumented to support
//! sanitizers, so we only provide special support on Linux when using the
//! `getrandom` syscall.
//!
//! ## Unsupported targets
//!
//! By default, `getrandom` will not compile on unsupported targets, but certain
Expand Down Expand Up @@ -208,6 +221,7 @@
#![no_std]
#![warn(rust_2018_idioms, unused_lifetimes, missing_docs)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![cfg_attr(feature = "unstable-sanitize", feature(cfg_sanitize))]

#[macro_use]
extern crate cfg_if;
Expand Down
18 changes: 14 additions & 4 deletions src/linux_android.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//! Implementation for Linux / Android without `/dev/urandom` fallback
use crate::{util_libc, Error};
use crate::{util, util_libc, Error};
use core::mem::MaybeUninit;

pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
Expand All @@ -8,12 +8,22 @@ pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {

// Also used by linux_android_with_fallback to check if the syscall is available.
pub fn getrandom_syscall(buf: &mut [MaybeUninit<u8>]) -> libc::ssize_t {
unsafe {
let res = unsafe {
libc::syscall(
libc::SYS_getrandom,
buf.as_mut_ptr().cast::<core::ffi::c_void>(),
buf.len(),
0,
) as libc::ssize_t
}
)
} as libc::ssize_t;
if let Ok(written) = usize::try_from(res) {
// XXX: LLVM has support to do this automatically if/when libc is
// compiled with it, but glibc that ships in typical Linux distros
// doesn't. Assume Android's Bionic is similar. `-Zsanitizer=memory`
// is not compatible with `+crt-static` according to rustc.
unsafe {
util::unpoison(buf.as_mut_ptr(), written);
}
};
res
}
17 changes: 17 additions & 0 deletions src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,20 @@ fn ptr_from_mut<T: ?Sized>(r: &mut T) -> *mut T {
fn ptr_from_ref<T: ?Sized>(r: &T) -> *const T {
r
}

#[allow(unused_variables)]
pub unsafe fn unpoison(ptr: *mut MaybeUninit<u8>, len: usize) {
#[cfg(feature = "unstable-sanitize")]
{
// XXX: `#![feature(cfg_sanitize)]` doesn't enable the feature gate correctly.
//#[cfg(sanitize = "memory")]
{
use core::ffi::c_void;
extern "C" {
// void __msan_unpoison(const volatile void *a, size_t size);
fn __msan_unpoison(a: *mut c_void, size: usize);
}
__msan_unpoison(ptr.cast::<c_void>(), len)
}
}
}
20 changes: 20 additions & 0 deletions tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,26 @@ fn test_huge() {
fn test_huge_uninit() {
let mut huge = [MaybeUninit::uninit(); 100_000];
getrandom_uninit_impl(&mut huge).unwrap();
check_initialized(&huge);
}

#[allow(unused_variables)]
fn check_initialized(buf: &[MaybeUninit<u8>]) {
#[cfg(feature = "unstable-sanitize")]
{
// XXX: `#![feature(cfg_sanitize)]` doesn't enable the feature gate correctly.
// #[cfg(sanitize = "memory")]
{
use core::ffi::c_void;
extern "C" {
// void __msan_check_mem_is_initialized(const volatile void *x, size_t size);
fn __msan_check_mem_is_initialized(x: *const c_void, size: usize);
}
unsafe {
__msan_check_mem_is_initialized(buf.as_ptr().cast::<c_void>(), buf.len());
}
}
}
}

// On WASM, the thread API always fails/panics
Expand Down

0 comments on commit f4a95fe

Please sign in to comment.