Skip to content

Commit

Permalink
Conditionally disable file fallback for Android and Linux (#396)
Browse files Browse the repository at this point in the history
  • Loading branch information
newpavlov authored Mar 27, 2024
1 parent 6b7bcb5 commit 8ffd43e
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 41 deletions.
1 change: 1 addition & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ jobs:
- uses: Swatinem/rust-cache@v2
- run: cargo test
- run: cargo test --features=std
- run: cargo test --features=linux_disable_fallback
- run: cargo test --features=custom # custom should do nothing here
- if: ${{ matrix.toolchain == 'nightly' }}
run: cargo test --benches
Expand Down
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,20 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased
### Added
- `linux_disable_fallback` crate feature to disable `/dev/urandom`-based fallback on Linux and
Android targets. Enabling this feature bumps minimum supported Linux kernel version to 4.17 and
Android API level to 23 (Marshmallow). [#396]

### Changed
- Disable `/dev/urandom` fallback for Linux targets outside of the following `target_arch`es:
`aarch64`, `arm`, `powerpc`, `powerpc64`, `s390x`, `x86`, `x86_64` [#396]
- Do not catch `EPERM` error code on Android while checking availability of
the `getrandom` syscall [#396]

[#396]: https://github.com/rust-random/getrandom/pull/396

## [0.2.12] - 2024-01-09
### Fixed
- Custom backend for targets without atomics [#385]
Expand Down
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ wasm-bindgen-test = "0.3.18"
[features]
# Implement std-only traits for getrandom::Error
std = []
# Disable `/dev/urandom` fallback for Linux and Android targets.
# Bumps minimum supported Linux kernel version to 3.17 and Android API level to 23 (Marshmallow).
linux_disable_fallback = []
# Feature to enable fallback RDRAND-based implementation on x86/x86_64
rdrand = []
# Feature to enable JavaScript bindings on wasm*-unknown-unknown
Expand Down
51 changes: 46 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,14 @@
//! This crate will then use the provided `webcrypto` implementation.
//!
//! ### Platform Support
//! This crate generally supports the same operating system and platform versions that the Rust standard library does.
//! Additional targets may be supported using pluggable custom implementations.
//! This crate generally supports the same operating system and platform versions
//! that the Rust standard library does. Additional targets may be supported using
//! pluggable custom implementations.
//!
//! This means that as Rust drops support for old versions of operating systems (such as old Linux kernel versions, Android API levels, etc)
//! in stable releases, `getrandom` may create new patch releases (`0.N.x`) that remove support for outdated platform versions.
//! This means that as Rust drops support for old versions of operating systems
//! (such as old Linux kernel versions, Android API levels, etc) in stable releases,
//! `getrandom` may create new patch releases (`0.N.x`) that remove support for
//! outdated platform versions.
//!
//! ### Custom implementations
//!
Expand Down Expand Up @@ -220,10 +223,48 @@ cfg_if! {
if #[cfg(any(target_os = "haiku", target_os = "redox", target_os = "nto", target_os = "aix"))] {
mod util_libc;
#[path = "use_file.rs"] mod imp;
} else if #[cfg(any(target_os = "android", target_os = "linux"))] {
} else if #[cfg(all(
not(feature = "linux_disable_fallback"),
any(
// Rust supports Android API level 19 (KitKat) [0] and the next upgrade targets
// level 21 (Lollipop) [1], while `getrandom(2)` was added only in
// level 23 (Marshmallow). Note that it applies only to the "old" `target_arch`es,
// RISC-V Android targets sufficiently new API level, same will apply for potential
// new Android `target_arch`es.
// [0]: https://blog.rust-lang.org/2023/01/09/android-ndk-update-r25.html
// [1]: https://github.com/rust-lang/rust/pull/120593
all(
target_os = "android",
any(
target_arch = "aarch64",
target_arch = "arm",
target_arch = "x86",
target_arch = "x86_64",
),
),
// Only on these `target_arch`es Rust supports Linux kernel versions (3.2+)
// that precede the version (3.17) in which `getrandom(2)` was added:
// https://doc.rust-lang.org/stable/rustc/platform-support.html
all(
target_os = "linux",
any(
target_arch = "aarch64",
target_arch = "arm",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "s390x",
target_arch = "x86",
target_arch = "x86_64",
),
)
),
))] {
mod util_libc;
mod use_file;
mod lazy;
#[path = "linux_android_with_fallback.rs"] mod imp;
} else if #[cfg(any(target_os = "android", target_os = "linux"))] {
mod util_libc;
#[path = "linux_android.rs"] mod imp;
} else if #[cfg(any(target_os = "illumos", target_os = "solaris"))] {
mod util_libc;
Expand Down
39 changes: 3 additions & 36 deletions src/linux_android.rs
Original file line number Diff line number Diff line change
@@ -1,40 +1,7 @@
//! Implementation for Linux / Android
use crate::{
lazy::LazyBool,
util_libc::{last_os_error, sys_fill_exact},
{use_file, Error},
};
//! Implementation for Linux / Android without `/dev/urandom` fallback
use crate::{util_libc, Error};
use core::mem::MaybeUninit;

pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
// getrandom(2) was introduced in Linux 3.17
static HAS_GETRANDOM: LazyBool = LazyBool::new();
if HAS_GETRANDOM.unsync_init(is_getrandom_available) {
sys_fill_exact(dest, |buf| unsafe {
getrandom(buf.as_mut_ptr() as *mut libc::c_void, buf.len(), 0)
})
} else {
use_file::getrandom_inner(dest)
}
}

