Skip to content

Commit 8ffd43e

Browse files
authored
Conditionally disable file fallback for Android and Linux (#396)
1 parent 6b7bcb5 commit 8ffd43e

File tree

7 files changed

+113
-41
lines changed

7 files changed

+113
-41
lines changed

.github/workflows/tests.yml

+1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ jobs:
6161
- uses: Swatinem/rust-cache@v2
6262
- run: cargo test
6363
- run: cargo test --features=std
64+
- run: cargo test --features=linux_disable_fallback
6465
- run: cargo test --features=custom # custom should do nothing here
6566
- if: ${{ matrix.toolchain == 'nightly' }}
6667
run: cargo test --benches

CHANGELOG.md

+14
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,20 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## Unreleased
8+
### Added
9+
- `linux_disable_fallback` crate feature to disable `/dev/urandom`-based fallback on Linux and
10+
Android targets. Enabling this feature bumps minimum supported Linux kernel version to 4.17 and
11+
Android API level to 23 (Marshmallow). [#396]
12+
13+
### Changed
14+
- Disable `/dev/urandom` fallback for Linux targets outside of the following `target_arch`es:
15+
`aarch64`, `arm`, `powerpc`, `powerpc64`, `s390x`, `x86`, `x86_64` [#396]
16+
- Do not catch `EPERM` error code on Android while checking availability of
17+
the `getrandom` syscall [#396]
18+
19+
[#396]: https://github.com/rust-random/getrandom/pull/396
20+
721
## [0.2.12] - 2024-01-09
822
### Fixed
923
- Custom backend for targets without atomics [#385]

Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ wasm-bindgen-test = "0.3.18"
3232
[features]
3333
# Implement std-only traits for getrandom::Error
3434
std = []
35+
# Disable `/dev/urandom` fallback for Linux and Android targets.
36+
# Bumps minimum supported Linux kernel version to 3.17 and Android API level to 23 (Marshmallow).
37+
linux_disable_fallback = []
3538
# Feature to enable fallback RDRAND-based implementation on x86/x86_64
3639
rdrand = []
3740
# Feature to enable JavaScript bindings on wasm*-unknown-unknown

src/lib.rs

+46-5
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,14 @@
9999
//! This crate will then use the provided `webcrypto` implementation.
100100
//!
101101
//! ### Platform Support
102-
//! This crate generally supports the same operating system and platform versions that the Rust standard library does.
103-
//! Additional targets may be supported using pluggable custom implementations.
102+
//! This crate generally supports the same operating system and platform versions
103+
//! that the Rust standard library does. Additional targets may be supported using
104+
//! pluggable custom implementations.
104105
//!
105-
//! This means that as Rust drops support for old versions of operating systems (such as old Linux kernel versions, Android API levels, etc)
106-
//! in stable releases, `getrandom` may create new patch releases (`0.N.x`) that remove support for outdated platform versions.
106+
//! This means that as Rust drops support for old versions of operating systems
107+
//! (such as old Linux kernel versions, Android API levels, etc) in stable releases,
108+
//! `getrandom` may create new patch releases (`0.N.x`) that remove support for
109+
//! outdated platform versions.
107110
//!
108111
//! ### Custom implementations
109112
//!
@@ -220,10 +223,48 @@ cfg_if! {
220223
if #[cfg(any(target_os = "haiku", target_os = "redox", target_os = "nto", target_os = "aix"))] {
221224
mod util_libc;
222225
#[path = "use_file.rs"] mod imp;
223-
} else if #[cfg(any(target_os = "android", target_os = "linux"))] {
226+
} else if #[cfg(all(
227+
not(feature = "linux_disable_fallback"),
228+
any(
229+
// Rust supports Android API level 19 (KitKat) [0] and the next upgrade targets
230+
// level 21 (Lollipop) [1], while `getrandom(2)` was added only in
231+
// level 23 (Marshmallow). Note that it applies only to the "old" `target_arch`es,
232+
// RISC-V Android targets sufficiently new API level, same will apply for potential
233+
// new Android `target_arch`es.
234+
// [0]: https://blog.rust-lang.org/2023/01/09/android-ndk-update-r25.html
235+
// [1]: https://github.com/rust-lang/rust/pull/120593
236+
all(
237+
target_os = "android",
238+
any(
239+
target_arch = "aarch64",
240+
target_arch = "arm",
241+
target_arch = "x86",
242+
target_arch = "x86_64",
243+
),
244+
),
245+
// Only on these `target_arch`es Rust supports Linux kernel versions (3.2+)
246+
// that precede the version (3.17) in which `getrandom(2)` was added:
247+
// https://doc.rust-lang.org/stable/rustc/platform-support.html
248+
all(
249+
target_os = "linux",
250+
any(
251+
target_arch = "aarch64",
252+
target_arch = "arm",
253+
target_arch = "powerpc",
254+
target_arch = "powerpc64",
255+
target_arch = "s390x",
256+
target_arch = "x86",
257+
target_arch = "x86_64",
258+
),
259+
)
260+
),
261+
))] {
224262
mod util_libc;
225263
mod use_file;
226264
mod lazy;
265+
#[path = "linux_android_with_fallback.rs"] mod imp;
266+
} else if #[cfg(any(target_os = "android", target_os = "linux"))] {
267+
mod util_libc;
227268
#[path = "linux_android.rs"] mod imp;
228269
} else if #[cfg(any(target_os = "illumos", target_os = "solaris"))] {
229270
mod util_libc;

src/linux_android.rs

+3-36
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,7 @@
1-
//! Implementation for Linux / Android
2-
use crate::{
3-
lazy::LazyBool,
4-
util_libc::{last_os_error, sys_fill_exact},
5-
{use_file, Error},
6-
};
1+
//! Implementation for Linux / Android without `/dev/urandom` fallback
2+
use crate::{util_libc, Error};
73
use core::mem::MaybeUninit;
84

95
pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
10-
// getrandom(2) was introduced in Linux 3.17
11-
static HAS_GETRANDOM: LazyBool = LazyBool::new();
12-
if HAS_GETRANDOM.unsync_init(is_getrandom_available) {
13-
sys_fill_exact(dest, |buf| unsafe {
14-
getrandom(buf.as_mut_ptr() as *mut libc::c_void, buf.len(), 0)
15-
})
16-
} else {
17-
use_file::getrandom_inner(dest)
18-
}
19-
}
20-
21-
fn is_getrandom_available() -> bool {
22-
let res = unsafe { getrandom(core::ptr::null_mut(), 0, libc::GRND_NONBLOCK) };
23-
if res < 0 {
24-
match last_os_error().raw_os_error() {
25-
Some(libc::ENOSYS) => false, // No kernel support
26-
Some(libc::EPERM) => false, // Blocked by seccomp
27-
_ => true,
28-
}
29-
} else {
30-
true
31-
}
32-
}
33-
34-
unsafe fn getrandom(
35-
buf: *mut libc::c_void,
36-
buflen: libc::size_t,
37-
flags: libc::c_uint,
38-
) -> libc::ssize_t {
39-
libc::syscall(libc::SYS_getrandom, buf, buflen, flags) as libc::ssize_t
6+
util_libc::sys_fill_exact(dest, util_libc::getrandom_syscall)
407
}

