diff options
| author | Jason A. Donenfeld <Jason@zx2c4.com> | 2022-04-08 22:09:44 +0200 |
|---|---|---|
| committer | Jason A. Donenfeld <Jason@zx2c4.com> | 2022-05-21 00:02:20 +0200 |
| commit | 18a9d58266dfb86a3e3e6b53a42798dd4348d93b (patch) | |
| tree | ee8cad0e916c301c1e3aced705ea22e4f9810d5e | |
| parent | 204da52c34db2aa7e58e7b743c2bf2cdf485c048 (diff) | |
| download | rust-18a9d58266dfb86a3e3e6b53a42798dd4348d93b.tar.gz rust-18a9d58266dfb86a3e3e6b53a42798dd4348d93b.zip | |
Use GRND_INSECURE instead of /dev/urandom when possible
From reading the source code, it appears like the desired semantic of std::unix::rand is to always provide some bytes and never block. For that reason GRND_NONBLOCK is checked before calling getrandom(0), so that getrandom(0) won't block. If it would block, then the function falls back to using /dev/urandom, which for the time being doesn't block. There are some drawbacks to using /dev/urandom, however, and so getrandom(GRND_INSECURE) was created as a replacement for this exact circumstance. getrandom(GRND_INSECURE) is the same as /dev/urandom, except: - It won't leave a warning in dmesg if used at early boot time, which is a common occurance (and the reason why I found this issue); - It won't introduce a tiny delay at early boot on newer kernels when /dev/urandom tries to opportunistically create jitter entropy; - It only requires 1 syscall, rather than 3. Other than that, it returns the same "quality" of randomness as /dev/urandom, and never blocks. It's only available on kernels ≥5.6, so we try to use it, cache the result of that attempt, and fall back to to the previous code if it didn't work.
| -rw-r--r-- | library/std/src/sys/unix/rand.rs | 15 |
1 files changed, 15 insertions, 0 deletions
diff --git a/library/std/src/sys/unix/rand.rs b/library/std/src/sys/unix/rand.rs index 17e8efbe097..ea6df724713 100644 --- a/library/std/src/sys/unix/rand.rs +++ b/library/std/src/sys/unix/rand.rs @@ -30,6 +30,9 @@ mod imp { #[cfg(any(target_os = "linux", target_os = "android"))] fn getrandom(buf: &mut [u8]) -> libc::ssize_t { + use crate::sync::atomic::{AtomicBool, Ordering}; + use crate::sys::os::errno; + // A weak symbol allows interposition, e.g. for perf measurements that want to // disable randomness for consistency. Otherwise, we'll try a raw syscall. // (`getrandom` was added in glibc 2.25, musl 1.1.20, android API level 28) @@ -41,6 +44,18 @@ mod imp { ) -> libc::ssize_t } + // This provides the best quality random numbers available at the given moment + // without ever blocking, and is preferable to falling back to /dev/urandom. + static GRND_INSECURE_AVAILABLE: AtomicBool = AtomicBool::new(true); + if GRND_INSECURE_AVAILABLE.load(Ordering::Relaxed) { + let ret = unsafe { getrandom(buf.as_mut_ptr().cast(), buf.len(), libc::GRND_INSECURE) }; + if ret == -1 && errno() as libc::c_int == libc::EINVAL { + GRND_INSECURE_AVAILABLE.store(false, Ordering::Relaxed); + } else { + return ret; + } + } + unsafe { getrandom(buf.as_mut_ptr().cast(), buf.len(), libc::GRND_NONBLOCK) } } |
