about summary refs log tree commit diff
path: root/src/libstd
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2014-11-06 13:31:58 -0800
committerAlex Crichton <alex@alexcrichton.com>2014-11-06 13:31:58 -0800
commit6783afcc17e81e580b82cc4f4872af3abb0a3662 (patch)
tree5c3bab4e311fcde875f051e93a1b19adb30b412d /src/libstd
parentfa530fff51b3e5ff74969c320dfef44c16d3bc22 (diff)
parentfdf5195f58971337ad46a9996613660279966120 (diff)
downloadrust-6783afcc17e81e580b82cc4f4872af3abb0a3662.tar.gz
rust-6783afcc17e81e580b82cc4f4872af3abb0a3662.zip
rollup merge of #18664 : klutzy/getrandom
Diffstat (limited to 'src/libstd')
-rw-r--r--src/libstd/rand/mod.rs8
-rw-r--r--src/libstd/rand/os.rs124
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