fn is_getrandom_available() -> bool {
let res = unsafe { getrandom(core::ptr::null_mut(), 0, libc::GRND_NONBLOCK) };
if res < 0 {
match last_os_error().raw_os_error() {
Some(libc::ENOSYS) => false, // No kernel support
Some(libc::EPERM) => false, // Blocked by seccomp
_ => true,
}
} else {
true
}
}

unsafe fn getrandom(
buf: *mut libc::c_void,
buflen: libc::size_t,
flags: libc::c_uint,
) -> libc::ssize_t {
libc::syscall(libc::SYS_getrandom, buf, buflen, flags) as libc::ssize_t
util_libc::sys_fill_exact(dest, util_libc::getrandom_syscall)
}
33 changes: 33 additions & 0 deletions src/linux_android_with_fallback.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//! Implementation for Linux / Android with `/dev/urandom` fallback
use crate::{
lazy::LazyBool,
util_libc::{getrandom_syscall, last_os_error, sys_fill_exact},
{use_file, Error},
};
use core::mem::MaybeUninit;

pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
// getrandom(2) was introduced in Linux 3.17
static HAS_GETRANDOM: LazyBool = LazyBool::new();
if HAS_GETRANDOM.unsync_init(is_getrandom_available) {
sys_fill_exact(dest, getrandom_syscall)
} else {
use_file::getrandom_inner(dest)
}
}

fn is_getrandom_available() -> bool {
if getrandom_syscall(&mut []) < 0 {
match last_os_error().raw_os_error() {
Some(libc::ENOSYS) => false, // No kernel support
// The fallback on EPERM is intentionally not done on Android since this workaround
// seems to be needed only for specific Linux-based products that aren't based
// on Android. See https://github.com/rust-random/getrandom/issues/229.
#[cfg(target_os = "linux")]
Some(libc::EPERM) => false, // Blocked by seccomp
_ => true,
}
} else {
true
}
}
13 changes: 13 additions & 0 deletions src/util_libc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,3 +151,16 @@ pub unsafe fn open_readonly(path: &str) -> Result<libc::c_int, Error> {
}
}
}

/// Thin wrapper around the `getrandom()` Linux system call
#[cfg(any(target_os = "android", target_os = "linux"))]
pub fn getrandom_syscall(buf: &mut [MaybeUninit<u8>]) -> libc::ssize_t {
unsafe {
libc::syscall(
libc::SYS_getrandom,
buf.as_mut_ptr() as *mut libc::c_void,
buf.len(),
0,
) as libc::ssize_t
}
}

0 comments on commit 8ffd43e

Please sign in to comment.