Skip to content

Commit 65660e0

Browse files
josephlrnewpavlov
authored andcommitted
Try getrandom() first on FreeBSD, add sys_fill_exact helper (#57)
1 parent 1705b40 commit 65660e0

File tree

5 files changed

+58
-45
lines changed

5 files changed

+58
-45
lines changed

src/freebsd.rs

+14-11
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@
77
// except according to those terms.
88

99
//! Implementation for FreeBSD
10-
extern crate std;
11-
10+
use crate::util_libc::{sys_fill_exact, Weak};
1211
use crate::Error;
1312
use core::num::NonZeroU32;
14-
use core::ptr;
15-
use std::io;
13+
use core::{mem, ptr};
14+
15+
type GetRandomFn = unsafe extern "C" fn(*mut u8, libc::size_t, libc::c_uint) -> libc::ssize_t;
1616

17-
fn kern_arnd(buf: &mut [u8]) -> Result<usize, Error> {
17+
fn kern_arnd(buf: &mut [u8]) -> libc::ssize_t {
1818
static MIB: [libc::c_int; 2] = [libc::CTL_KERN, libc::KERN_ARND];
1919
let mut len = buf.len();
2020
let ret = unsafe {
@@ -29,17 +29,20 @@ fn kern_arnd(buf: &mut [u8]) -> Result<usize, Error> {
2929
};
3030
if ret == -1 {
3131
error!("freebsd: kern.arandom syscall failed");
32-
return Err(io::Error::last_os_error().into());
32+
-1
33+
} else {
34+
len as libc::ssize_t
3335
}
34-
Ok(len)
3536
}
3637

3738
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
38-
let mut start = 0;
39-
while start < dest.len() {
40-
start += kern_arnd(&mut dest[start..])?;
39+
static GETRANDOM: Weak = unsafe { Weak::new("getrandom\0") };
40+
if let Some(fptr) = GETRANDOM.ptr() {
41+
let func: GetRandomFn = unsafe { mem::transmute(fptr) };
42+
sys_fill_exact(dest, |buf| unsafe { func(buf.as_mut_ptr(), buf.len(), 0) })
43+
} else {
44+
sys_fill_exact(dest, kern_arnd)
4145
}
42-
Ok(())
4346
}
4447

4548
#[inline(always)]

src/lib.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@
1414
//! |------------------|---------------------------------------------------------
1515
//! | Linux, Android | [`getrandom`][1] system call if available, otherwise [`/dev/urandom`][2] after reading from `/dev/random` once
1616
//! | Windows | [`RtlGenRandom`][3]
17-
//! | macOS | [`getentropy()`][19] if available, otherise [`/dev/random`][20] (identical to `/dev/urandom`)
17+
//! | macOS | [`getentropy()`][19] if available, otherwise [`/dev/random`][20] (identical to `/dev/urandom`)
1818
//! | iOS | [`SecRandomCopyBytes`][4]
19-
//! | FreeBSD | [`kern.arandom`][5]
19+
//! | FreeBSD | [`getrandom()`][21] if available, otherwise [`kern.arandom`][5]
2020
//! | OpenBSD | [`getentropy`][6]
2121
//! | NetBSD | [`/dev/urandom`][7] after reading from `/dev/random` once
2222
//! | Dragonfly BSD | [`/dev/random`][8]
@@ -118,6 +118,7 @@
118118
//! [18]: https://software.intel.com/en-us/articles/intel-digital-random-number-generator-drng-software-implementation-guide
119119
//! [19]: https://www.unix.com/man-page/mojave/2/getentropy/
120120
//! [20]: https://www.unix.com/man-page/mojave/4/random/
121+
//! [21]: https://www.freebsd.org/cgi/man.cgi?query=getrandom&manpath=FreeBSD+12.0-stable
121122
122123
#![doc(
123124
html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk.png",

src/linux_android.rs

+10-23
Original file line numberDiff line numberDiff line change
@@ -10,45 +10,32 @@
1010
extern crate std;
1111

1212
use crate::util::LazyBool;
13+
use crate::util_libc::sys_fill_exact;
1314
use crate::{use_file, Error};
1415
use core::num::NonZeroU32;
1516
use std::io;
1617

17-
fn syscall_getrandom(dest: &mut [u8], block: bool) -> Result<usize, io::Error> {
18-
let flags = if block { 0 } else { libc::GRND_NONBLOCK };
19-
let ret = unsafe { libc::syscall(libc::SYS_getrandom, dest.as_mut_ptr(), dest.len(), flags) };
20-
if ret < 0 {
21-
let err = io::Error::last_os_error();
22-
if err.raw_os_error() == Some(libc::EINTR) {
23-
return Ok(0); // Call was interrupted, try again
24-
}
25-
error!("Linux getrandom syscall failed with return value {}", ret);
26-
return Err(err);
27-
}
28-
Ok(ret as usize)
29-
}
30-
3118
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
3219
static HAS_GETRANDOM: LazyBool = LazyBool::new();
3320
if HAS_GETRANDOM.unsync_init(is_getrandom_available) {
34-
let mut start = 0;
35-
while start < dest.len() {
36-
start += syscall_getrandom(&mut dest[start..], true)?;
37-
}
38-
Ok(())
21+
sys_fill_exact(dest, |buf| unsafe {
22+
libc::syscall(libc::SYS_getrandom, buf.as_mut_ptr(), buf.len(), 0) as libc::ssize_t
23+
})
3924
} else {
4025
use_file::getrandom_inner(dest)
4126
}
4227
}
4328

4429
fn is_getrandom_available() -> bool {
45-
match syscall_getrandom(&mut [], false) {
46-
Err(err) => match err.raw_os_error() {
30+
let res = unsafe { libc::syscall(libc::SYS_getrandom, 0, 0, libc::GRND_NONBLOCK) };
31+
if res < 0 {
32+
match io::Error::last_os_error().raw_os_error() {
4733
Some(libc::ENOSYS) => false, // No kernel support
4834
Some(libc::EPERM) => false, // Blocked by seccomp
4935
_ => true,
50-
},
51-
Ok(_) => true,
36+
}
37+
} else {
38+
true
5239
}
5340
}
5441

src/solaris_illumos.rs

+4-9
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,10 @@
1717
//! To make sure we can compile on both Solaris and its derivatives, as well as
1818
//! function, we check for the existance of getrandom(2) in libc by calling
1919
//! libc::dlsym.
20-
extern crate std;
21-
22-
use crate::util_libc::Weak;
20+
use crate::util_libc::{sys_fill_exact, Weak};
2321
use crate::{use_file, Error};
2422
use core::mem;
2523
use core::num::NonZeroU32;
26-
use std::io;
2724

2825
#[cfg(target_os = "illumos")]
2926
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> {
3734
// 256 bytes is the lowest common denominator across all the Solaris
3835
// derived platforms for atomically obtaining random data.
3936
for chunk in dest.chunks_mut(256) {
40-
let ret = unsafe { func(chunk.as_mut_ptr(), chunk.len(), 0) };
41-
if ret != chunk.len() as _ {
42-
error!("getrandom syscall failed with ret={}", ret);
43-
return Err(io::Error::last_os_error().into());
44-
}
37+
sys_fill_exact(chunk, |buf| unsafe {
38+
func(buf.as_mut_ptr(), buf.len(), 0) as libc::ssize_t
39+
})?
4540
}
4641
Ok(())
4742
} else {

src/util_libc.rs

+27
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,36 @@
55
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
66
// option. This file may not be copied, modified, or distributed
77
// except according to those terms.
8+
extern crate std;
89

910
use crate::util::LazyUsize;
11+
use crate::Error;
1012
use core::ptr::NonNull;
13+
use std::io;
14+
15+
// Fill a buffer by repeatedly invoking a system call. The `sys_fill` function:
16+
// - should return -1 and set errno on failure
17+
// - should return the number of bytes written on success
18+
pub fn sys_fill_exact(
19+
mut buf: &mut [u8],
20+
sys_fill: impl Fn(&mut [u8]) -> libc::ssize_t,
21+
) -> Result<(), Error> {
22+
while !buf.is_empty() {
23+
let res = sys_fill(buf);
24+
if res < 0 {
25+
let err = io::Error::last_os_error();
26+
// We should try again if the call was interrupted.
27+
if err.raw_os_error() != Some(libc::EINTR) {
28+
return Err(err.into());
29+
}
30+
} else {
31+
// We don't check for EOF (ret = 0) as the data we are reading
32+
// should be an infinite stream of random bytes.
33+
buf = &mut buf[(res as usize)..];
34+
}
35+
}
36+
Ok(())
37+
}
1138

1239
// A "weak" binding to a C function that may or may not be present at runtime.
1340
// Used for supporting newer OS features while still building on older systems.

0 commit comments

Comments
 (0)