Skip to content

Commit f4a95fe

Browse files
committed
Support msan; Android/Linux: unpoison output of getrandom syscall.
See the added comment for details.
1 parent 8686806 commit f4a95fe

File tree

6 files changed

+70
-4
lines changed

6 files changed

+70
-4
lines changed

Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ rustc-dep-of-std = [
5252
"libc/rustc-dep-of-std",
5353
"wasi/rustc-dep-of-std",
5454
]
55+
# Enable support for sanitizers; Requires Rust feature `cfg_sanitize`.
56+
unstable-sanitize = []
5557
# Unstable/test-only feature to run wasm-bindgen tests in a browser
5658
test-in-browser = []
5759

src/custom.rs

+3
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,9 @@ pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
9696
// compatibility with implementations that rely on that (e.g. Rust
9797
// implementations that construct a `&mut [u8]` slice from `dest` and
9898
// `len`).
99+
// XXX: Because we do this, memory sanitizer isn't able to detect when
100+
// `__getrandom_custom` fails to fill `dest`, but we can't poison `dest`
101+
// here either, for the same reason we have to fill it in the first place.
99102
let dest = uninit_slice_fill_zero(dest);
100103
let ret = unsafe { __getrandom_custom(dest.as_mut_ptr(), dest.len()) };
101104
match NonZeroU32::new(ret) {

src/lib.rs

+14
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,19 @@
3434
//!
3535
//! Pull Requests that add support for new targets to `getrandom` are always welcome.
3636
//!
37+
//! ## Memory Sanitizer (msan) support
38+
//!
39+
//! The `unstable-sanitize` feature adds Memory Sanitizer support. You must use
40+
//! Rust Nightly, e.g.
41+
//! ```sh
42+
//! RUSTFLAGS="-Zsanitizer=memory" \
43+
//! cargo +nightly test \
44+
//! -Zbuild-std --target=x86_64-unknown-linux-gnu --features=unstable-sanitize
45+
//! ```
46+
//! It is assumed that libstd/libc have had their APis instrumented to support
47+
//! sanitizers, so we only provide special support on Linux when using the
48+
//! `getrandom` syscall.
49+
//!
3750
//! ## Unsupported targets
3851
//!
3952
//! By default, `getrandom` will not compile on unsupported targets, but certain
@@ -208,6 +221,7 @@
208221
#![no_std]
209222
#![warn(rust_2018_idioms, unused_lifetimes, missing_docs)]
210223
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
224+
#![cfg_attr(feature = "unstable-sanitize", feature(cfg_sanitize))]
211225

212226
#[macro_use]
213227
extern crate cfg_if;

src/linux_android.rs

+14-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//! Implementation for Linux / Android without `/dev/urandom` fallback
2-
use crate::{util_libc, Error};
2+
use crate::{util, util_libc, Error};
33
use core::mem::MaybeUninit;
44

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

99
// Also used by linux_android_with_fallback to check if the syscall is available.
1010
pub fn getrandom_syscall(buf: &mut [MaybeUninit<u8>]) -> libc::ssize_t {
11-
unsafe {
11+
let res = unsafe {
1212
libc::syscall(
1313
libc::SYS_getrandom,
1414
buf.as_mut_ptr().cast::<core::ffi::c_void>(),
1515
buf.len(),
1616
0,
17-
) as libc::ssize_t
18-
}
17+
)
18+
} as libc::ssize_t;
19+
if let Ok(written) = usize::try_from(res) {
20+
// XXX: LLVM has support to do this automatically if/when libc is
21+
// compiled with it, but glibc that ships in typical Linux distros
22+
// doesn't. Assume Android's Bionic is similar. `-Zsanitizer=memory`
23+
// is not compatible with `+crt-static` according to rustc.
24+
unsafe {
25+
util::unpoison(buf.as_mut_ptr(), written);
26+
}
27+
};
28+
res
1929
}

src/util.rs

+17
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,20 @@ fn ptr_from_mut<T: ?Sized>(r: &mut T) -> *mut T {
4646
fn ptr_from_ref<T: ?Sized>(r: &T) -> *const T {
4747
r
4848
}
49+
50+
#[allow(unused_variables)]
51+
pub unsafe fn unpoison(ptr: *mut MaybeUninit<u8>, len: usize) {
52+
#[cfg(feature = "unstable-sanitize")]
53+
{
54+
// XXX: `#![feature(cfg_sanitize)]` doesn't enable the feature gate correctly.
55+
//#[cfg(sanitize = "memory")]
56+
{
57+
use core::ffi::c_void;
58+
extern "C" {
59+
// void __msan_unpoison(const volatile void *a, size_t size);
60+
fn __msan_unpoison(a: *mut c_void, size: usize);
61+
}
62+
__msan_unpoison(ptr.cast::<c_void>(), len)
63+
}
64+
}
65+
}

tests/common/mod.rs

+20
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,26 @@ fn test_huge() {
109109
fn test_huge_uninit() {
110110
let mut huge = [MaybeUninit::uninit(); 100_000];
111111
getrandom_uninit_impl(&mut huge).unwrap();
112+
check_initialized(&huge);
113+
}
114+
115+
#[allow(unused_variables)]
116+
fn check_initialized(buf: &[MaybeUninit<u8>]) {
117+
#[cfg(feature = "unstable-sanitize")]
118+
{
119+
// XXX: `#![feature(cfg_sanitize)]` doesn't enable the feature gate correctly.
120+
// #[cfg(sanitize = "memory")]
121+
{
122+
use core::ffi::c_void;
123+
extern "C" {
124+
// void __msan_check_mem_is_initialized(const volatile void *x, size_t size);
125+
fn __msan_check_mem_is_initialized(x: *const c_void, size: usize);
126+
}
127+
unsafe {
128+
__msan_check_mem_is_initialized(buf.as_ptr().cast::<c_void>(), buf.len());
129+
}
130+
}
131+
}
112132
}
113133

114134
// On WASM, the thread API always fails/panics

0 commit comments

Comments
 (0)