diff --git a/Cargo.toml b/Cargo.toml index b9357bce..581b3f21 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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 = [] diff --git a/src/custom.rs b/src/custom.rs index 79be7fc2..29b62815 100644 --- a/src/custom.rs +++ b/src/custom.rs @@ -96,6 +96,9 @@ pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> 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) { diff --git a/src/lib.rs b/src/lib.rs index d1e9ef09..170478ab 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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 @@ -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; diff --git a/src/linux_android.rs b/src/linux_android.rs index 7c1fede4..4e7dd81f 100644 --- a/src/linux_android.rs +++ b/src/linux_android.rs @@ -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]) -> Result<(), Error> { @@ -8,12 +8,22 @@ pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { // Also used by linux_android_with_fallback to check if the syscall is available. pub fn getrandom_syscall(buf: &mut [MaybeUninit]) -> libc::ssize_t { - unsafe { + let res = unsafe { libc::syscall( libc::SYS_getrandom, buf.as_mut_ptr().cast::(), 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 } diff --git a/src/util.rs b/src/util.rs index 9eeba9a7..3beeb1d8 100644 --- a/src/util.rs +++ b/src/util.rs @@ -46,3 +46,20 @@ fn ptr_from_mut(r: &mut T) -> *mut T { fn ptr_from_ref(r: &T) -> *const T { r } + +#[allow(unused_variables)] +pub unsafe fn unpoison(ptr: *mut MaybeUninit, 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::(), len) + } + } +} diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 6f283e09..29efcb3a 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -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]) { + #[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::(), buf.len()); + } + } + } } // On WASM, the thread API always fails/panics