Files
rust/src/libstd/sys/unix/rand.rs

211 lines
6.8 KiB
Rust
Raw Normal View History

// Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use mem;
use slice;
pub fn hashmap_random_keys() -> (u64, u64) {
let mut v = (0, 0);
unsafe {
let view = slice::from_raw_parts_mut(&mut v as *mut _ as *mut u8,
mem::size_of_val(&v));
imp::fill_bytes(view);
}
return v
}
#[cfg(all(unix,
not(target_os = "ios"),
not(target_os = "openbsd"),
not(target_os = "freebsd"),
not(target_os = "fuchsia")))]
mod imp {
use fs::File;
use io::Read;
use libc;
use sync::atomic::{AtomicBool, AtomicI32, Ordering};
use sys::os::errno;
static GETRANDOM_URANDOM_FD: AtomicI32 = AtomicI32::new(-1);
#[cfg(any(target_os = "linux", target_os = "android"))]
static GETRANDOM_UNAVAILABLE: AtomicBool = AtomicBool::new(false);
#[cfg(any(target_os = "linux", target_os = "android"))]
fn is_getrandom_permanently_unavailable() -> bool {
GETRANDOM_UNAVAILABLE.load(Ordering::Relaxed)
}
#[cfg(not(any(target_os = "linux", target_os = "android")))]
fn is_getrandom_permanently_unavailable() -> bool {
true
}
#[cfg(any(target_os = "linux", target_os = "android"))]
fn getrandom(buf: &mut [u8]) -> libc::c_long {
unsafe {
libc::syscall(libc::SYS_getrandom, buf.as_mut_ptr(), buf.len(), libc::GRND_NONBLOCK)
}
}
#[cfg(not(any(target_os = "linux", target_os = "android")))]
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 {
if is_getrandom_permanently_unavailable() {
return false;
}
let mut read = 0;
while read < v.len() {
2015-01-17 16:15:52 -08:00
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);
} else if err == libc::EAGAIN {
return false;
} else {
panic!("unexpected getrandom error: {}", err);
}
} else {
read += result as usize;
}
}
true
}
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
// 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 over a
// possibly predictable entropy pool.
if getrandom_fill_bytes(v) {
return;
}
// getrandom failed for some reason. If the getrandom call is
// permanently unavailable (OS without getrandom, or OS version without
// getrandom), we'll keep around the fd for /dev/urandom for future
// requests, to avoid re-opening the file on every call.
//
// Otherwise, open /dev/urandom, read from it, and close it again.
use super::super::ext::io::{FromRawFd, IntoRawFd};
let mut fd = GETRANDOM_URANDOM_FD.load(Ordering::Relaxed);
let mut close_fd = false;
if fd == -1 {
if !is_getrandom_permanently_unavailable() {
close_fd = true;
}
let file = File::open("/dev/urandom").expect("failed to open /dev/urandom");
fd = file.into_raw_fd();
// If some other thread also opened /dev/urandom and set the global
// fd already, we close our fd at the end of the function.
if !close_fd && GETRANDOM_URANDOM_FD.compare_and_swap(-1, fd, Ordering::Relaxed) != -1 {
close_fd = true;
}
}
let mut file = unsafe { File::from_raw_fd(fd) };
let res = file.read_exact(v);
if !close_fd {
let _ = file.into_raw_fd();
}
res.expect("failed to read /dev/urandom");
}
}
#[cfg(target_os = "openbsd")]
mod imp {
use libc;
use sys::os::errno;
pub fn fill_bytes(v: &mut [u8]) {
// getentropy(2) permits a maximum buffer size of 256 bytes
for s in v.chunks_mut(256) {
let ret = unsafe {
libc::getentropy(s.as_mut_ptr() as *mut libc::c_void, s.len())
};
if ret == -1 {
panic!("unexpected getentropy error: {}", errno());
}
}
}
}
2014-05-05 10:07:49 +03:00
#[cfg(target_os = "ios")]
mod imp {
2015-04-01 20:38:58 +03:00
use io;
2015-08-11 14:52:22 -05:00
use libc::{c_int, size_t};
use ptr;
2014-05-05 10:07:49 +03:00
2015-08-05 17:50:18 -04:00
enum SecRandom {}
2014-05-05 10:07:49 +03:00
2014-10-27 15:37:07 -07:00
#[allow(non_upper_case_globals)]
const kSecRandomDefault: *const SecRandom = ptr::null();
2014-05-05 10:07:49 +03:00
extern {
2014-06-25 12:47:34 -07:00
fn SecRandomCopyBytes(rnd: *const SecRandom,
count: size_t,
bytes: *mut u8) -> c_int;
2014-05-05 10:07:49 +03:00
}
pub fn fill_bytes(v: &mut [u8]) {
let ret = unsafe {
SecRandomCopyBytes(kSecRandomDefault,
v.len(),
v.as_mut_ptr())
};
if ret == -1 {
panic!("couldn't generate random bytes: {}",
io::Error::last_os_error());
2014-05-05 10:07:49 +03:00
}
}
}
#[cfg(target_os = "freebsd")]
mod imp {
use libc;
use ptr;
pub fn fill_bytes(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);
}
}
}
}
#[cfg(target_os = "fuchsia")]
mod imp {
#[link(name = "zircon")]
extern {
fn zx_cprng_draw(buffer: *mut u8, len: usize);
}
pub fn fill_bytes(v: &mut [u8]) {
unsafe { zx_cprng_draw(v.as_mut_ptr(), v.len()) }
}
}