From f4eeb608d2cfe0de2fc29facbbaf5becca66e11a Mon Sep 17 00:00:00 2001 From: joboet Date: Tue, 7 Mar 2023 18:55:45 +0100 Subject: [PATCH] std: implement `io::Entropy`, refactor random data generation --- library/std/src/collections/hash/map.rs | 14 +- library/std/src/io/entropy.rs | 125 +++++++ library/std/src/io/mod.rs | 10 + library/std/src/io/readbuf.rs | 14 + library/std/src/sys/hermit/entropy.rs | 22 ++ library/std/src/sys/hermit/mod.rs | 15 +- library/std/src/sys/sgx/abi/usercalls/mod.rs | 5 +- library/std/src/sys/sgx/entropy.rs | 36 ++ library/std/src/sys/sgx/mod.rs | 19 +- library/std/src/sys/solid/entropy.rs | 31 ++ library/std/src/sys/solid/mod.rs | 11 +- library/std/src/sys/unix/entropy.rs | 373 +++++++++++++++++++ library/std/src/sys/unix/mod.rs | 4 +- library/std/src/sys/unix/rand.rs | 302 --------------- library/std/src/sys/unsupported/common.rs | 4 - library/std/src/sys/unsupported/entropy.rs | 22 ++ library/std/src/sys/unsupported/mod.rs | 1 + library/std/src/sys/wasi/entropy.rs | 25 ++ library/std/src/sys/wasi/mod.rs | 12 +- library/std/src/sys/wasm/mod.rs | 2 + library/std/src/sys/windows/entropy.rs | 59 +++ library/std/src/sys/windows/mod.rs | 4 +- library/std/src/sys/windows/pipe.rs | 5 +- library/std/src/sys/windows/rand.rs | 39 -- 24 files changed, 744 insertions(+), 410 deletions(-) create mode 100644 library/std/src/io/entropy.rs create mode 100644 library/std/src/sys/hermit/entropy.rs create mode 100644 library/std/src/sys/sgx/entropy.rs create mode 100644 library/std/src/sys/solid/entropy.rs create mode 100644 library/std/src/sys/unix/entropy.rs delete mode 100644 library/std/src/sys/unix/rand.rs create mode 100644 library/std/src/sys/unsupported/entropy.rs create mode 100644 library/std/src/sys/wasi/entropy.rs create mode 100644 library/std/src/sys/windows/entropy.rs delete mode 100644 library/std/src/sys/windows/rand.rs diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index 742c4cc7c5539..f60b83473be9e 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -13,9 +13,9 @@ use crate::error::Error; use crate::fmt::{self, Debug}; #[allow(deprecated)] use crate::hash::{BuildHasher, Hash, Hasher, SipHasher13}; +use crate::io::{entropy, Read}; use crate::iter::FusedIterator; use crate::ops::Index; -use crate::sys; /// A [hash map] implemented with quadratic probing and SIMD lookup. /// @@ -3122,7 +3122,17 @@ impl RandomState { // increment one of the seeds on every RandomState creation, giving // every corresponding HashMap a different iteration order. thread_local!(static KEYS: Cell<(u64, u64)> = { - Cell::new(sys::hashmap_random_keys()) + if crate::sys::entropy::INSECURE_HASHMAP { + Cell::new((1, 2)) + } else { + let mut v = [0u8; 16]; + let mut entropy = entropy(); + entropy.set_insecure(true); + entropy.read_exact(&mut v).expect("failed to generate random keys for hashmap"); + let key1 = v[..8].try_into().unwrap(); + let key2 = v[8..].try_into().unwrap(); + Cell::new((u64::from_ne_bytes(key1), u64::from_ne_bytes(key2))) + } }); KEYS.with(|keys| { diff --git a/library/std/src/io/entropy.rs b/library/std/src/io/entropy.rs new file mode 100644 index 0000000000000..f89bd2a6f768a --- /dev/null +++ b/library/std/src/io/entropy.rs @@ -0,0 +1,125 @@ +use super::{BorrowedBuf, BorrowedCursor, Read, Result}; +use crate::sys::entropy as sys; + +/// A reader which returns random bytes from the system entropy source. +/// +/// This struct is generally created by calling [`entropy()`]. Please +/// see the documentation of [`entropy()`] for more details. +#[derive(Debug)] +#[unstable(feature = "io_entropy", issue = "none")] +pub struct Entropy { + insecure: bool, +} + +impl Entropy { + pub(crate) fn set_insecure(&mut self, insecure: bool) { + self.insecure = insecure; + } +} + +#[unstable(feature = "io_entropy", issue = "none")] +impl Read for Entropy { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> Result { + sys::Entropy { insecure: self.insecure }.read(buf) + } + + #[inline] + fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> { + let mut buf = BorrowedBuf::from(buf); + self.read_buf_exact(buf.unfilled()) + } + + #[inline] + fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> Result<()> { + sys::Entropy { insecure: self.insecure }.read_buf(buf) + } + + #[inline] + fn read_buf_exact(&mut self, buf: BorrowedCursor<'_>) -> Result<()> { + sys::Entropy { insecure: self.insecure }.read_buf_exact(buf) + } +} + +/// Constructs a new handle to the system entropy source. +/// +/// Reads from the resulting reader will return high-quality random data that +/// is suited for cryptographic purposes (by the standard of the platform defaults). +/// +/// Be aware that, because the data is of very high quality, reading high amounts +/// of data can be very slow, and potentially slow down other processes requiring +/// random data. Use a pseudo-random number generator if speed is important. +/// +/// # Platform sources +/// +/// | OS | Source +/// |------------------|-------- +/// | Linux, Android | [`getrandom`][1] if available, otherwise [`/dev/urandom`][2] after successfully polling [`/dev/random`][2] +/// | Windows | [`BCryptGenRandom`][3], falling back to [`RtlGenRandom`][4] +/// | macOS | [`getentropy`][5] if available, falling back to [`/dev/urandom`][6] +/// | OpenBSD | [`getentropy`][7] +/// | iOS, watchOS | [`SecRandomCopyBytes`][8] +/// | FreeBSD | [`kern.arandom`][9] +/// | NetBSD | [`kern.arandom`][10] +/// | Fuchsia | [`zx_cprng_draw`][11] +/// | WASM | *Unsupported* +/// | WASI | [`random_get`][12] +/// | Emscripten | [`getentropy`][7] +/// | Redox | `rand:` +/// | VxWorks | `randABytes` after checking entropy pool initialization with `randSecure` +/// | Haiku | `/dev/urandom` +/// | ESP-IDF, Horizon | [`getrandom`][1] +/// | Other UNIXes | `/dev/random` +/// | Hermit | [`read_entropy`][13] +/// | SGX | [`RDRAND`][14] +/// | SOLID | `SOLID_RNG_SampleRandomBytes` +/// +/// [1]: https://man7.org/linux/man-pages/man2/getrandom.2.html +/// [2]: https://man7.org/linux/man-pages/man7/random.7.html +/// [3]: https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom +/// [4]: https://learn.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom +/// [5]: https://www.unix.com/man-page/mojave/2/getentropy/ +/// [6]: https://www.unix.com/man-page/mojave/4/random/ +/// [7]: https://man.openbsd.org/getentropy.2 +/// [8]: https://developer.apple.com/documentation/security/1399291-secrandomcopybytes?language=objc +/// [9]: https://man.freebsd.org/cgi/man.cgi?query=random&sektion=4&manpath=FreeBSD+13.1-RELEASE+and+Ports +/// [10]: https://man.netbsd.org/rnd.4 +/// [11]: https://fuchsia.dev/fuchsia-src/reference/syscalls/cprng_draw +/// [12]: https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md +/// [13]: https://docs.rs/hermit-abi/latest/hermit_abi/fn.read_entropy.html +/// [14]: https://www.intel.com/content/www/us/en/developer/articles/guide/intel-digital-random-number-generator-drng-software-implementation-guide.html +/// +/// # Examples +/// +/// Generating a seed for a random number generator: +/// +/// ```rust +/// #![feature(io_entropy)] +/// +/// # use std::io::Result; +/// # fn main() -> Result<()> { +/// use std::io::{entropy, Read}; +/// +/// let mut seed = [0u8; 32]; +/// entropy().read_exact(&mut seed)?; +/// println!("seed: {seed:?}"); +/// # Ok(()) +/// # } +/// ``` +/// +/// Implementing your very own `/dev/random`: +/// +/// ```rust, no_run +/// #![feature(io_entropy)] +/// +/// use std::io::{copy, entropy, stdout}; +/// +/// fn main() { +/// let _ = copy(&mut entropy(), &mut stdout()); +/// } +/// ``` +#[inline] +#[unstable(feature = "io_entropy", issue = "none")] +pub fn entropy() -> Entropy { + Entropy { insecure: false } +} diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 4b31c552eedd2..b0b87b584d619 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -262,6 +262,8 @@ use crate::sys_common::memchr; #[stable(feature = "bufwriter_into_parts", since = "1.56.0")] pub use self::buffered::WriterPanicked; +#[unstable(feature = "io_entropy", issue = "none")] +pub use self::entropy::{entropy, Entropy}; #[unstable(feature = "raw_os_error_ty", issue = "107792")] pub use self::error::RawOsError; pub(crate) use self::stdio::attempt_print_to_stderr; @@ -289,6 +291,7 @@ pub(crate) use error::const_io_error; mod buffered; pub(crate) mod copy; mod cursor; +mod entropy; mod error; mod impls; pub mod prelude; @@ -351,6 +354,13 @@ where } } +/// Implement the `read` method by forwarding to `read_buf`. +// FIXME(joboet): remove once #106643 is merged. +pub(crate) fn default_read(r: &mut R, buf: &mut [u8]) -> Result { + let mut buf = BorrowedBuf::from(buf); + r.read_buf(buf.unfilled()).map(|()| buf.len()) +} + // This uses an adaptive system to extend the vector when it fills. We want to // avoid paying to allocate and zero a huge chunk of memory if the reader only // has 4 bytes while still making large reads if the reader does have a ton diff --git a/library/std/src/io/readbuf.rs b/library/std/src/io/readbuf.rs index 4800eeda022bb..b300735fca215 100644 --- a/library/std/src/io/readbuf.rs +++ b/library/std/src/io/readbuf.rs @@ -230,6 +230,20 @@ impl<'a> BorrowedCursor<'a> { &mut self.buf.buf[self.buf.filled..] } + /// Returns a pointer to the current position of the cursor, with provenance to the + /// rest of the buffer. + /// + /// The resulting pointer may not be used to deinitialize any bytes in the initialized + /// section of the buffer. + // FIXME(joboet): Maybe make this public? + #[allow(unused)] + pub(crate) fn as_ptr(&mut self) -> *mut u8 { + // SAFETY: deinitialization requires `unsafe` in the caller, where they have to make + // sure not to deinitialize any bytes. The burden of prove is moved to the pointer + // operation. Just returning a pointer is sound. + unsafe { self.as_mut().as_mut_ptr().cast() } + } + /// Advance the cursor by asserting that `n` bytes have been filled. /// /// After advancing, the `n` bytes are no longer accessible via the cursor and can only be diff --git a/library/std/src/sys/hermit/entropy.rs b/library/std/src/sys/hermit/entropy.rs new file mode 100644 index 0000000000000..6e36d0024e734 --- /dev/null +++ b/library/std/src/sys/hermit/entropy.rs @@ -0,0 +1,22 @@ +use super::{abi, cvt}; +use crate::io::{default_read, BorrowedCursor, Read, Result}; + +pub const INSECURE_HASHMAP: bool = false; + +pub struct Entropy { + pub insecure: bool, +} + +impl Read for Entropy { + fn read(&mut self, buf: &mut [u8]) -> Result { + default_read(self, buf) + } + + fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> Result<()> { + unsafe { + let len = cvt(abi::read_entropy(buf.as_ptr(), buf.capacity(), 0))?; + buf.advance(len as usize); + Ok(()) + } + } +} diff --git a/library/std/src/sys/hermit/mod.rs b/library/std/src/sys/hermit/mod.rs index c7cb8466705cb..9086f0df80c02 100644 --- a/library/std/src/sys/hermit/mod.rs +++ b/library/std/src/sys/hermit/mod.rs @@ -21,6 +21,7 @@ pub mod alloc; pub mod args; #[path = "../unix/cmath.rs"] pub mod cmath; +pub mod entropy; pub mod env; pub mod fd; pub mod fs; @@ -75,20 +76,6 @@ pub fn abort_internal() -> ! { } } -pub fn hashmap_random_keys() -> (u64, u64) { - let mut buf = [0; 16]; - let mut slice = &mut buf[..]; - while !slice.is_empty() { - let res = cvt(unsafe { abi::read_entropy(slice.as_mut_ptr(), slice.len(), 0) }) - .expect("failed to generate random hashmap keys"); - slice = &mut slice[res as usize..]; - } - - let key1 = buf[..8].try_into().unwrap(); - let key2 = buf[8..].try_into().unwrap(); - (u64::from_ne_bytes(key1), u64::from_ne_bytes(key2)) -} - // This function is needed by the panic runtime. The symbol is named in // pre-link args for the target specification, so keep that in sync. #[cfg(not(test))] diff --git a/library/std/src/sys/sgx/abi/usercalls/mod.rs b/library/std/src/sys/sgx/abi/usercalls/mod.rs index e19e843267a90..e78952d316a03 100644 --- a/library/std/src/sys/sgx/abi/usercalls/mod.rs +++ b/library/std/src/sys/sgx/abi/usercalls/mod.rs @@ -1,6 +1,6 @@ use crate::cmp; use crate::io::{Error as IoError, ErrorKind, IoSlice, IoSliceMut, Result as IoResult}; -use crate::sys::rand::rdrand64; +use crate::sys::entropy::rdrand64; use crate::time::{Duration, Instant}; pub(crate) mod alloc; @@ -164,7 +164,8 @@ pub fn wait(event_mask: u64, mut timeout: u64) -> IoResult { // trusted to ensure accurate timeouts. if let Ok(timeout_signed) = i64::try_from(timeout) { let tenth = timeout_signed / 10; - let deviation = (rdrand64() as i64).checked_rem(tenth).unwrap_or(0); + let rand = rdrand64().unwrap_or_else(|| rtabort!("Failed to obtain random data")); + let deviation = (rand as i64).checked_rem(tenth).unwrap_or(0); timeout = timeout_signed.saturating_add(deviation) as _; } } diff --git a/library/std/src/sys/sgx/entropy.rs b/library/std/src/sys/sgx/entropy.rs new file mode 100644 index 0000000000000..6bf09c2db3bc0 --- /dev/null +++ b/library/std/src/sys/sgx/entropy.rs @@ -0,0 +1,36 @@ +use crate::io::{const_io_error, default_read, BorrowedCursor, ErrorKind, Read, Result}; + +pub const INSECURE_HASHMAP: bool = false; + +pub struct Entropy { + pub insecure: bool, +} + +impl Read for Entropy { + fn read(&mut self, buf: &mut [u8]) -> Result { + default_read(self, buf) + } + + fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> Result<()> { + if buf.capacity() != 0 { + let rand = rdrand64() + .ok_or(const_io_error!(ErrorKind::WouldBlock, "no random data available"))?; + buf.append(&rand.to_ne_bytes()[..usize::min(buf.capacity(), 8)]); + Ok(()) + } else { + Ok(()) + } + } +} + +pub(super) fn rdrand64() -> Option { + unsafe { + let mut ret: u64 = 0; + for _ in 0..10 { + if crate::arch::x86_64::_rdrand64_step(&mut ret) == 1 { + return Some(ret); + } + } + None + } +} diff --git a/library/std/src/sys/sgx/mod.rs b/library/std/src/sys/sgx/mod.rs index 9865a945bad1d..e3100e110353f 100644 --- a/library/std/src/sys/sgx/mod.rs +++ b/library/std/src/sys/sgx/mod.rs @@ -15,6 +15,7 @@ pub mod alloc; pub mod args; #[path = "../unix/cmath.rs"] pub mod cmath; +pub mod entropy; pub mod env; pub mod fd; #[path = "../unsupported/fs.rs"] @@ -144,24 +145,6 @@ pub extern "C" fn __rust_abort() { abort_internal(); } -pub mod rand { - pub fn rdrand64() -> u64 { - unsafe { - let mut ret: u64 = 0; - for _ in 0..10 { - if crate::arch::x86_64::_rdrand64_step(&mut ret) == 1 { - return ret; - } - } - rtabort!("Failed to obtain random data"); - } - } -} - -pub fn hashmap_random_keys() -> (u64, u64) { - (self::rand::rdrand64(), self::rand::rdrand64()) -} - pub use crate::sys_common::{AsInner, FromInner, IntoInner}; pub trait TryIntoInner: Sized { diff --git a/library/std/src/sys/solid/entropy.rs b/library/std/src/sys/solid/entropy.rs new file mode 100644 index 0000000000000..232bd489bc822 --- /dev/null +++ b/library/std/src/sys/solid/entropy.rs @@ -0,0 +1,31 @@ +use super::abi; +use super::error::SolidError; +use crate::io::{default_read, BorrowedCursor, Read, Result}; + +pub const INSECURE_HASHMAP: bool = false; + +pub struct Entropy { + pub insecure: bool, +} + +impl Read for Entropy { + fn read(&mut self, buf: &mut [u8]) -> Result { + default_read(self, buf) + } + + fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> Result<()> { + SolidError::err_if_negative(unsafe { + abi::SOLID_RNG_SampleRandomBytes(buf.as_mut().as_mut_ptr().cast(), buf.capacity()) + }) + .map_err(|e| e.as_io_error())?; + + unsafe { + buf.advance(buf.capacity()); + Ok(()) + } + } + + fn read_buf_exact(&mut self, buf: BorrowedCursor<'_>) -> Result<()> { + self.read_buf(buf) + } +} diff --git a/library/std/src/sys/solid/mod.rs b/library/std/src/sys/solid/mod.rs index 923d27fd9369d..3f536e8b7ff6c 100644 --- a/library/std/src/sys/solid/mod.rs +++ b/library/std/src/sys/solid/mod.rs @@ -23,6 +23,7 @@ pub mod alloc; pub mod args; #[path = "../unix/cmath.rs"] pub mod cmath; +pub mod entropy; pub mod env; // `error` is `pub(crate)` so that it can be accessed by `itron/error.rs` as // `crate::sys::error` @@ -80,13 +81,3 @@ pub fn decode_error_kind(code: i32) -> crate::io::ErrorKind { pub fn abort_internal() -> ! { unsafe { libc::abort() } } - -pub fn hashmap_random_keys() -> (u64, u64) { - unsafe { - let mut out = crate::mem::MaybeUninit::<[u64; 2]>::uninit(); - let result = abi::SOLID_RNG_SampleRandomBytes(out.as_mut_ptr() as *mut u8, 16); - assert_eq!(result, 0, "SOLID_RNG_SampleRandomBytes failed: {result}"); - let [x1, x2] = out.assume_init(); - (x1, x2) - } -} diff --git a/library/std/src/sys/unix/entropy.rs b/library/std/src/sys/unix/entropy.rs new file mode 100644 index 0000000000000..4c84a4ba850bf --- /dev/null +++ b/library/std/src/sys/unix/entropy.rs @@ -0,0 +1,373 @@ +use crate::io::{default_read, BorrowedCursor, Read, Result}; + +pub const INSECURE_HASHMAP: bool = false; + +pub struct Entropy { + pub insecure: bool, +} + +cfg_if::cfg_if! { + if #[cfg(any( + target_os = "ios", + target_os = "watchos", + target_os = "openbsd", + target_os = "freebsd", + target_os = "netbsd", + target_os = "fuchsia", + target_os = "vxworks", + target_os = "espidf", + target_os = "horizon", + target_os = "emscripten" + ))] { + impl Read for Entropy { + fn read(&mut self, buf: &mut [u8]) -> Result { + default_read(self, buf) + } + + fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> Result<()> { + syscall(buf, self.insecure) + } + + // On some platforms, the syscall always fills the complete buffer. + #[cfg(any( + target_os = "ios", + target_os = "watchos", + target_os = "fuchsia", + ))] + fn read_buf_exact(&mut self, buf: BorrowedCursor<'_>) -> Result<()> { + syscall(buf, self.insecure) + } + } + } else if #[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "macos", + ))] { + use crate::fs::File; + + impl Read for Entropy { + fn read(&mut self, buf: &mut [u8]) -> Result { + default_read(self, buf) + } + + fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> Result<()> { + syscall(buf.reborrow(), self.insecure).unwrap_or_else(|| { + let mut file = file(self.insecure)?; + file.read_buf(buf) + }) + } + } + } else { + use crate::fs::File; + + impl Read for Entropy { + fn read(&mut self, buf: &mut [u8]) -> Result { + default_read(self, buf) + } + + fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> Result<()> { + let mut file = file(self.insecure)?; + file.read_buf(buf) + } + + fn read_buf_exact(&mut self, buf: BorrowedCursor<'_>) -> Result<()> { + let mut file = file(self.insecure)?; + file.read_buf_exact(buf) + } + } + } +} + +#[cfg(any(target_os = "macos", target_os = "redox", target_os = "haiku"))] +fn file(_: bool) -> Result<&'static File> { + use crate::sync::OnceLock; + + static URANDOM: OnceLock = OnceLock::new(); + + #[cfg(not(target_os = "redox"))] + let path = "/dev/urandom"; + #[cfg(target_os = "redox")] + let path = "rand:"; + URANDOM.get_or_try_init(|| File::open(path)) +} + +#[cfg(any(target_os = "linux", target_os = "android"))] +fn file(insecure: bool) -> Result<&'static File> { + use crate::io::{Error, ErrorKind}; + use crate::os::fd::AsRawFd; + use crate::sync::atomic::{AtomicBool, Ordering::Relaxed}; + use crate::sync::OnceLock; + + static URANDOM: OnceLock = OnceLock::new(); + static IS_READY: AtomicBool = AtomicBool::new(false); + + let urandom = URANDOM.get_or_try_init(|| File::open("/dev/urandom"))?; + + while !insecure && !IS_READY.load(Relaxed) { + let random = File::open("/dev/random")?; + + let mut pollfd = libc::pollfd { fd: random.as_raw_fd(), events: libc::POLLIN, revents: 0 }; + let res = unsafe { libc::poll(&mut pollfd, 1, -1) }; + match (res < 0).then(|| Error::last_os_error()) { + None => { + IS_READY.store(true, Relaxed); + break; + } + Some(e) if e.kind() == ErrorKind::Interrupted => continue, + Some(e) => return Err(e), + } + } + + Ok(urandom) +} + +#[cfg(not(any( + target_os = "linux", + target_os = "android", + target_os = "macos", + target_os = "ios", + target_os = "watchos", + target_os = "openbsd", + target_os = "freebsd", + target_os = "netbsd", + target_os = "fuchsia", + target_os = "vxworks", + target_os = "redox", + target_os = "espidf", + target_os = "horizon", +)))] +fn file(insecure: bool) -> Result<&'static File> { + use crate::sync::OnceLock; + + static RANDOM: OnceLock = OnceLock::new(); + static URANDOM: OnceLock = OnceLock::new(); + + if insecure { + URANDOM.get_or_try_init(|| File::open("/dev/urandom")) + } else { + RANDOM.get_or_try_init(|| File::open("/dev/random")) + } +} + +#[cfg(any(target_os = "linux", target_os = "android"))] +fn syscall(mut buf: BorrowedCursor<'_>, insecure: bool) -> Option> { + use crate::io::Error; + use crate::sync::atomic::{AtomicBool, Ordering::Relaxed}; + use crate::sys::os::errno; + use crate::sys::weak::syscall; + + // 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) + syscall! { + fn getrandom( + buffer: *mut libc::c_void, + length: libc::size_t, + flags: libc::c_uint + ) -> libc::ssize_t + } + + static GRND_AVAILABLE: AtomicBool = AtomicBool::new(true); + + // 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_AVAILABLE.load(Relaxed) { + return None; + } + + loop { + let flags = if insecure && GRND_INSECURE_AVAILABLE.load(Relaxed) { + libc::GRND_INSECURE + } else if insecure { + libc::GRND_NONBLOCK + } else { + 0 + }; + + let ret = unsafe { getrandom(buf.as_ptr().cast(), buf.capacity(), flags) }; + if ret != -1 { + unsafe { + buf.advance(ret as usize); + return Some(Ok(())); + } + } else { + match errno() { + // Only returned if a flag is incorrect. This is only encountered on + // systems where GRND_INSECURE is not available, so we remember that + // and try again with GRND_NONBLOCK. + libc::EINVAL => GRND_INSECURE_AVAILABLE.store(false, Relaxed), + // Fall back to /dev/urandom to generate insecure data, as GRND_INSECURE + // is not available. + libc::EAGAIN => return None, + // If getrandom is not available or blocked by seccomp, fall back to + // the file method. + libc::ENOSYS | libc::EPERM => { + GRND_AVAILABLE.store(false, Relaxed); + return None; + } + err => return Some(Err(Error::from_raw_os_error(err))), + } + } + } +} + +#[cfg(target_os = "macos")] +fn syscall(mut buf: BorrowedCursor<'_>, _: bool) -> Option> { + use crate::io::Error; + use crate::sys::weak::weak; + use libc::{c_int, c_void, size_t}; + + weak!(fn getentropy(*mut c_void, size_t) -> c_int); + + let getentropy = getentropy.get()?; + // getentropy(2) permits a maximum buffer size of 256 bytes + let len = usize::min(buf.capacity(), 256); + let ret = unsafe { getentropy(buf.as_ptr().cast(), len) }; + if ret != -1 { + unsafe { + buf.advance(len); + Some(Ok(())) + } + } else { + Some(Err(Error::last_os_error())) + } +} + +#[cfg(any(target_os = "openbsd", target_os = "emscripten"))] +fn syscall(mut buf: BorrowedCursor<'_>, _: bool) -> Result<()> { + use crate::io::Error; + + // getentropy(2) permits a maximum buffer size of 256 bytes + let len = usize::min(buf.capacity(), 256); + let ret = unsafe { libc::getentropy(buf.as_ptr().cast(), len) }; + if ret != -1 { + unsafe { + buf.advance(len); + Ok(()) + } + } else { + Err(Error::last_os_error()) + } +} + +// On iOS and MacOS `SecRandomCopyBytes` calls `CCRandomCopyBytes` with +// `kCCRandomDefault`. `CCRandomCopyBytes` manages a CSPRNG which is seeded +// from `/dev/random` and which runs on its own thread accessed via GCD. +// This seems needlessly heavyweight for the purposes of generating two u64s +// once per thread in `hashmap_random_keys`. Therefore `SecRandomCopyBytes` is +// only used on iOS where direct access to `/dev/urandom` is blocked by the +// sandbox. +#[cfg(any(target_os = "ios", target_os = "watchos"))] +fn syscall(mut buf: BorrowedCursor<'_>, _: bool) -> Result<()> { + use crate::io::Error; + use crate::ptr; + use libc::{c_int, size_t}; + + enum SecRandom {} + + #[allow(non_upper_case_globals)] + const kSecRandomDefault: *const SecRandom = ptr::null(); + + extern "C" { + fn SecRandomCopyBytes(rnd: *const SecRandom, count: size_t, bytes: *mut u8) -> c_int; + } + + let ret = unsafe { SecRandomCopyBytes(kSecRandomDefault, buf.capacity(), buf.as_ptr()) }; + if ret != -1 { + unsafe { + buf.advance(buf.capacity()); + Ok(()) + } + } else { + Err(Error::last_os_error()) + } +} + +#[cfg(any(target_os = "freebsd", target_os = "netbsd"))] +fn syscall(mut buf: BorrowedCursor<'_>, _: bool) -> Result<()> { + use crate::io::Error; + use crate::ptr; + + let mib = [libc::CTL_KERN, libc::KERN_ARND]; + // kern.arandom permits a maximum buffer size of 256 bytes + let mut len = usize::min(buf.capacity(), 256); + let ret = unsafe { + libc::sysctl( + mib.as_ptr(), + mib.len() as libc::c_uint, + buf.as_ptr().cast(), + &mut len, + ptr::null(), + 0, + ) + }; + + if ret != -1 { + unsafe { + buf.advance(len); + Ok(()) + } + } else { + Err(Error::last_os_error()) + } +} + +#[cfg(target_os = "fuchsia")] +fn syscall(mut buf: BorrowedCursor<'_>, _: bool) -> Result<()> { + #[link(name = "zircon")] + extern "C" { + fn zx_cprng_draw(buffer: *mut u8, len: usize); + } + + unsafe { + zx_cprng_draw(buf.as_ptr(), buf.capacity()); + buf.advance(buf.capacity()); + Ok(()) + } +} + +#[cfg(target_os = "vxworks")] +fn syscall(mut buf: BorrowedCursor<'_>, _: bool) -> Result<()> { + use crate::io::Error; + use crate::sync::atomic::{AtomicBool, Ordering::Relaxed}; + + static RNG_INIT: AtomicBool = AtomicBool::new(false); + while !RNG_INIT.load(Relaxed) { + let ret = unsafe { libc::randSecure() }; + if ret < 0 { + return Err(Error::last_os_error()); + } else if ret > 0 { + RNG_INIT.store(true, Relaxed); + break; + } + unsafe { libc::usleep(10) }; + } + + let len = buf.capacity().try_into().unwrap_or(i32::MAX); + let ret = unsafe { libc::randABytes(buf.as_ptr().cast(), len) }; + if ret == 0 { + unsafe { + buf.advance(len); + Ok(()) + } + } else { + Err(Error::last_os_error()) + } +} + +#[cfg(any(target_os = "espidf", target_os = "horizon"))] +fn syscall(mut buf: BorrowedCursor<'_>, _: bool) -> Result<()> { + use crate::io::Error; + + let ret = unsafe { libc::getrandom(buf.as_ptr().cast(), buf.capacity(), 0) }; + if ret != -1 { + unsafe { + buf.advance(ret as usize); + Ok(()) + } + } else { + Err(Error::last_os_error()) + } +} diff --git a/library/std/src/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs index 68c9520cc9eb4..bb9a83e8aa709 100644 --- a/library/std/src/sys/unix/mod.rs +++ b/library/std/src/sys/unix/mod.rs @@ -3,8 +3,6 @@ use crate::ffi::CStr; use crate::io::ErrorKind; -pub use self::rand::hashmap_random_keys; - #[cfg(not(target_os = "espidf"))] #[macro_use] pub mod weak; @@ -14,6 +12,7 @@ pub mod android; pub mod args; #[path = "../unix/cmath.rs"] pub mod cmath; +pub mod entropy; pub mod env; pub mod fd; pub mod fs; @@ -34,7 +33,6 @@ pub mod os_str; pub mod path; pub mod pipe; pub mod process; -pub mod rand; pub mod stack_overflow; pub mod stdio; pub mod thread; diff --git a/library/std/src/sys/unix/rand.rs b/library/std/src/sys/unix/rand.rs deleted file mode 100644 index 0f347ffab42fe..0000000000000 --- a/library/std/src/sys/unix/rand.rs +++ /dev/null @@ -1,302 +0,0 @@ -pub fn hashmap_random_keys() -> (u64, u64) { - const KEY_LEN: usize = core::mem::size_of::(); - - let mut v = [0u8; KEY_LEN * 2]; - imp::fill_bytes(&mut v); - - let key1 = v[0..KEY_LEN].try_into().unwrap(); - let key2 = v[KEY_LEN..].try_into().unwrap(); - - (u64::from_ne_bytes(key1), u64::from_ne_bytes(key2)) -} - -#[cfg(all( - unix, - not(target_os = "macos"), - not(target_os = "ios"), - not(target_os = "watchos"), - not(target_os = "openbsd"), - not(target_os = "freebsd"), - not(target_os = "netbsd"), - not(target_os = "fuchsia"), - not(target_os = "redox"), - not(target_os = "vxworks"), - not(target_os = "emscripten") -))] -mod imp { - use crate::fs::File; - use crate::io::Read; - - #[cfg(any(target_os = "linux", target_os = "android"))] - use crate::sys::weak::syscall; - - #[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) - syscall! { - fn getrandom( - buffer: *mut libc::c_void, - length: libc::size_t, - flags: libc::c_uint - ) -> 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) } - } - - #[cfg(any(target_os = "espidf", target_os = "horizon"))] - fn getrandom(buf: &mut [u8]) -> libc::ssize_t { - unsafe { libc::getrandom(buf.as_mut_ptr().cast(), buf.len(), 0) } - } - - #[cfg(not(any( - target_os = "linux", - target_os = "android", - target_os = "espidf", - target_os = "horizon" - )))] - fn getrandom_fill_bytes(_buf: &mut [u8]) -> bool { - false - } - - #[cfg(any( - target_os = "linux", - target_os = "android", - target_os = "espidf", - target_os = "horizon" - ))] - fn getrandom_fill_bytes(v: &mut [u8]) -> bool { - use crate::sync::atomic::{AtomicBool, Ordering}; - use crate::sys::os::errno; - - static GETRANDOM_UNAVAILABLE: AtomicBool = AtomicBool::new(false); - if GETRANDOM_UNAVAILABLE.load(Ordering::Relaxed) { - return false; - } - - let mut read = 0; - while read < v.len() { - 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 || err == libc::EPERM { - // Fall back to reading /dev/urandom if `getrandom` is not - // supported on the current kernel. - // - // Also fall back in case it is disabled by something like - // seccomp or inside of virtual machines. - GETRANDOM_UNAVAILABLE.store(true, Ordering::Relaxed); - return false; - } 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 because it is permanently or temporarily (because - // of missing entropy) unavailable. Open /dev/urandom, read from it, - // and close it again. - let mut file = File::open("/dev/urandom").expect("failed to open /dev/urandom"); - file.read_exact(v).expect("failed to read /dev/urandom") - } -} - -#[cfg(target_os = "macos")] -mod imp { - use crate::fs::File; - use crate::io::Read; - use crate::sys::os::errno; - use crate::sys::weak::weak; - use libc::{c_int, c_void, size_t}; - - fn getentropy_fill_bytes(v: &mut [u8]) -> bool { - weak!(fn getentropy(*mut c_void, size_t) -> c_int); - - getentropy - .get() - .map(|f| { - // getentropy(2) permits a maximum buffer size of 256 bytes - for s in v.chunks_mut(256) { - let ret = unsafe { f(s.as_mut_ptr() as *mut c_void, s.len()) }; - if ret == -1 { - panic!("unexpected getentropy error: {}", errno()); - } - } - true - }) - .unwrap_or(false) - } - - pub fn fill_bytes(v: &mut [u8]) { - if getentropy_fill_bytes(v) { - return; - } - - // for older macos which doesn't support getentropy - let mut file = File::open("/dev/urandom").expect("failed to open /dev/urandom"); - file.read_exact(v).expect("failed to read /dev/urandom") - } -} - -#[cfg(any(target_os = "openbsd", target_os = "emscripten"))] -mod imp { - use crate::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()); - } - } - } -} - -// On iOS and MacOS `SecRandomCopyBytes` calls `CCRandomCopyBytes` with -// `kCCRandomDefault`. `CCRandomCopyBytes` manages a CSPRNG which is seeded -// from `/dev/random` and which runs on its own thread accessed via GCD. -// This seems needlessly heavyweight for the purposes of generating two u64s -// once per thread in `hashmap_random_keys`. Therefore `SecRandomCopyBytes` is -// only used on iOS where direct access to `/dev/urandom` is blocked by the -// sandbox. -#[cfg(any(target_os = "ios", target_os = "watchos"))] -mod imp { - use crate::io; - use crate::ptr; - use libc::{c_int, size_t}; - - enum SecRandom {} - - #[allow(non_upper_case_globals)] - const kSecRandomDefault: *const SecRandom = ptr::null(); - - extern "C" { - fn SecRandomCopyBytes(rnd: *const SecRandom, count: size_t, bytes: *mut u8) -> c_int; - } - - 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()); - } - } -} - -#[cfg(any(target_os = "freebsd", target_os = "netbsd"))] -mod imp { - use crate::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 "C" { - 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()) } - } -} - -#[cfg(target_os = "redox")] -mod imp { - use crate::fs::File; - use crate::io::Read; - - pub fn fill_bytes(v: &mut [u8]) { - // Open rand:, read from it, and close it again. - let mut file = File::open("rand:").expect("failed to open rand:"); - file.read_exact(v).expect("failed to read rand:") - } -} - -#[cfg(target_os = "vxworks")] -mod imp { - use crate::io; - use core::sync::atomic::{AtomicBool, Ordering::Relaxed}; - - pub fn fill_bytes(v: &mut [u8]) { - static RNG_INIT: AtomicBool = AtomicBool::new(false); - while !RNG_INIT.load(Relaxed) { - let ret = unsafe { libc::randSecure() }; - if ret < 0 { - panic!("couldn't generate random bytes: {}", io::Error::last_os_error()); - } else if ret > 0 { - RNG_INIT.store(true, Relaxed); - break; - } - unsafe { libc::usleep(10) }; - } - let ret = unsafe { - libc::randABytes(v.as_mut_ptr() as *mut libc::c_uchar, v.len() as libc::c_int) - }; - if ret < 0 { - panic!("couldn't generate random bytes: {}", io::Error::last_os_error()); - } - } -} diff --git a/library/std/src/sys/unsupported/common.rs b/library/std/src/sys/unsupported/common.rs index 5cd9e57de19ee..5cf6217e6081d 100644 --- a/library/std/src/sys/unsupported/common.rs +++ b/library/std/src/sys/unsupported/common.rs @@ -30,7 +30,3 @@ pub fn decode_error_kind(_code: i32) -> crate::io::ErrorKind { pub fn abort_internal() -> ! { core::intrinsics::abort(); } - -pub fn hashmap_random_keys() -> (u64, u64) { - (1, 2) -} diff --git a/library/std/src/sys/unsupported/entropy.rs b/library/std/src/sys/unsupported/entropy.rs new file mode 100644 index 0000000000000..b76f25c62edab --- /dev/null +++ b/library/std/src/sys/unsupported/entropy.rs @@ -0,0 +1,22 @@ +use super::unsupported; +use crate::io::{BorrowedCursor, Read, Result}; + +pub const INSECURE_HASHMAP: bool = true; + +pub struct Entropy { + pub insecure: bool, +} + +impl Read for Entropy { + fn read(&mut self, _: &mut [u8]) -> Result { + unsupported() + } + + fn read_buf(&mut self, _: BorrowedCursor<'_>) -> Result<()> { + unsupported() + } + + fn read_buf_exact(&mut self, _: BorrowedCursor<'_>) -> Result<()> { + unsupported() + } +} diff --git a/library/std/src/sys/unsupported/mod.rs b/library/std/src/sys/unsupported/mod.rs index 15b22c620d583..80e39b370b375 100644 --- a/library/std/src/sys/unsupported/mod.rs +++ b/library/std/src/sys/unsupported/mod.rs @@ -4,6 +4,7 @@ pub mod alloc; pub mod args; #[path = "../unix/cmath.rs"] pub mod cmath; +pub mod entropy; pub mod env; pub mod fs; pub mod io; diff --git a/library/std/src/sys/wasi/entropy.rs b/library/std/src/sys/wasi/entropy.rs new file mode 100644 index 0000000000000..d9ea025ef0f8b --- /dev/null +++ b/library/std/src/sys/wasi/entropy.rs @@ -0,0 +1,25 @@ +use super::err2io; +use crate::io::{default_read, BorrowedCursor, Read, Result}; + +pub const INSECURE_HASHMAP: bool = false; + +pub struct Entropy { + pub insecure: bool, +} + +impl Read for Entropy { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> Result { + default_read(self, buf) + } + + #[inline] + fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> Result<()> { + unsafe { wasi::random_get(buf.as_ptr(), buf.capacity()).map_err(err2io) } + } + + #[inline] + fn read_buf_exact(&mut self, buf: BorrowedCursor<'_>) -> Result<()> { + self.read_buf(buf) + } +} diff --git a/library/std/src/sys/wasi/mod.rs b/library/std/src/sys/wasi/mod.rs index 1dc3f2b20266d..73db3e8bb685d 100644 --- a/library/std/src/sys/wasi/mod.rs +++ b/library/std/src/sys/wasi/mod.rs @@ -15,13 +15,13 @@ //! guaranteed to be a runtime error! use crate::io as std_io; -use crate::mem; #[path = "../unix/alloc.rs"] pub mod alloc; pub mod args; #[path = "../unix/cmath.rs"] pub mod cmath; +pub mod entropy; pub mod env; pub mod fd; pub mod fs; @@ -90,16 +90,6 @@ pub fn abort_internal() -> ! { unsafe { libc::abort() } } -pub fn hashmap_random_keys() -> (u64, u64) { - let mut ret = (0u64, 0u64); - unsafe { - let base = &mut ret as *mut (u64, u64) as *mut u8; - let len = mem::size_of_val(&ret); - wasi::random_get(base, len).expect("random_get failure"); - } - return ret; -} - fn err2io(err: wasi::Errno) -> std_io::Error { std_io::Error::from_raw_os_error(err.raw().into()) } diff --git a/library/std/src/sys/wasm/mod.rs b/library/std/src/sys/wasm/mod.rs index 77ebe3c4ac6f5..296c9d1395ebe 100644 --- a/library/std/src/sys/wasm/mod.rs +++ b/library/std/src/sys/wasm/mod.rs @@ -21,6 +21,8 @@ pub mod alloc; pub mod args; #[path = "../unix/cmath.rs"] pub mod cmath; +#[path = "../unsupported/entropy.rs"] +pub mod entropy; pub mod env; #[path = "../unsupported/fs.rs"] pub mod fs; diff --git a/library/std/src/sys/windows/entropy.rs b/library/std/src/sys/windows/entropy.rs new file mode 100644 index 0000000000000..824efe467e566 --- /dev/null +++ b/library/std/src/sys/windows/entropy.rs @@ -0,0 +1,59 @@ +use crate::io::{default_read, BorrowedCursor, Error, Read, Result}; +use crate::ptr; +use crate::sys::c; + +pub const INSECURE_HASHMAP: bool = false; + +pub struct Entropy { + pub insecure: bool, +} + +impl Read for Entropy { + fn read(&mut self, buf: &mut [u8]) -> Result { + default_read(self, buf) + } + + fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> Result<()> { + let len = buf.capacity().try_into().unwrap_or(c::ULONG::MAX); + let ret = unsafe { + c::BCryptGenRandom( + ptr::null_mut(), + buf.as_ptr().cast(), + len, + c::BCRYPT_USE_SYSTEM_PREFERRED_RNG, + ) + }; + if c::nt_success(ret) { + unsafe { + buf.advance(len as usize); + Ok(()) + } + } else { + fallback_rng(buf) + } + } +} + +/// Generate random numbers using the fallback RNG function (RtlGenRandom) +/// +/// This is necessary because of a failure to load the SysWOW64 variant of the +/// bcryptprimitives.dll library from code that lives in bcrypt.dll +/// See +#[cfg(not(target_vendor = "uwp"))] +#[inline(never)] +fn fallback_rng(mut buf: BorrowedCursor<'_>) -> Result<()> { + let len = buf.capacity().try_into().unwrap_or(c::ULONG::MAX); + let ret = unsafe { c::RtlGenRandom(buf.as_ptr().cast(), len) }; + + if ret != 0 { Ok(()) } else { Err(Error::last_os_error()) } +} + +/// We can't use RtlGenRandom with UWP, so there is no fallback +#[cfg(target_vendor = "uwp")] +#[inline(never)] +fn fallback_rng(_: BorrowedCursor<'_>) -> Result<()> { + Err(const_io_error!( + ErrorKind::Unsupported, + "fallback RNG broken: RtlGenRandom() not supported on UWP" + )) +} diff --git a/library/std/src/sys/windows/mod.rs b/library/std/src/sys/windows/mod.rs index 77359abe42995..d9f07be806b1f 100644 --- a/library/std/src/sys/windows/mod.rs +++ b/library/std/src/sys/windows/mod.rs @@ -7,8 +7,6 @@ use crate::os::windows::ffi::{OsStrExt, OsStringExt}; use crate::path::PathBuf; use crate::time::Duration; -pub use self::rand::hashmap_random_keys; - #[macro_use] pub mod compat; @@ -16,6 +14,7 @@ pub mod alloc; pub mod args; pub mod c; pub mod cmath; +pub mod entropy; pub mod env; pub mod fs; pub mod handle; @@ -28,7 +27,6 @@ pub mod os_str; pub mod path; pub mod pipe; pub mod process; -pub mod rand; pub mod stdio; pub mod thread; pub mod thread_local_dtor; diff --git a/library/std/src/sys/windows/pipe.rs b/library/std/src/sys/windows/pipe.rs index 0780b29584da4..3e7e2c97d726f 100644 --- a/library/std/src/sys/windows/pipe.rs +++ b/library/std/src/sys/windows/pipe.rs @@ -11,7 +11,6 @@ use crate::sync::atomic::Ordering::SeqCst; use crate::sys::c; use crate::sys::fs::{File, OpenOptions}; use crate::sys::handle::Handle; -use crate::sys::hashmap_random_keys; use crate::sys_common::IntoInner; //////////////////////////////////////////////////////////////////////////////// @@ -212,7 +211,9 @@ fn random_number() -> usize { return N.fetch_add(1, SeqCst); } - N.store(hashmap_random_keys().0 as usize, SeqCst); + let mut bytes = [0; mem::size_of::()]; + crate::io::entropy().read_exact(&mut bytes).expect("failed to generate random number"); + N.store(usize::from_ne_bytes(bytes), SeqCst); } } diff --git a/library/std/src/sys/windows/rand.rs b/library/std/src/sys/windows/rand.rs deleted file mode 100644 index cdf37cfe9117b..0000000000000 --- a/library/std/src/sys/windows/rand.rs +++ /dev/null @@ -1,39 +0,0 @@ -use crate::io; -use crate::mem; -use crate::ptr; -use crate::sys::c; - -pub fn hashmap_random_keys() -> (u64, u64) { - let mut v = (0, 0); - let ret = unsafe { - c::BCryptGenRandom( - ptr::null_mut(), - &mut v as *mut _ as *mut u8, - mem::size_of_val(&v) as c::ULONG, - c::BCRYPT_USE_SYSTEM_PREFERRED_RNG, - ) - }; - if c::nt_success(ret) { v } else { fallback_rng() } -} - -/// Generate random numbers using the fallback RNG function (RtlGenRandom) -/// -/// This is necessary because of a failure to load the SysWOW64 variant of the -/// bcryptprimitives.dll library from code that lives in bcrypt.dll -/// See -#[cfg(not(target_vendor = "uwp"))] -#[inline(never)] -fn fallback_rng() -> (u64, u64) { - let mut v = (0, 0); - let ret = - unsafe { c::RtlGenRandom(&mut v as *mut _ as *mut u8, mem::size_of_val(&v) as c::ULONG) }; - - if ret != 0 { v } else { panic!("fallback RNG broken: {}", io::Error::last_os_error()) } -} - -/// We can't use RtlGenRandom with UWP, so there is no fallback -#[cfg(target_vendor = "uwp")] -#[inline(never)] -fn fallback_rng() -> (u64, u64) { - panic!("fallback RNG broken: RtlGenRandom() not supported on UWP"); -}