From 14f6df98094cea8f7671b67ea7bb1184fcfd2101 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Thu, 6 Jun 2024 09:00:45 -0700 Subject: [PATCH] linux_android: Use direct syscall instead of libc to support *-none. 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. --- .clippy.toml | 2 +- .github/workflows/tests.yml | 6 ++- Cargo.toml | 8 +++- README.md | 2 +- src/lib.rs | 9 ++-- src/linux_android.rs | 74 +++++++++++++++++++++++++----- src/linux_android_with_fallback.rs | 4 ++ src/util_unix.rs | 8 +++- 8 files changed, 92 insertions(+), 21 deletions(-) diff --git a/.clippy.toml b/.clippy.toml index 5cccb362..abe19b3a 100644 --- a/.clippy.toml +++ b/.clippy.toml @@ -1 +1 @@ -msrv = "1.57" +msrv = "1.59" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f3831d5c..54af9750 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -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. @@ -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' }} @@ -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 diff --git a/Cargo.toml b/Cargo.toml index b9357bce..946c7f35 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" @@ -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] diff --git a/README.md b/README.md index 56af89dd..608d5dc8 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/src/lib.rs b/src/lib.rs index c1f36377..1b01fc5a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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 @@ -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; diff --git a/src/linux_android.rs b/src/linux_android.rs index 151f7c5c..415594e7 100644 --- a/src/linux_android.rs +++ b/src/linux_android.rs @@ -6,18 +6,68 @@ pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> 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]) -> Result { - 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]) -> Result { + const _:() = assert!(core::mem::size_of::() == core::mem::size_of::()); + + 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::() <= core::mem::size_of::()); + 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::(), - buf.len(), - 0, - ) - }; - const _: () = assert!(core::mem::size_of::() == core::mem::size_of::()); - usize::try_from(ret as isize).map_err(|_| last_os_error()) + pub fn getrandom_syscall(buf: &mut [MaybeUninit]) -> Result { + let ret: libc::c_long = unsafe { + libc::syscall( + libc::SYS_getrandom, + buf.as_mut_ptr().cast::(), + buf.len(), + 0, + ) + }; + const _:() = assert!(core::mem::size_of::() == core::mem::size_of::()); + usize::try_from(ret as isize).map_err(|_| last_os_error()) + } + } } diff --git a/src/linux_android_with_fallback.rs b/src/linux_android_with_fallback.rs index 3a7d1bad..3bfeef18 100644 --- a/src/linux_android_with_fallback.rs +++ b/src/linux_android_with_fallback.rs @@ -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]) -> Result<(), Error> { // getrandom(2) was introduced in Linux 3.17 static HAS_GETRANDOM: LazyBool = LazyBool::new(); diff --git a/src/util_unix.rs b/src/util_unix.rs index 67513507..2b81f921 100644 --- a/src/util_unix.rs +++ b/src/util_unix.rs @@ -9,12 +9,18 @@ pub fn sys_fill_exact( mut buf: &mut [MaybeUninit], sys_fill: impl Fn(&mut [MaybeUninit]) -> Result, ) -> 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); } }