Try getrandom() first on FreeBSD, add sys_fill_exact helper (#57)

This commit is contained in:
Joseph Richey
2019-07-09 12:26:49 -07:00
committed by Artyom Pavlov
parent 1705b40655
commit 65660e00be
5 changed files with 58 additions and 45 deletions
+14 -11
View File
@@ -7,14 +7,14 @@
// except according to those terms.
//! Implementation for FreeBSD
extern crate std;
use crate::util_libc::{sys_fill_exact, Weak};
use crate::Error;
use core::num::NonZeroU32;
use core::ptr;
use std::io;
use core::{mem, ptr};
fn kern_arnd(buf: &mut [u8]) -> Result<usize, Error> {
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];
let mut len = buf.len();
let ret = unsafe {
@@ -29,17 +29,20 @@ fn kern_arnd(buf: &mut [u8]) -> Result<usize, Error> {
};
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..])?;
static GETRANDOM: Weak = unsafe { Weak::new("getrandom\0") };
if let Some(fptr) = GETRANDOM.ptr() {
let func: GetRandomFn = unsafe { mem::transmute(fptr) };
sys_fill_exact(dest, |buf| unsafe { func(buf.as_mut_ptr(), buf.len(), 0) })
} else {
sys_fill_exact(dest, kern_arnd)
}
Ok(())
}
#[inline(always)]
+3 -2
View File
@@ -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 | [`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]
@@ -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",
+10 -23
View File
@@ -10,45 +10,32 @@
extern crate std;
use crate::util::LazyBool;
use crate::util_libc::sys_fill_exact;
use crate::{use_file, Error};
use core::num::NonZeroU32;
use std::io;
fn syscall_getrandom(dest: &mut [u8], block: bool) -> Result<usize, io::Error> {
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(())
sys_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
}
}
+4 -9
View File
@@ -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::{sys_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());
}
sys_fill_exact(chunk, |buf| unsafe {
func(buf.as_mut_ptr(), buf.len(), 0) as libc::ssize_t
})?
}
Ok(())
} else {
+27
View File
@@ -5,9 +5,36 @@
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, 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;
// 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 = 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(())
}
// 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.