Skip to content

Commit

Permalink
Auto merge of #53725 - tbu-:pr_getrandom_syscalls, r=alexcrichton
Browse files Browse the repository at this point in the history
Reduce number of syscalls in `rand`

This skips the initial zero-length `getrandom` call and
directly hands the user buffer to the operating system, saving one
`getrandom` syscall.
  • Loading branch information
bors committed Sep 2, 2018
2 parents 763d91a + b95c491 commit 9395f0a
Showing 1 changed file with 26 additions and 40 deletions.
66 changes: 26 additions & 40 deletions src/libstd/sys/unix/rand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ pub fn hashmap_random_keys() -> (u64, u64) {
mod imp {
use fs::File;
use io::Read;
#[cfg(any(target_os = "linux", target_os = "android"))]
use libc;
use sys::os::errno;

#[cfg(any(target_os = "linux", target_os = "android"))]
fn getrandom(buf: &mut [u8]) -> libc::c_long {
Expand All @@ -40,71 +40,57 @@ mod imp {
}

#[cfg(not(any(target_os = "linux", target_os = "android")))]
fn getrandom(_buf: &mut [u8]) -> libc::c_long { -1 }
fn getrandom_fill_bytes(_buf: &mut [u8]) -> bool { false }

#[cfg(any(target_os = "linux", target_os = "android"))]
fn getrandom_fill_bytes(v: &mut [u8]) -> bool {
use sync::atomic::{AtomicBool, Ordering};
use sys::os::errno;

static GETRANDOM_UNAVAILABLE: AtomicBool = AtomicBool::new(false);
if GETRANDOM_UNAVAILABLE.load(Ordering::Relaxed) {
return false;
}

let mut read = 0;
while read < v.len() {
let result = getrandom(&mut v[read..]);
if result == -1 {
let err = errno() as libc::c_int;
if err == libc::EINTR {
continue;
} else if err == libc::ENOSYS {
GETRANDOM_UNAVAILABLE.store(true, Ordering::Relaxed);
return false;
} else if err == libc::EAGAIN {
return false
return false;
} else {
panic!("unexpected getrandom error: {}", err);
}
} else {
read += result as usize;
}
}

return true
true
}

#[cfg(any(target_os = "linux", target_os = "android"))]
fn is_getrandom_available() -> bool {
use io;
use sync::atomic::{AtomicBool, Ordering};
use sync::Once;

static CHECKER: Once = Once::new();
static AVAILABLE: AtomicBool = AtomicBool::new(false);

CHECKER.call_once(|| {
let mut buf: [u8; 0] = [];
let result = getrandom(&mut buf);
let available = if result == -1 {
let err = io::Error::last_os_error().raw_os_error();
err != Some(libc::ENOSYS)
} else {
true
};
AVAILABLE.store(available, Ordering::Relaxed);
});

AVAILABLE.load(Ordering::Relaxed)
}

#[cfg(not(any(target_os = "linux", target_os = "android")))]
fn is_getrandom_available() -> bool { false }

pub fn fill_bytes(v: &mut [u8]) {
// getrandom_fill_bytes here can fail if getrandom() returns EAGAIN,
// meaning it would have blocked because the non-blocking pool (urandom)
// has not initialized in the kernel yet due to a lack of entropy the
// has not initialized in the kernel yet due to a lack of entropy. The
// fallback we do here is to avoid blocking applications which could
// depend on this call without ever knowing they do and don't have a
// work around. The PRNG of /dev/urandom will still be used but not
// over a completely full entropy pool
if is_getrandom_available() && getrandom_fill_bytes(v) {
return
// work around. The PRNG of /dev/urandom will still be used but over a
// possibly predictable entropy pool.
if getrandom_fill_bytes(v) {
return;
}

let mut file = File::open("/dev/urandom")
.expect("failed to open /dev/urandom");
file.read_exact(v).expect("failed to read /dev/urandom");
// getrandom failed because it is permanently or temporarily (because
// of missing entropy) unavailable. Open /dev/urandom, read from it,
// and close it again.
let mut file = File::open("/dev/urandom").expect("failed to open /dev/urandom");
file.read_exact(v).expect("failed to read /dev/urandom")
}
}

Expand Down

0 comments on commit 9395f0a

Please sign in to comment.