Skip to content

Commit

Permalink
linux_android: Use direct syscall instead of libc to support *-none.
Browse files Browse the repository at this point in the history
Remove the last libc dependency from `linux_android`, as a step
towards supporting x86_64-unknown-linux-none.

This requires bumping the MSRV to 1.59.
  • Loading branch information
briansmith committed Jun 6, 2024
1 parent ec888f9 commit 14f6df9
Show file tree
Hide file tree
Showing 8 changed files with 92 additions and 21 deletions.
2 changes: 1 addition & 1 deletion .clippy.toml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
msrv = "1.57"
msrv = "1.59"
6 changes: 5 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-22.04, windows-2022]
toolchain: [nightly, beta, stable, 1.57]
toolchain: [nightly, beta, stable, 1.59]
# Only Test macOS on stable to reduce macOS CI jobs
include:
# x86_64-apple-darwin.
Expand All @@ -61,6 +61,8 @@ jobs:
- uses: Swatinem/rust-cache@v2
- run: cargo test
- run: cargo test --features=std
# This is assumed to be the same code path as x86_64-*-linux-none, since
# we don't/can't run tests for that target yet.
- run: cargo test --features=linux_disable_fallback
- run: cargo test --features=custom # custom should do nothing here
- if: ${{ matrix.toolchain == 'nightly' }}
Expand Down Expand Up @@ -361,6 +363,8 @@ jobs:
features: ["rdrand"]
- target: i686-unknown-hurd-gnu
features: ["std"]
- target: x86_64-unknown-linux-none
features: ["rdrand"]
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@nightly # Required to build libcore
Expand Down
8 changes: 6 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "getrandom"
version = "0.2.15" # Also update html_root_url in lib.rs when bumping this
edition = "2021"
rust-version = "1.57" # Sync .clippy.toml, tests.yml, and README.md.
rust-version = "1.59" # Sync .clippy.toml, tests.yml, and README.md.
authors = ["The Rand Project Developers"]
license = "MIT OR Apache-2.0"
description = "A small cross-platform library for retrieving random data from system source"
Expand All @@ -18,7 +18,11 @@ cfg-if = "1"
compiler_builtins = { version = "0.1", optional = true }
core = { version = "1.0", optional = true, package = "rustc-std-workspace-core" }

[target.'cfg(unix)'.dependencies]
# XXX: Additionally, we don't use libc when feature `linux_disable_fallback` is
# enabled on `x86_64-*-linux-*`, but we can't express this. In that case, we
# require libc to be built, and we force it to be linked, but we don't actually
# use anything from it.
[target.'cfg(all(unix, not(all(target_arch = "x86_64", target_os = "linux", target_env = ""))))'.dependencies]
libc = { version = "0.2.154", default-features = false }

[target.'cfg(target_os = "wasi")'.dependencies]
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ crate features, WASM support and Custom RNGs see the

## Minimum Supported Rust Version

This crate requires Rust 1.57.0 or later.
This crate requires Rust 1.59.0 or later.

## Platform Support

Expand Down
9 changes: 6 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,10 @@ cfg_if! {
mod util_libc;
#[path = "getrandom.rs"] mod imp;
} else if #[cfg(all(
not(feature = "linux_disable_fallback"),
// Always treat feature="linux_disable_fallback" identically to
// target_env="" to ensure the code paths are the same. This is
// important because we can't run tests for target_env="" (yet).
not(any(feature = "linux_disable_fallback", target_env="")),
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
Expand Down Expand Up @@ -304,8 +307,8 @@ cfg_if! {
mod linux_android;
#[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;
mod linux_android;
use linux_android as imp;
} else if #[cfg(target_os = "solaris")] {
mod util_libc;
#[path = "solaris.rs"] mod imp;
Expand Down
74 changes: 62 additions & 12 deletions src/linux_android.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,68 @@ pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
sys_fill_exact(dest, getrandom_syscall)
}

// The value of `EINTR` is not architecture-specific. It is checked against
// `libc::EINTR` by linux_android_with_fallback.rs.
pub const EINTR: i32 = 4;

