From f875daca54b14d74a3b236b971b88d5e5f739b81 Mon Sep 17 00:00:00 2001 From: Doug Goldstein Date: Sat, 16 Apr 2016 15:25:56 -0500 Subject: [PATCH 1/3] rand: don't block before random pool is initialized If we attempt a read with getrandom() on Linux the syscall can block before the random pool is initialized unless the GRND_NONBLOCK flag is passed. This flag causes getrandom() to instead return EAGAIN while the pool is uninitialized. To avoid downstream users of crate or std functionality that have no ability to avoid this blocking behavior this change causes Rust to read bytes from /dev/urandom while getrandom() would block and once getrandom() is available to use that. Fixes #32953. Signed-off-by: Doug Goldstein --- src/libstd/sys/unix/rand.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/libstd/sys/unix/rand.rs b/src/libstd/sys/unix/rand.rs index 92c3bf8829adf..7f52a9c1bee96 100644 --- a/src/libstd/sys/unix/rand.rs +++ b/src/libstd/sys/unix/rand.rs @@ -41,8 +41,10 @@ mod imp { #[cfg(target_arch = "aarch64")] const NR_GETRANDOM: libc::c_long = 278; + const GRND_NONBLOCK: libc::c_uint = 0x0001; + unsafe { - libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), 0) + libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK) } } @@ -63,6 +65,11 @@ mod imp { let err = errno() as libc::c_int; if err == libc::EINTR { continue; + } else if err == libc::EAGAIN { + let reader = File::open("/dev/urandom").expect("Unable to open /dev/urandom"); + let mut reader_rng = ReaderRng::new(reader); + reader_rng.fill_bytes(& mut v[read..]); + read += v.len() as usize; } else { panic!("unexpected getrandom error: {}", err); } From 121225f17d039e51ea462437189c7c5d229291a8 Mon Sep 17 00:00:00 2001 From: Doug Goldstein Date: Wed, 20 Apr 2016 13:28:05 -0400 Subject: [PATCH 2/3] HashMap: add info to docs about random seed quality The random functions that HashMap use make no guarantees about the quality of random data so this documents that to the user so that they are aware. This was brought about by the change to the Linux random code to not block until the urandom pool was initialized to avoid users of crates that internally use HashMap being caught unaware and having their application block until the urandom pool is initialized. Signed-off-by: Doug Goldstein --- src/libstd/collections/hash/map.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index c20270e830665..3de9276b5acb9 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -202,8 +202,10 @@ fn test_resize_policy() { /// The hashes are all keyed by the thread-local random number generator /// on creation by default. This means that the ordering of the keys is /// randomized, but makes the tables more resistant to -/// denial-of-service attacks (Hash DoS). This behavior can be -/// overridden with one of the constructors. +/// denial-of-service attacks (Hash DoS). No guarantees are made to the +/// quality of the random data. The implementation uses the best available +/// random data from your platform at the time of creation. This behavior +/// can be overridden with one of the constructors. /// /// It is required that the keys implement the `Eq` and `Hash` traits, although /// this can frequently be achieved by using `#[derive(PartialEq, Eq, Hash)]`. From 61cbd07dec2d9670f170bed44f1815ea29a8a214 Mon Sep 17 00:00:00 2001 From: Doug Goldstein Date: Wed, 20 Apr 2016 18:29:33 -0400 Subject: [PATCH 3/3] rand: add comments about getrandom() fallback Add some comments so that people know why we are performing a fallback from getrandom() and what that fallback aims to achieve. Signed-off-by: Doug Goldstein --- src/libstd/sys/unix/rand.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/libstd/sys/unix/rand.rs b/src/libstd/sys/unix/rand.rs index 7f52a9c1bee96..25a7a3ce50dc4 100644 --- a/src/libstd/sys/unix/rand.rs +++ b/src/libstd/sys/unix/rand.rs @@ -66,6 +66,14 @@ mod imp { if err == libc::EINTR { continue; } else if err == libc::EAGAIN { + // if getrandom() returns EAGAIN 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 not over a completely + // full entropy pool let reader = File::open("/dev/urandom").expect("Unable to open /dev/urandom"); let mut reader_rng = ReaderRng::new(reader); reader_rng.fill_bytes(& mut v[read..]);