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.
  • Loading branch information
briansmith committed Jun 6, 2024
1 parent 0122c66 commit a9719e4
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 16 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,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
6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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, but we can't express this. In that case, we require libc to be
# available, 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
5 changes: 3 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ cfg_if! {
// are used in practice to target pre-3.17 kernels.
target_env = "musl",
),
not(target_env = "")
)
),
))] {
Expand All @@ -304,8 +305,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 as usize..).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 a9719e4

Please sign in to comment.