diff --git a/src/linux_android.rs b/src/linux_android.rs index 8ab69eaac..9aba795fc 100644 --- a/src/linux_android.rs +++ b/src/linux_android.rs @@ -14,44 +14,30 @@ use core::num::NonZeroU32; use lazy_static::lazy_static; use std::io; -fn syscall_getrandom(dest: &mut [u8], block: bool) -> Result { - let flags = if block { 0 } else { libc::GRND_NONBLOCK }; - let ret = unsafe { libc::syscall(libc::SYS_getrandom, dest.as_mut_ptr(), dest.len(), flags) }; - if ret < 0 { - let err = io::Error::last_os_error(); - if err.raw_os_error() == Some(libc::EINTR) { - return Ok(0); // Call was interrupted, try again - } - error!("Linux getrandom syscall failed with return value {}", ret); - return Err(err); - } - Ok(ret as usize) -} - pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { lazy_static! { static ref HAS_GETRANDOM: bool = is_getrandom_available(); } match *HAS_GETRANDOM { true => { - let mut start = 0; - while start < dest.len() { - start += syscall_getrandom(&mut dest[start..], true)?; - } - Ok(()) - } + sys_fill_exact(dest, |buf| unsafe { + getrandom(buf.as_mut_ptr() as *mut libc::c_void, buf.len(), 0) + }) + }, false => use_file::getrandom_inner(dest), } } fn is_getrandom_available() -> bool { - match syscall_getrandom(&mut [], false) { - Err(err) => match err.raw_os_error() { + let res = unsafe { getrandom(core::ptr::null_mut(), 0, libc::GRND_NONBLOCK) }; + if res < 0 { + match io::Error::last_os_error().raw_os_error() { Some(libc::ENOSYS) => false, // No kernel support Some(libc::EPERM) => false, // Blocked by seccomp _ => true, - }, - Ok(_) => true, + } + } else { + true } } @@ -59,3 +45,37 @@ fn is_getrandom_available() -> bool { pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> { None } + +// Fill a buffer by repeatedly invoking a system call. The `sys_fill` function: +// - should return -1 and set errno on failure +// - should return the number of bytes written on success +// +// From src/util_libc.rs 65660e00 +fn sys_fill_exact( + mut buf: &mut [u8], + sys_fill: impl Fn(&mut [u8]) -> libc::ssize_t, +) -> Result<(), Error> { + while !buf.is_empty() { + let res = sys_fill(buf); + if res < 0 { + let err = io::Error::last_os_error(); + // We should try again if the call was interrupted. + if err.raw_os_error() != Some(libc::EINTR) { + return Err(err.into()); + } + } else { + // We don't check for EOF (ret = 0) as the data we are reading + // should be an infinite stream of random bytes. + buf = &mut buf[(res as usize)..]; + } + } + Ok(()) +} + +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 +}