From 32b221ff7a5f62b3473b8a9e7c9e54aa7b12d654 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Mon, 8 Jul 2019 22:11:25 -0700 Subject: [PATCH 1/4] Add fill_exact helper function --- src/freebsd.rs | 17 ++++++----------- src/linux_android.rs | 33 ++++++++++----------------------- src/solaris_illumos.rs | 13 ++++--------- src/util_libc.rs | 21 +++++++++++++++++++++ 4 files changed, 41 insertions(+), 43 deletions(-) diff --git a/src/freebsd.rs b/src/freebsd.rs index 3a8ab609..80c4bc42 100644 --- a/src/freebsd.rs +++ b/src/freebsd.rs @@ -7,14 +7,12 @@ // except according to those terms. //! Implementation for FreeBSD -extern crate std; - +use crate::util_libc::fill_exact; use crate::Error; use core::num::NonZeroU32; use core::ptr; -use std::io; -fn kern_arnd(buf: &mut [u8]) -> Result { +fn kern_arnd(buf: &mut [u8]) -> libc::ssize_t { static MIB: [libc::c_int; 2] = [libc::CTL_KERN, libc::KERN_ARND]; let mut len = buf.len(); let ret = unsafe { @@ -29,17 +27,14 @@ fn kern_arnd(buf: &mut [u8]) -> Result { }; if ret == -1 { error!("freebsd: kern.arandom syscall failed"); - return Err(io::Error::last_os_error().into()); + -1 + } else { + len as libc::ssize_t } - Ok(len) } pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { - let mut start = 0; - while start < dest.len() { - start += kern_arnd(&mut dest[start..])?; - } - Ok(()) + fill_exact(dest, kern_arnd) } #[inline(always)] diff --git a/src/linux_android.rs b/src/linux_android.rs index d7c046da..97235100 100644 --- a/src/linux_android.rs +++ b/src/linux_android.rs @@ -10,45 +10,32 @@ extern crate std; use crate::util::LazyBool; +use crate::util_libc::fill_exact; use crate::{use_file, Error}; use core::num::NonZeroU32; 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> { static HAS_GETRANDOM: LazyBool = LazyBool::new(); if HAS_GETRANDOM.unsync_init(is_getrandom_available) { - let mut start = 0; - while start < dest.len() { - start += syscall_getrandom(&mut dest[start..], true)?; - } - Ok(()) + fill_exact(dest, |buf| unsafe { + libc::syscall(libc::SYS_getrandom, buf.as_mut_ptr(), buf.len(), 0) as libc::ssize_t + }) } else { 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 { libc::syscall(libc::SYS_getrandom, 0, 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 } } diff --git a/src/solaris_illumos.rs b/src/solaris_illumos.rs index 9d629e40..99b9dee4 100644 --- a/src/solaris_illumos.rs +++ b/src/solaris_illumos.rs @@ -17,13 +17,10 @@ //! To make sure we can compile on both Solaris and its derivatives, as well as //! function, we check for the existance of getrandom(2) in libc by calling //! libc::dlsym. -extern crate std; - -use crate::util_libc::Weak; +use crate::util_libc::{fill_exact, Weak}; use crate::{use_file, Error}; use core::mem; use core::num::NonZeroU32; -use std::io; #[cfg(target_os = "illumos")] type GetRandomFn = unsafe extern "C" fn(*mut u8, libc::size_t, libc::c_uint) -> libc::ssize_t; @@ -37,11 +34,9 @@ pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { // 256 bytes is the lowest common denominator across all the Solaris // derived platforms for atomically obtaining random data. for chunk in dest.chunks_mut(256) { - let ret = unsafe { func(chunk.as_mut_ptr(), chunk.len(), 0) }; - if ret != chunk.len() as _ { - error!("getrandom syscall failed with ret={}", ret); - return Err(io::Error::last_os_error().into()); - } + fill_exact(chunk, |buf| unsafe { + func(buf.as_mut_ptr(), buf.len(), 0) as libc::ssize_t + })? } Ok(()) } else { diff --git a/src/util_libc.rs b/src/util_libc.rs index 5554079b..48822cbc 100644 --- a/src/util_libc.rs +++ b/src/util_libc.rs @@ -5,9 +5,30 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. +extern crate std; use crate::util::LazyUsize; +use crate::Error; use core::ptr::NonNull; +use std::io; + +pub fn fill_exact(mut buf: &mut [u8], f: impl Fn(&mut [u8]) -> libc::ssize_t) -> Result<(), Error> { + while !buf.is_empty() { + let res = f(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(()) +} // A "weak" binding to a C function that may or may not be present at runtime. // Used for supporting newer OS features while still building on older systems. From 813b1171bbf37beeca32870367bfd618209b1955 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Sat, 6 Jul 2019 23:53:04 -0700 Subject: [PATCH 2/4] freebsd: Try getrandom() first --- src/freebsd.rs | 14 +++++++++++--- src/lib.rs | 3 ++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/freebsd.rs b/src/freebsd.rs index 80c4bc42..9bfb0c7f 100644 --- a/src/freebsd.rs +++ b/src/freebsd.rs @@ -7,10 +7,12 @@ // except according to those terms. //! Implementation for FreeBSD -use crate::util_libc::fill_exact; +use crate::util_libc::{fill_exact, Weak}; use crate::Error; use core::num::NonZeroU32; -use core::ptr; +use core::{mem, ptr}; + +type GetRandomFn = unsafe extern "C" fn(*mut u8, libc::size_t, libc::c_uint) -> libc::ssize_t; fn kern_arnd(buf: &mut [u8]) -> libc::ssize_t { static MIB: [libc::c_int; 2] = [libc::CTL_KERN, libc::KERN_ARND]; @@ -34,7 +36,13 @@ fn kern_arnd(buf: &mut [u8]) -> libc::ssize_t { } pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { - fill_exact(dest, kern_arnd) + static GETRANDOM: Weak = unsafe { Weak::new("getrandom\0") }; + if let Some(fptr) = GETRANDOM.ptr() { + let func: GetRandomFn = unsafe { mem::transmute(fptr) }; + fill_exact(dest, |buf| unsafe { func(buf.as_mut_ptr(), buf.len(), 0) }) + } else { + fill_exact(dest, kern_arnd) + } } #[inline(always)] diff --git a/src/lib.rs b/src/lib.rs index 4c7dc5c4..dc3f3105 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,7 +16,7 @@ //! | Windows | [`RtlGenRandom`][3] //! | macOS | [`getentropy()`][19] if available, otherise [`/dev/random`][20] (identical to `/dev/urandom`) //! | iOS | [`SecRandomCopyBytes`][4] -//! | FreeBSD | [`kern.arandom`][5] +//! | FreeBSD | [`getrandom()`][21] if available, otherise [`kern.arandom`][5] //! | OpenBSD | [`getentropy`][6] //! | NetBSD | [`/dev/urandom`][7] after reading from `/dev/random` once //! | Dragonfly BSD | [`/dev/random`][8] @@ -118,6 +118,7 @@ //! [18]: https://software.intel.com/en-us/articles/intel-digital-random-number-generator-drng-software-implementation-guide //! [19]: https://www.unix.com/man-page/mojave/2/getentropy/ //! [20]: https://www.unix.com/man-page/mojave/4/random/ +//! [21]: https://www.freebsd.org/cgi/man.cgi?query=getrandom&manpath=FreeBSD+12.0-stable #![doc( html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk.png", From 0adc101ef8d6b960f52d891245896ba77b7dd66b Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Tue, 9 Jul 2019 12:07:38 -0700 Subject: [PATCH 3/4] Rename to sys_fill_exact and add docs --- src/freebsd.rs | 6 +++--- src/linux_android.rs | 4 ++-- src/solaris_illumos.rs | 4 ++-- src/util_libc.rs | 10 ++++++++-- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/freebsd.rs b/src/freebsd.rs index 9bfb0c7f..aa77b221 100644 --- a/src/freebsd.rs +++ b/src/freebsd.rs @@ -7,7 +7,7 @@ // except according to those terms. //! Implementation for FreeBSD -use crate::util_libc::{fill_exact, Weak}; +use crate::util_libc::{sys_fill_exact, Weak}; use crate::Error; use core::num::NonZeroU32; use core::{mem, ptr}; @@ -39,9 +39,9 @@ pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { static GETRANDOM: Weak = unsafe { Weak::new("getrandom\0") }; if let Some(fptr) = GETRANDOM.ptr() { let func: GetRandomFn = unsafe { mem::transmute(fptr) }; - fill_exact(dest, |buf| unsafe { func(buf.as_mut_ptr(), buf.len(), 0) }) + sys_fill_exact(dest, |buf| unsafe { func(buf.as_mut_ptr(), buf.len(), 0) }) } else { - fill_exact(dest, kern_arnd) + sys_fill_exact(dest, kern_arnd) } } diff --git a/src/linux_android.rs b/src/linux_android.rs index 97235100..8aa1d58d 100644 --- a/src/linux_android.rs +++ b/src/linux_android.rs @@ -10,7 +10,7 @@ extern crate std; use crate::util::LazyBool; -use crate::util_libc::fill_exact; +use crate::util_libc::sys_fill_exact; use crate::{use_file, Error}; use core::num::NonZeroU32; use std::io; @@ -18,7 +18,7 @@ use std::io; pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { static HAS_GETRANDOM: LazyBool = LazyBool::new(); if HAS_GETRANDOM.unsync_init(is_getrandom_available) { - fill_exact(dest, |buf| unsafe { + sys_fill_exact(dest, |buf| unsafe { libc::syscall(libc::SYS_getrandom, buf.as_mut_ptr(), buf.len(), 0) as libc::ssize_t }) } else { diff --git a/src/solaris_illumos.rs b/src/solaris_illumos.rs index 99b9dee4..8539971b 100644 --- a/src/solaris_illumos.rs +++ b/src/solaris_illumos.rs @@ -17,7 +17,7 @@ //! To make sure we can compile on both Solaris and its derivatives, as well as //! function, we check for the existance of getrandom(2) in libc by calling //! libc::dlsym. -use crate::util_libc::{fill_exact, Weak}; +use crate::util_libc::{sys_fill_exact, Weak}; use crate::{use_file, Error}; use core::mem; use core::num::NonZeroU32; @@ -34,7 +34,7 @@ pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { // 256 bytes is the lowest common denominator across all the Solaris // derived platforms for atomically obtaining random data. for chunk in dest.chunks_mut(256) { - fill_exact(chunk, |buf| unsafe { + sys_fill_exact(chunk, |buf| unsafe { func(buf.as_mut_ptr(), buf.len(), 0) as libc::ssize_t })? } diff --git a/src/util_libc.rs b/src/util_libc.rs index 48822cbc..8804fdc6 100644 --- a/src/util_libc.rs +++ b/src/util_libc.rs @@ -12,9 +12,15 @@ use crate::Error; use core::ptr::NonNull; use std::io; -pub fn fill_exact(mut buf: &mut [u8], f: impl Fn(&mut [u8]) -> libc::ssize_t) -> Result<(), Error> { +// 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 +pub 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 = f(buf); + let res = sys_fill(buf); if res < 0 { let err = io::Error::last_os_error(); // We should try again if the call was interrupted. From dcc8902f8d9a6ba5ad21c123537bce2a391239db Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Tue, 9 Jul 2019 12:08:37 -0700 Subject: [PATCH 4/4] Fix typos --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index dc3f3105..f3c06574 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,9 +14,9 @@ //! |------------------|--------------------------------------------------------- //! | Linux, Android | [`getrandom`][1] system call if available, otherwise [`/dev/urandom`][2] after reading from `/dev/random` once //! | Windows | [`RtlGenRandom`][3] -//! | macOS | [`getentropy()`][19] if available, otherise [`/dev/random`][20] (identical to `/dev/urandom`) +//! | macOS | [`getentropy()`][19] if available, otherwise [`/dev/random`][20] (identical to `/dev/urandom`) //! | iOS | [`SecRandomCopyBytes`][4] -//! | FreeBSD | [`getrandom()`][21] if available, otherise [`kern.arandom`][5] +//! | FreeBSD | [`getrandom()`][21] if available, otherwise [`kern.arandom`][5] //! | OpenBSD | [`getentropy`][6] //! | NetBSD | [`/dev/urandom`][7] after reading from `/dev/random` once //! | Dragonfly BSD | [`/dev/random`][8]