// Also used by linux_android_with_fallback to check if the syscall is available.
pub fn getrandom_syscall(buf: &mut [MaybeUninit<u8>]) -> Result<usize, Error> {
use crate::util_libc::last_os_error;
cfg_if! {
// Assume Android always has libc available and always go through libc on
// Android to support any future possible restrictions on direct syscall
// access by the Android sandbox.
//
// TODO: Expand inilne assembly to other architectures to avoid depending
// on libc on Linux.
if #[cfg(all(target_os = "linux", target_arch = "x86_64"))] {
type Word = u64;
type IWord = i64;

#[allow(non_upper_case_globals)]
pub const SYS_getrandom: IWord = if cfg!(target_arch = "x86_64") { 318 } else { unimplemented!() };

pub fn getrandom_syscall(buf: &mut [MaybeUninit<u8>]) -> Result<usize, Error> {
const _:() = assert!(core::mem::size_of::<Word>() == core::mem::size_of::<usize>());

let mut ret: IWord;
let flags = 0;
unsafe {
core::arch::asm!(
"syscall",
in("rax") SYS_getrandom,
in("rdi") buf.as_mut_ptr(),
in("rsi") buf.len(),
in("rdx") flags,
lateout("rcx") _,
lateout("r11") _,
lateout("rax") ret,
options(nostack),
);
}
match Word::try_from(ret) {
Ok(written) => {
const _:() = assert!(core::mem::size_of::<Word>() <= core::mem::size_of::<usize>());
Ok(written as usize)
},
Err(_) => {
Err(u32::try_from(ret.unsigned_abs()).map_or(
Error::UNEXPECTED, Error::from_os_error))
}
}
}
} else {
use crate::util_libc::last_os_error;

let ret: libc::c_long = unsafe {
libc::syscall(
libc::SYS_getrandom,
buf.as_mut_ptr().cast::<core::ffi::c_void>(),
buf.len(),
0,
)
};
const _: () = assert!(core::mem::size_of::<libc::c_long>() == core::mem::size_of::<isize>());
usize::try_from(ret as isize).map_err(|_| last_os_error())
pub fn getrandom_syscall(buf: &mut [MaybeUninit<u8>]) -> Result<usize, Error> {
let ret: libc::c_long = unsafe {
libc::syscall(
libc::SYS_getrandom,
buf.as_mut_ptr().cast::<core::ffi::c_void>(),
buf.len(),
0,
)
};
const _:() = assert!(core::mem::size_of::<libc::c_long>() == core::mem::size_of::<isize>());
usize::try_from(ret as isize).map_err(|_| last_os_error())
}
}
}
4 changes: 4 additions & 0 deletions src/linux_android_with_fallback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
use crate::{lazy::LazyBool, linux_android, use_file, Error};
use core::mem::MaybeUninit;

const _: () = assert!(linux_android::EINTR == libc::EINTR);
#[cfg(target_arch = "x86_64")]
const _: () = assert!(linux_android::SYS_getrandom == libc::SYS_getrandom);

pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
// getrandom(2) was introduced in Linux 3.17
static HAS_GETRANDOM: LazyBool = LazyBool::new();
Expand Down
8 changes: 7 additions & 1 deletion src/util_unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,18 @@ pub fn sys_fill_exact(
mut buf: &mut [MaybeUninit<u8>],
sys_fill: impl Fn(&mut [MaybeUninit<u8>]) -> Result<usize, Error>,
) -> Result<(), Error> {
// Avoid depending on libc for Linux/Android.
#[cfg(any(target_os = "android", target_os = "linux"))]
use crate::linux_android::EINTR;
#[cfg(not(any(target_os = "android", target_os = "linux")))]
use libc::EINTR;

while !buf.is_empty() {
match sys_fill(buf) {
Ok(res) if res > 0 => buf = buf.get_mut(res..).ok_or(Error::UNEXPECTED)?,
Err(err) => {
// We should try again if the call was interrupted.
if err.raw_os_error() != Some(libc::EINTR) {
if err.raw_os_error() != Some(EINTR) {
return Err(err);
}
}
Expand Down

0 comments on commit 14f6df9

Please sign in to comment.