Try getrandom() first on FreeBSD, add sys_fill_exact helper (#57)
This commit is contained in:
committed by
Artyom Pavlov
parent
1705b40655
commit
65660e00be
+14
-11
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user