From 7a15f026f2666a16255904879028acdd5513c766 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Thu, 5 Nov 2020 12:38:09 -0800 Subject: [PATCH] linux: try to use libc getrandom to allow interposition We'll try to use a weak `getrandom` symbol first, because that allows things like `LD_PRELOAD` interposition. For example, perf measurements might want to disable randomness to get reproducible results. If the weak symbol is not found, we fall back to a raw `SYS_getrandom` call. --- library/std/src/sys/unix/rand.rs | 15 ++++++++++++--- library/std/src/sys/unix/weak.rs | 21 +++++++++++++++++++-- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/library/std/src/sys/unix/rand.rs b/library/std/src/sys/unix/rand.rs index eed6fbf13b7..b563011913c 100644 --- a/library/std/src/sys/unix/rand.rs +++ b/library/std/src/sys/unix/rand.rs @@ -25,10 +25,19 @@ mod imp { use crate::io::Read; #[cfg(any(target_os = "linux", target_os = "android"))] - fn getrandom(buf: &mut [u8]) -> libc::c_long { - unsafe { - libc::syscall(libc::SYS_getrandom, buf.as_mut_ptr(), buf.len(), libc::GRND_NONBLOCK) + fn getrandom(buf: &mut [u8]) -> libc::ssize_t { + // A weak symbol allows interposition, e.g. for perf measurements that want to + // disable randomness for consistency. Otherwise, we'll try a raw syscall. + // (`getrandom` was added in glibc 2.25, musl 1.1.20, android API level 28) + weak_syscall! { + fn getrandom( + buffer: *mut libc::c_void, + length: libc::size_t, + flags: libc::c_uint + ) -> libc::ssize_t } + + unsafe { getrandom(buf.as_mut_ptr().cast(), buf.len(), libc::GRND_NONBLOCK) } } #[cfg(not(any(target_os = "linux", target_os = "android")))] diff --git a/library/std/src/sys/unix/weak.rs b/library/std/src/sys/unix/weak.rs index f4b33a00f7c..3aa43858b92 100644 --- a/library/std/src/sys/unix/weak.rs +++ b/library/std/src/sys/unix/weak.rs @@ -66,7 +66,7 @@ unsafe fn fetch(name: &str) -> usize { libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr()) as usize } -#[cfg(not(target_os = "linux"))] +#[cfg(not(any(target_os = "linux", target_os = "android")))] macro_rules! syscall { (fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => ( unsafe fn $name($($arg_name: $t),*) -> $ret { @@ -84,7 +84,7 @@ macro_rules! syscall { ) } -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] macro_rules! syscall { (fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => ( unsafe fn $name($($arg_name:$t),*) -> $ret { @@ -99,3 +99,20 @@ macro_rules! syscall { } ) } + +/// Use a weak symbol from libc when possible, allowing `LD_PRELOAD` interposition, +/// but if it's not found just use a raw syscall. +#[cfg(any(target_os = "linux", target_os = "android"))] +macro_rules! weak_syscall { + (fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => ( + unsafe fn $name($($arg_name:$t),*) -> $ret { + weak! { fn $name($($t),*) -> $ret } + if let Some(fun) = $name.get() { + fun($($arg_name),*) + } else { + syscall! { fn $name($($arg_name:$t),*) -> $ret } + $name($($arg_name),*) + } + } + ) +}