Skip to content

Commit

Permalink
Auto merge of #35884 - habnabit:freebsd-arc4rand, r=alexcrichton
Browse files Browse the repository at this point in the history
Use arc4rand(9) on FreeBSD

From rust-random/rand#112:

>After reading through #30691 it seems that there's general agreement that using OS-provided facilities for seeding rust userland processes is fine as long as it doesn't use too much from libc. FreeBSD's `arc4random_buf(3)` is not only a whole lot of libc code, but also not even currently exposed in the libc crate. Fortunately, the mechanism `arc4random_buf(3)` et al. use for getting entropy from the kernel ([`arc4rand(9)`](https://www.freebsd.org/cgi/man.cgi?query=arc4random&apropos=0&sektion=9&manpath=FreeBSD+10.3-RELEASE&arch=default&format=html)) is exposed via `sysctl(3)` with constants that are already in the libc crate.

>I haven't found too much documentation on `KERN_ARND`—it's missing or only briefly described in most of the places that cover sysctl mibs. But, from digging through the kernel source, it appears that the sysctl used in this PR is very close to just calling `arc4rand(9)` directly (with `reseed` set to 0 and no way to change it).

I expected [rand](/rust-lang-nursery/rand) to reply quicker, so I tried submitting it there first. It's been a few weeks with no comment, so I don't know the state of it, but maybe someone will see it here and have an opinion. This is basically the same patch. It pains me to duplicate the code but I guess it hasn't been factored out into just one place yet.
  • Loading branch information
bors committed Aug 25, 2016
2 parents 17a2be8 + 0a70944 commit 528c6f3
Showing 1 changed file with 77 additions and 30 deletions.
107 changes: 77 additions & 30 deletions src/libstd/sys/unix/rand.rs
Expand Up @@ -10,14 +10,31 @@

pub use self::imp::OsRng;

#[cfg(all(unix, not(target_os = "ios"), not(target_os = "openbsd")))]
use mem;

fn next_u32(mut fill_buf: &mut FnMut(&mut [u8])) -> u32 {
let mut buf: [u8; 4] = [0; 4];
fill_buf(&mut buf);
unsafe { mem::transmute::<[u8; 4], u32>(buf) }
}

fn next_u64(mut fill_buf: &mut FnMut(&mut [u8])) -> u64 {
let mut buf: [u8; 8] = [0; 8];
fill_buf(&mut buf);
unsafe { mem::transmute::<[u8; 8], u64>(buf) }
}

#[cfg(all(unix,
not(target_os = "ios"),
not(target_os = "openbsd"),
not(target_os = "freebsd")))]
mod imp {
use self::OsRngInner::*;
use super::{next_u32, next_u64};

use fs::File;
use io;
use libc;
use mem;
use rand::Rng;
use rand::reader::ReaderRng;
use sys::os::errno;
Expand Down Expand Up @@ -87,18 +104,6 @@ mod imp {
}
}

fn getrandom_next_u32() -> u32 {
let mut buf: [u8; 4] = [0; 4];
getrandom_fill_bytes(&mut buf);
unsafe { mem::transmute::<[u8; 4], u32>(buf) }
}

fn getrandom_next_u64() -> u64 {
let mut buf: [u8; 8] = [0; 8];
getrandom_fill_bytes(&mut buf);
unsafe { mem::transmute::<[u8; 8], u64>(buf) }
}

#[cfg(all(target_os = "linux",
any(target_arch = "x86_64",
target_arch = "x86",
Expand Down Expand Up @@ -163,13 +168,13 @@ mod imp {
impl Rng for OsRng {
fn next_u32(&mut self) -> u32 {
match self.inner {
OsGetrandomRng => getrandom_next_u32(),
OsGetrandomRng => next_u32(&mut getrandom_fill_bytes),
OsReaderRng(ref mut rng) => rng.next_u32(),
}
}
fn next_u64(&mut self) -> u64 {
match self.inner {
OsGetrandomRng => getrandom_next_u64(),
OsGetrandomRng => next_u64(&mut getrandom_fill_bytes),
OsReaderRng(ref mut rng) => rng.next_u64(),
}
}
Expand All @@ -184,9 +189,10 @@ mod imp {

#[cfg(target_os = "openbsd")]
mod imp {
use super::{next_u32, next_u64};

use io;
use libc;
use mem;
use sys::os::errno;
use rand::Rng;

Expand All @@ -205,14 +211,10 @@ mod imp {

impl Rng for OsRng {
fn next_u32(&mut self) -> u32 {
let mut v = [0; 4];
self.fill_bytes(&mut v);
unsafe { mem::transmute(v) }
next_u32(&mut |v| self.fill_bytes(v))
}
fn next_u64(&mut self) -> u64 {
let mut v = [0; 8];
self.fill_bytes(&mut v);
unsafe { mem::transmute(v) }
next_u64(&mut |v| self.fill_bytes(v))
}
fn fill_bytes(&mut self, v: &mut [u8]) {
// getentropy(2) permits a maximum buffer size of 256 bytes
Expand All @@ -230,8 +232,9 @@ mod imp {

#[cfg(target_os = "ios")]
mod imp {
use super::{next_u32, next_u64};

use io;
use mem;
use ptr;
use rand::Rng;
use libc::{c_int, size_t};
Expand Down Expand Up @@ -265,14 +268,10 @@ mod imp {

impl Rng for OsRng {
fn next_u32(&mut self) -> u32 {
let mut v = [0; 4];
self.fill_bytes(&mut v);
unsafe { mem::transmute(v) }
next_u32(&mut |v| self.fill_bytes(v))
}
fn next_u64(&mut self) -> u64 {
let mut v = [0; 8];
self.fill_bytes(&mut v);
unsafe { mem::transmute(v) }
next_u64(&mut |v| self.fill_bytes(v))
}
fn fill_bytes(&mut self, v: &mut [u8]) {
let ret = unsafe {
Expand All @@ -286,3 +285,51 @@ mod imp {
}
}
}

#[cfg(target_os = "freebsd")]
mod imp {
use super::{next_u32, next_u64};

use io;
use libc;
use rand::Rng;
use ptr;

pub struct OsRng {
// dummy field to ensure that this struct cannot be constructed outside
// of this module
_dummy: (),
}

impl OsRng {
/// Create a new `OsRng`.
pub fn new() -> io::Result<OsRng> {
Ok(OsRng { _dummy: () })
}
}

impl Rng for OsRng {
fn next_u32(&mut self) -> u32 {
next_u32(&mut |v| self.fill_bytes(v))
}
fn next_u64(&mut self) -> u64 {
next_u64(&mut |v| self.fill_bytes(v))
}
fn fill_bytes(&mut self, v: &mut [u8]) {
let mib = [libc::CTL_KERN, libc::KERN_ARND];
// kern.arandom permits a maximum buffer size of 256 bytes
for s in v.chunks_mut(256) {
let mut s_len = s.len();
let ret = unsafe {
libc::sysctl(mib.as_ptr(), mib.len() as libc::c_uint,
s.as_mut_ptr() as *mut _, &mut s_len,
ptr::null(), 0)
};
if ret == -1 || s_len != s.len() {
panic!("kern.arandom sysctl failed! (returned {}, s.len() {}, oldlenp {})",
ret, s.len(), s_len);
}
}
}
}
}

0 comments on commit 528c6f3

Please sign in to comment.