From 3502c89f794307df7f55a98b76979bcbe0cad8aa Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Sat, 8 Mar 2025 21:37:44 -0800 Subject: [PATCH] Test Linux fallback paths without use of cfg()s This PR changes our Linux fallback tests to test the "real" fallback paths rather than using various `cfg()`s within the implementation. As these tests require being run sequentally and only work on Linux, they are configured to require manual invocation. Also renames `tests/mod.rs` to `tests/common.rs` to reuse our tests. Actually testing the netbsd fallback is harder, so that's best left for a future PR. Signed-off-by: Joe Richey --- .github/workflows/tests.yml | 12 ++----- CHANGELOG.md | 5 ++- Cargo.toml | 10 ++++-- src/backends/linux_android_with_fallback.rs | 9 +---- tests/{mod.rs => common.rs} | 0 tests/linux_force_fallback.rs | 38 +++++++++++++++++++++ tests/linux_no_fallback.rs | 17 +++++++++ 7 files changed, 69 insertions(+), 22 deletions(-) rename tests/{mod.rs => common.rs} (100%) create mode 100644 tests/linux_force_fallback.rs create mode 100644 tests/linux_no_fallback.rs diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 84b148b4..274a99d3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -53,6 +53,8 @@ jobs: targets: ${{ matrix.target }} - uses: Swatinem/rust-cache@v2 - run: cargo test --target=${{ matrix.target }} --features=std + - run: cargo test --test linux_no_fallback -- --test-threads 1 + - run: cargo test --test linux_force_fallback -- --test-threads 1 - env: RUSTFLAGS: -Dwarnings --cfg getrandom_backend="linux_getrandom" RUSTDOCFLAGS: -Dwarnings --cfg getrandom_backend="linux_getrandom" @@ -61,18 +63,10 @@ jobs: RUSTFLAGS: -Dwarnings --cfg getrandom_backend="linux_raw" RUSTDOCFLAGS: -Dwarnings --cfg getrandom_backend="linux_raw" run: cargo test --target=${{ matrix.target }} --features=std - - env: - RUSTFLAGS: -Dwarnings --cfg getrandom_test_linux_fallback - RUSTDOCFLAGS: -Dwarnings --cfg getrandom_test_linux_fallback - run: cargo test --features=std - - env: - RUSTFLAGS: -Dwarnings --cfg getrandom_test_linux_without_fallback - RUSTDOCFLAGS: -Dwarnings --cfg getrandom_test_linux_without_fallback - run: cargo test --features=std - env: RUSTFLAGS: -Dwarnings --cfg getrandom_backend="rdrand" RUSTDOCFLAGS: -Dwarnings --cfg getrandom_backend="rdrand" - run: cargo test --features=std + run: cargo test --target=${{ matrix.target }} --features=std ios: name: iOS Simulator diff --git a/CHANGELOG.md b/CHANGELOG.md index b53ccd03..b8042860 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,9 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `efi_rng` opt-in backend [#570] - `linux_raw` opt-in backend [#572] - `.cargo/config.toml` example in the crate-level docs [#591] -- `getrandom_test_linux_without_fallback` configuration flag to test that file fallback - is not triggered in the `linux_android_with_fallback` backend [#605] - Cygwin support [#626] +- Add tests for Linux fallback paths without hardcoding `cfg`s [#628] ### Changed - Remove `windows-targets` dependency and use [`raw-dylib`] directly [#627] @@ -35,11 +34,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [#597]: https://github.com/rust-random/getrandom/pull/597 [#602]: https://github.com/rust-random/getrandom/pull/602 [#603]: https://github.com/rust-random/getrandom/pull/603 -[#605]: https://github.com/rust-random/getrandom/pull/605 [#610]: https://github.com/rust-random/getrandom/pull/610 [#614]: https://github.com/rust-random/getrandom/pull/614 [#626]: https://github.com/rust-random/getrandom/pull/626 [#627]: https://github.com/rust-random/getrandom/pull/627 +[#628]: https://github.com/rust-random/getrandom/pull/628 [`raw-dylib`]: https://doc.rust-lang.org/reference/items/external-blocks.html?highlight=link#dylib-versus-raw-dylib ## [0.3.1] - 2025-01-28 diff --git a/Cargo.toml b/Cargo.toml index e55cbb89..6e3e0406 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -84,12 +84,18 @@ check-cfg = [ 'cfg(getrandom_backend, values("custom", "efi_rng", "rdrand", "rndr", "linux_getrandom", "linux_raw", "wasm_js"))', 'cfg(getrandom_msan)', 'cfg(getrandom_windows_legacy)', - 'cfg(getrandom_test_linux_fallback)', - 'cfg(getrandom_test_linux_without_fallback)', 'cfg(getrandom_test_netbsd_fallback)', 'cfg(target_os, values("cygwin"))', # TODO(MSRV 1.86): Remove this. ] +# Linux-specific fallback tests +[[test]] +name = "linux_force_fallback" +test = false +[[test]] +name = "linux_no_fallback" +test = false + [package.metadata.docs.rs] features = ["std"] diff --git a/src/backends/linux_android_with_fallback.rs b/src/backends/linux_android_with_fallback.rs index 2ad8f0a4..c05c2663 100644 --- a/src/backends/linux_android_with_fallback.rs +++ b/src/backends/linux_android_with_fallback.rs @@ -41,9 +41,7 @@ fn init() -> NonNull { let dangling_ptr = NonNull::dangling().as_ptr(); // Check that `getrandom` syscall is supported by kernel let res = unsafe { getrandom_fn(dangling_ptr, 0, 0) }; - if cfg!(getrandom_test_linux_fallback) { - NOT_AVAILABLE - } else if res.is_negative() { + if res.is_negative() { match util_libc::last_os_error().raw_os_error() { Some(libc::ENOSYS) => NOT_AVAILABLE, // No kernel support // The fallback on EPERM is intentionally not done on Android since this workaround @@ -60,11 +58,6 @@ fn init() -> NonNull { None => NOT_AVAILABLE, }; - #[cfg(getrandom_test_linux_without_fallback)] - if res_ptr == NOT_AVAILABLE { - panic!("Fallback is triggered with enabled `getrandom_test_linux_without_fallback`") - } - GETRANDOM_FN.store(res_ptr.as_ptr(), Ordering::Release); res_ptr } diff --git a/tests/mod.rs b/tests/common.rs similarity index 100% rename from tests/mod.rs rename to tests/common.rs diff --git a/tests/linux_force_fallback.rs b/tests/linux_force_fallback.rs new file mode 100644 index 00000000..7a91a942 --- /dev/null +++ b/tests/linux_force_fallback.rs @@ -0,0 +1,38 @@ +//! This test forces the Linux fallback path to be taken. This test must be +//! run manually and sequentally: +//! +//! cargo test --test linux_force_fallback -- --test-threads 1 +use std::{ + ffi::c_void, + fs::{read_dir, DirEntry}, + sync::atomic::{AtomicBool, Ordering}, +}; + +use libc::{__errno_location, c_uint, size_t, ssize_t, ENOSYS}; + +static FAKE_GETRANDOM_CALLED: AtomicBool = AtomicBool::new(false); + +// Override libc::getrandom to simulate failure +#[export_name = "getrandom"] +pub unsafe extern "C" fn fake_getrandom(_: *mut c_void, _: size_t, _: c_uint) -> ssize_t { + FAKE_GETRANDOM_CALLED.store(true, Ordering::SeqCst); + unsafe { *__errno_location() = ENOSYS }; + -1 +} + +mod common; + +#[test] +fn fake_getrandom_is_called() { + assert!(FAKE_GETRANDOM_CALLED.load(Ordering::SeqCst)); +} + +#[test] +fn dev_urandom_is_open() { + fn is_urandom(entry: DirEntry) -> bool { + let path = entry.path().canonicalize().expect("entry is valid"); + path.to_str() == Some("/dev/urandom") + } + let mut dir = read_dir("/proc/self/fd").expect("/proc/self exists"); + assert!(dir.any(|path| is_urandom(path.expect("entry exists")))); +} diff --git a/tests/linux_no_fallback.rs b/tests/linux_no_fallback.rs new file mode 100644 index 00000000..6f63d18e --- /dev/null +++ b/tests/linux_no_fallback.rs @@ -0,0 +1,17 @@ +//! This test ensures the Linux fallback path is not taken. As this test will +//! fail in some Linux configurations, it must be run manually and sequentally: +//! +//! cargo test --test linux_no_fallback -- --test-threads 1 +use std::fs::{read_dir, DirEntry}; + +mod common; + +#[test] +fn dev_urandom_is_not_open() { + fn is_urandom(entry: DirEntry) -> bool { + let path = entry.path().canonicalize().expect("entry is valid"); + path.to_str() == Some("/dev/urandom") + } + let mut dir = read_dir("/proc/self/fd").expect("/proc/self exists"); + assert!(dir.all(|path| !is_urandom(path.expect("entry exists")))); +}