Skip to content

Commit

Permalink
Detect use of MemorySanitizer without using Nightly-only features (#571)
Browse files Browse the repository at this point in the history
This allows msan detection to "just-work" whenever someone passes
`-Zsanitizer=memory`. Users no longer need to do any
`getrandom`-specific configuration.

This will also continue working once
rust-lang/rust#123615 is merged, which
stabilizes some sanitizers (but not MemorySanitizer).

This is the approch taken by other low-level crates:
-
[`parking_lot_core`](https://github.com/Amanieu/parking_lot/blob/ca920b31312839013b4455aba1d53a4aede21b2f/core/build.rs)
-
[`crossbeam-utils`](https://github.com/crossbeam-rs/crossbeam/blob/00283fb1818174c25b02d7f1c883c5e19f8506a4/crossbeam-utils/build.rs#L42)

The only downside is that this adds a build-script, but it's as small as
possible, doesn't seem to impact build times, and is only a temporary
workaround.

---------

Signed-off-by: Joe Richey <[email protected]>
  • Loading branch information
josephlr authored Dec 18, 2024
1 parent dc89211 commit 9b902af
Show file tree
Hide file tree
Showing 6 changed files with 23 additions and 17 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,9 @@ jobs:
toolchain: nightly-2024-10-08
components: rust-src
- env:
RUSTFLAGS: -Dwarnings -Zsanitizer=memory --cfg getrandom_sanitize
# `--all-targets` is used to skip doc tests which currently fail linking
run: cargo test -Zbuild-std --target=x86_64-unknown-linux-gnu --all-targets
RUSTFLAGS: -Dwarnings -Zsanitizer=memory
RUSTDOCFLAGS: -Dwarnings -Zsanitizer=memory
run: cargo test -Zbuild-std --target=x86_64-unknown-linux-gnu

cross:
name: Cross
Expand Down
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `Error::new_custom` method [#507]
- `rndr` opt-in backend [#512]
- `linux_rustix` opt-in backend [#520]
- Memory sanitizer support gated behind `getrandom_sanitize` configuration flag [#521]
- Automatic MemorySanitizer support [#521] [#571]
- `u32` and `u64` functions for generating random values of the respective type [#544]

### Fixed
Expand All @@ -61,6 +61,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
[#554]: https://github.com/rust-random/getrandom/pull/554
[#555]: https://github.com/rust-random/getrandom/pull/555
[#557]: https://github.com/rust-random/getrandom/pull/557
[#571]: https://github.com/rust-random/getrandom/pull/571

## [0.2.15] - 2024-05-06
### Added
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ rustc-dep-of-std = ["dep:compiler_builtins", "dep:core"]
level = "warn"
check-cfg = [
'cfg(getrandom_backend, values("custom", "rdrand", "rndr", "linux_getrandom", "linux_rustix", "wasm_js", "esp_idf"))',
'cfg(getrandom_sanitize)',
'cfg(getrandom_msan)',
'cfg(getrandom_test_linux_fallback)',
'cfg(getrandom_test_netbsd_fallback)',
]
Expand Down
13 changes: 6 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,15 +267,14 @@ our code should correctly handle it and return an error, e.g.

## Sanitizer support

If your code uses [`fill_uninit`] and you enable memory sanitization
(i.e. `-Zsanitizer=memory`), you need to pass the `getrandom_sanitize`
configuration flag to enable unpoisoning of the destination buffer
filled by `fill_uninit`.
If your code uses [`fill_uninit`] and you enable
[MemorySanitizer](https://doc.rust-lang.org/beta/unstable-book/compiler-flags/sanitizer.html#memorysanitizer)
(i.e. `-Zsanitizer=memory`), we will automatically handle unpoisoning
of the destination buffer filled by `fill_uninit`.

For example, it can be done as follows (requires a Nightly compiler):
You can run sanitizer tests for your crate dependent on `getrandom` like this:
```sh
RUSTFLAGS="-Zsanitizer=memory --cfg getrandom_sanitize" \
cargo test -Zbuild-std --target=x86_64-unknown-linux-gnu
RUSTFLAGS="-Zsanitizer=memory" cargo test -Zbuild-std --target=x86_64-unknown-linux-gnu
```

## Minimum Supported Rust Version
Expand Down
9 changes: 9 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Automatically detect cfg(sanitize = "memory") even if cfg(sanitize) isn't
// supported. Build scripts get cfg() info, even if the cfg is unstable.
fn main() {
println!("cargo:rerun-if-changed=build.rs");
let santizers = std::env::var("CARGO_CFG_SANITIZE").unwrap_or_default();
if santizers.contains("memory") {
println!("cargo:rustc-cfg=getrandom_msan");
}
}
7 changes: 2 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
#![doc = include_str!("../README.md")]
#![warn(rust_2018_idioms, unused_lifetimes, missing_docs)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![cfg_attr(getrandom_sanitize, feature(cfg_sanitize))]
#![deny(
clippy::cast_lossless,
clippy::cast_possible_truncation,
Expand Down Expand Up @@ -99,17 +98,15 @@ pub fn fill_uninit(dest: &mut [MaybeUninit<u8>]) -> Result<&mut [u8], Error> {
backends::fill_inner(dest)?;
}

#[cfg(getrandom_sanitize)]
#[cfg(sanitize = "memory")]
#[cfg(getrandom_msan)]
extern "C" {
fn __msan_unpoison(a: *mut core::ffi::c_void, size: usize);
}

// SAFETY: `dest` has been fully initialized by `imp::fill_inner`
// since it returned `Ok`.
Ok(unsafe {
#[cfg(getrandom_sanitize)]
#[cfg(sanitize = "memory")]
#[cfg(getrandom_msan)]
__msan_unpoison(dest.as_mut_ptr().cast(), dest.len());

util::slice_assume_init_mut(dest)
Expand Down

0 comments on commit 9b902af

Please sign in to comment.