Skip to content

Test Linux fallback paths without use of cfg()s #628

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 3 additions & 9 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure that the order of tests is guaranteed?

- env:
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="linux_getrandom"
RUSTDOCFLAGS: -Dwarnings --cfg getrandom_backend="linux_getrandom"
Expand All @@ -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
Expand Down
5 changes: 2 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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
Expand Down
10 changes: 8 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]

Expand Down
9 changes: 1 addition & 8 deletions src/backends/linux_android_with_fallback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,7 @@ fn init() -> NonNull<c_void> {
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
Expand All @@ -60,11 +58,6 @@ fn init() -> NonNull<c_void> {
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
}
Expand Down
File renamed without changes.
38 changes: 38 additions & 0 deletions tests/linux_force_fallback.rs
Original file line number Diff line number Diff line change
@@ -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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think AcqRel should be sufficient here?

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"))));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not simply inline the is_urandom function?

let has_open_urandom = read_dir("/proc/self/fd")
    .expect("/proc/self exists")
    .any(|dir_entry| {
        let path = dir_entry.expect("entry exists").path();
        let path = path.canonicalize().expect("entry is valid");
        path.to_str() == Some("/dev/urandom")
    });
assert!(has_open_urandom);

}
17 changes: 17 additions & 0 deletions tests/linux_no_fallback.rs
Original file line number Diff line number Diff line change
@@ -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"))));
}
Loading