src/linux_android_with_fallback.rs

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//! Implementation for Linux / Android with `/dev/urandom` fallback
2+
use crate::{
3+
lazy::LazyBool,
4+
util_libc::{getrandom_syscall, last_os_error, sys_fill_exact},
5+
{use_file, Error},
6+
};
7+
use core::mem::MaybeUninit;
8+
9+
pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
10+
// getrandom(2) was introduced in Linux 3.17
11+
static HAS_GETRANDOM: LazyBool = LazyBool::new();
12+
if HAS_GETRANDOM.unsync_init(is_getrandom_available) {
13+
sys_fill_exact(dest, getrandom_syscall)
14+
} else {
15+
use_file::getrandom_inner(dest)
16+
}
17+
}
18+
19+
fn is_getrandom_available() -> bool {
20+
if getrandom_syscall(&mut []) < 0 {
21+
match last_os_error().raw_os_error() {
22+
Some(libc::ENOSYS) => false, // No kernel support
23+
// The fallback on EPERM is intentionally not done on Android since this workaround
24+
// seems to be needed only for specific Linux-based products that aren't based
25+
// on Android. See https://github.com/rust-random/getrandom/issues/229.
26+
#[cfg(target_os = "linux")]
27+
Some(libc::EPERM) => false, // Blocked by seccomp
28+
_ => true,
29+
}
30+
} else {
31+
true
32+
}
33+
}

src/util_libc.rs

+13
Original file line numberDiff line numberDiff line change
@@ -151,3 +151,16 @@ pub unsafe fn open_readonly(path: &str) -> Result<libc::c_int, Error> {
151151
}
152152
}
153153
}
154+
155+
/// Thin wrapper around the `getrandom()` Linux system call
156+
#[cfg(any(target_os = "android", target_os = "linux"))]
157+
pub fn getrandom_syscall(buf: &mut [MaybeUninit<u8>]) -> libc::ssize_t {
158+
unsafe {
159+
libc::syscall(
160+
libc::SYS_getrandom,
161+
buf.as_mut_ptr() as *mut libc::c_void,
162+
buf.len(),
163+
0,
164+
) as libc::ssize_t
165+
}
166+
}

0 commit comments

Comments
 (0)