diff options
| author | Alex Crichton <alex@alexcrichton.com> | 2014-11-06 13:31:58 -0800 |
|---|---|---|
| committer | Alex Crichton <alex@alexcrichton.com> | 2014-11-06 13:31:58 -0800 |
| commit | 6783afcc17e81e580b82cc4f4872af3abb0a3662 (patch) | |
| tree | 5c3bab4e311fcde875f051e93a1b19adb30b412d /src/libstd | |
| parent | fa530fff51b3e5ff74969c320dfef44c16d3bc22 (diff) | |
| parent | fdf5195f58971337ad46a9996613660279966120 (diff) | |
| download | rust-6783afcc17e81e580b82cc4f4872af3abb0a3662.tar.gz rust-6783afcc17e81e580b82cc4f4872af3abb0a3662.zip | |
rollup merge of #18664 : klutzy/getrandom
Diffstat (limited to 'src/libstd')
| -rw-r--r-- | src/libstd/rand/mod.rs | 8 | ||||
| -rw-r--r-- | src/libstd/rand/os.rs | 124 |
2 files changed, 120 insertions, 12 deletions
diff --git a/src/libstd/rand/mod.rs b/src/libstd/rand/mod.rs index 21e531d211a..5ef2c2fe23d 100644 --- a/src/libstd/rand/mod.rs +++ b/src/libstd/rand/mod.rs @@ -45,8 +45,12 @@ //! so the "quality" of `/dev/random` is not better than `/dev/urandom` in most cases. //! However, this means that `/dev/urandom` can yield somewhat predictable randomness //! if the entropy pool is very small, such as immediately after first booting. -//! If an application likely to be run soon after first booting, or on a system with very -//! few entropy sources, one should consider using `/dev/random` via `ReaderRng`. +//! Linux 3,17 added `getrandom(2)` system call which solves the issue: it blocks if entropy +//! pool is not initialized yet, but it does not block once initialized. +//! `OsRng` tries to use `getrandom(2)` if available, and use `/dev/urandom` fallback if not. +//! If an application does not have `getrandom` and likely to be run soon after first booting, +//! or on a system with very few entropy sources, one should consider using `/dev/random` via +//! `ReaderRng`. //! - On some systems (e.g. FreeBSD, OpenBSD and Mac OS X) there is no difference //! between the two sources. (Also note that, on some systems e.g. FreeBSD, both `/dev/random` //! and `/dev/urandom` may block once if the CSPRNG has not seeded yet.) diff --git a/src/libstd/rand/os.rs b/src/libstd/rand/os.rs index b6b66e593a2..e648a618516 100644 --- a/src/libstd/rand/os.rs +++ b/src/libstd/rand/os.rs @@ -15,45 +15,149 @@ pub use self::imp::OsRng; #[cfg(all(unix, not(target_os = "ios")))] mod imp { + extern crate libc; + use io::{IoResult, File}; use path::Path; use rand::Rng; use rand::reader::ReaderRng; use result::{Ok, Err}; + use slice::{ImmutableSlice, MutableSlice}; + use mem; + use os::errno; + + #[cfg(all(target_os = "linux", + any(target_arch = "x86_64", target_arch = "x86", target_arch = "arm")))] + fn getrandom(buf: &mut [u8]) -> libc::c_long { + extern "C" { + fn syscall(number: libc::c_long, ...) -> libc::c_long; + } + + #[cfg(target_arch = "x86_64")] + const NR_GETRANDOM: libc::c_long = 318; + #[cfg(target_arch = "x86")] + const NR_GETRANDOM: libc::c_long = 355; + #[cfg(target_arch = "arm")] + const NR_GETRANDOM: libc::c_long = 384; + + unsafe { + syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), 0u) + } + } + + #[cfg(not(all(target_os = "linux", + any(target_arch = "x86_64", target_arch = "x86", target_arch = "arm"))))] + fn getrandom(_buf: &mut [u8]) -> libc::c_long { -1 } + + fn getrandom_fill_bytes(v: &mut [u8]) { + let mut read = 0; + let len = v.len(); + while read < len { + let result = getrandom(v[mut read..]); + if result == -1 { + let err = errno() as libc::c_int; + if err == libc::EINTR { + continue; + } else { + panic!("unexpected getrandom error: {}", err); + } + } else { + read += result as uint; + } + } + } + + fn getrandom_next_u32() -> u32 { + let mut buf: [u8, ..4] = [0u8, ..4]; + getrandom_fill_bytes(&mut buf); + unsafe { mem::transmute::<[u8, ..4], u32>(buf) } + } + + fn getrandom_next_u64() -> u64 { + let mut buf: [u8, ..8] = [0u8, ..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", target_arch = "arm")))] + fn is_getrandom_available() -> bool { + use sync::atomic::{AtomicBool, INIT_ATOMIC_BOOL, Relaxed}; + + static GETRANDOM_CHECKED: AtomicBool = INIT_ATOMIC_BOOL; + static GETRANDOM_AVAILABLE: AtomicBool = INIT_ATOMIC_BOOL; + + if !GETRANDOM_CHECKED.load(Relaxed) { + let mut buf: [u8, ..0] = []; + let result = getrandom(&mut buf); + let available = if result == -1 { + let err = errno() as libc::c_int; + err != libc::ENOSYS + } else { + true + }; + GETRANDOM_AVAILABLE.store(available, Relaxed); + GETRANDOM_CHECKED.store(true, Relaxed); + available + } else { + GETRANDOM_AVAILABLE.load(Relaxed) + } + } + + #[cfg(not(all(target_os = "linux", + any(target_arch = "x86_64", target_arch = "x86", target_arch = "arm"))))] + fn is_getrandom_available() -> bool { false } /// A random number generator that retrieves randomness straight from /// the operating system. Platform sources: /// /// - Unix-like systems (Linux, Android, Mac OSX): read directly from - /// `/dev/urandom`. + /// `/dev/urandom`, or from `getrandom(2)` system call if available. /// - Windows: calls `CryptGenRandom`, using the default cryptographic /// service provider with the `PROV_RSA_FULL` type. /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed /// This does not block. - #[cfg(unix)] pub struct OsRng { - inner: ReaderRng<File> + inner: OsRngInner, + } + + enum OsRngInner { + OsGetrandomRng, + OsReaderRng(ReaderRng<File>), } impl OsRng { /// Create a new `OsRng`. pub fn new() -> IoResult<OsRng> { + if is_getrandom_available() { + return Ok(OsRng { inner: OsGetrandomRng }); + } + let reader = try!(File::open(&Path::new("/dev/urandom"))); let reader_rng = ReaderRng::new(reader); - Ok(OsRng { inner: reader_rng }) + Ok(OsRng { inner: OsReaderRng(reader_rng) }) } } impl Rng for OsRng { fn next_u32(&mut self) -> u32 { - self.inner.next_u32() + match self.inner { + OsGetrandomRng => getrandom_next_u32(), + OsReaderRng(ref mut rng) => rng.next_u32(), + } } fn next_u64(&mut self) -> u64 { - self.inner.next_u64() + match self.inner { + OsGetrandomRng => getrandom_next_u64(), + OsReaderRng(ref mut rng) => rng.next_u64(), + } } fn fill_bytes(&mut self, v: &mut [u8]) { - self.inner.fill_bytes(v) + match self.inner { + OsGetrandomRng => getrandom_fill_bytes(v), + OsReaderRng(ref mut rng) => rng.fill_bytes(v) + } } } } @@ -75,7 +179,7 @@ mod imp { /// the operating system. Platform sources: /// /// - Unix-like systems (Linux, Android, Mac OSX): read directly from - /// `/dev/urandom`. + /// `/dev/urandom`, or from `getrandom(2)` system call if available. /// - Windows: calls `CryptGenRandom`, using the default cryptographic /// service provider with the `PROV_RSA_FULL` type. /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed @@ -145,10 +249,10 @@ mod imp { /// the operating system. Platform sources: /// /// - Unix-like systems (Linux, Android, Mac OSX): read directly from - /// `/dev/urandom`. + /// `/dev/urandom`, or from `getrandom(2)` system call if available. /// - Windows: calls `CryptGenRandom`, using the default cryptographic /// service provider with the `PROV_RSA_FULL` type. - /// + /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed /// This does not block. pub struct OsRng { hcryptprov: HCRYPTPROV |
