Skip to content

Freebsd: Try getrandom() first #57

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jul 9, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 14 additions & 11 deletions src/freebsd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

type GetRandomFn = unsafe extern "C" fn(*mut u8, libc::size_t, libc::c_uint) -> libc::ssize_t;

fn kern_arnd(buf: &mut [u8]) -> Result<usize, Error> {
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 {
Expand All @@ -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)]
Expand Down
5 changes: 3 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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",
Expand Down
33 changes: 10 additions & 23 deletions src/linux_android.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}

Expand Down
13 changes: 4 additions & 9 deletions src/solaris_illumos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 {
Expand Down
27 changes: 27 additions & 0 deletions src/util_libc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems odd to me to assume that calling last_os_error is appropriate here given the function name. At least this should be mentioned by function documentation.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

During removal of std dependency it will have to be replaced with an explicit errno, so I think it's fine to keep it as-is for the time being.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I changed the function to be named sys_fill_exact and added a comment to explain the expected semantics of the sys_fill function. @newpavlov is right that this will be replaced with an explicit call to an errno function in #54

// 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.
Expand Down