about summary refs log tree commit diff
diff options
context:
space:
mode:
authorjoboet <jonasboettiger@icloud.com>2024-08-15 13:28:02 +0200
committerjoboet <jonasboettiger@icloud.com>2024-09-23 10:29:51 +0200
commit5c1c72572479afe98734d5f78fa862abe662c41a (patch)
tree4f43788d776b0b1dfc423ef8d9852ece3960f9b3
parent702987f75b74f789ba227ee04a3d7bb1680c2309 (diff)
downloadrust-5c1c72572479afe98734d5f78fa862abe662c41a.tar.gz
rust-5c1c72572479afe98734d5f78fa862abe662c41a.zip
std: implement the `random` feature
Implements the ACP https://github.com/rust-lang/libs-team/issues/393.
-rw-r--r--library/core/src/lib.rs2
-rw-r--r--library/core/src/random.rs62
-rw-r--r--library/std/src/hash/random.rs5
-rw-r--r--library/std/src/lib.rs4
-rw-r--r--library/std/src/random.rs100
-rw-r--r--library/std/src/sys/mod.rs1
-rw-r--r--library/std/src/sys/pal/hermit/mod.rs14
-rw-r--r--library/std/src/sys/pal/sgx/abi/usercalls/mod.rs4
-rw-r--r--library/std/src/sys/pal/sgx/mod.rs18
-rw-r--r--library/std/src/sys/pal/solid/mod.rs10
-rw-r--r--library/std/src/sys/pal/teeos/mod.rs3
-rw-r--r--library/std/src/sys/pal/teeos/rand.rs21
-rw-r--r--library/std/src/sys/pal/uefi/mod.rs33
-rw-r--r--library/std/src/sys/pal/unix/mod.rs2
-rw-r--r--library/std/src/sys/pal/unix/rand.rs302
-rw-r--r--library/std/src/sys/pal/unsupported/common.rs4
-rw-r--r--library/std/src/sys/pal/wasi/helpers.rs12
-rw-r--r--library/std/src/sys/pal/wasi/mod.rs2
-rw-r--r--library/std/src/sys/pal/wasip2/mod.rs2
-rw-r--r--library/std/src/sys/pal/windows/mod.rs2
-rw-r--r--library/std/src/sys/pal/windows/pipe.rs7
-rw-r--r--library/std/src/sys/pal/windows/rand.rs27
-rw-r--r--library/std/src/sys/pal/zkvm/mod.rs8
-rw-r--r--library/std/src/sys/random/apple.rs22
-rw-r--r--library/std/src/sys/random/espidf.rs9
-rw-r--r--library/std/src/sys/random/fuchsia.rs13
-rw-r--r--library/std/src/sys/random/hermit.rs7
-rw-r--r--library/std/src/sys/random/horizon.rs7
-rw-r--r--library/std/src/sys/random/linux.rs170
-rw-r--r--library/std/src/sys/random/mod.rs98
-rw-r--r--library/std/src/sys/random/netbsd.rs19
-rw-r--r--library/std/src/sys/random/redox.rs12
-rw-r--r--library/std/src/sys/random/sgx.rs67
-rw-r--r--library/std/src/sys/random/solid.rs8
-rw-r--r--library/std/src/sys/random/teeos.rs7
-rw-r--r--library/std/src/sys/random/uefi.rs27
-rw-r--r--library/std/src/sys/random/unix.rs33
-rw-r--r--library/std/src/sys/random/unix_legacy.rs20
-rw-r--r--library/std/src/sys/random/unsupported.rs15
-rw-r--r--library/std/src/sys/random/vxworks.rs25
-rw-r--r--library/std/src/sys/random/wasi.rs5
-rw-r--r--library/std/src/sys/random/windows.rs20
-rw-r--r--library/std/src/sys/random/zkvm.rs21
43 files changed, 786 insertions, 464 deletions
diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs
index a30b57c19d4..b4393889e75 100644
--- a/library/core/src/lib.rs
+++ b/library/core/src/lib.rs
@@ -394,6 +394,8 @@ pub mod panicking;
 #[unstable(feature = "core_pattern_types", issue = "123646")]
 pub mod pat;
 pub mod pin;
+#[unstable(feature = "random", issue = "none")]
+pub mod random;
 #[unstable(feature = "new_range_api", issue = "125687")]
 pub mod range;
 pub mod result;
diff --git a/library/core/src/random.rs b/library/core/src/random.rs
new file mode 100644
index 00000000000..9e0d0d0c58b
--- /dev/null
+++ b/library/core/src/random.rs
@@ -0,0 +1,62 @@
+//! Random value generation.
+//!
+//! The [`Random`] trait allows generating a random value for a type using a
+//! given [`RandomSource`].
+
+/// A source of randomness.
+#[unstable(feature = "random", issue = "none")]
+pub trait RandomSource {
+    /// Fills `bytes` with random bytes.
+    fn fill_bytes(&mut self, bytes: &mut [u8]);
+}
+
+/// A trait for getting a random value for a type.
+///
+/// **Warning:** Be careful when manipulating random values! The
+/// [`random`](Random::random) method on integers samples them with a uniform
+/// distribution, so a value of 1 is just as likely as [`i32::MAX`]. By using
+/// modulo operations, some of the resulting values can become more likely than
+/// others. Use audited crates when in doubt.
+#[unstable(feature = "random", issue = "none")]
+pub trait Random: Sized {
+    /// Generates a random value.
+    fn random(source: &mut (impl RandomSource + ?Sized)) -> Self;
+}
+
+impl Random for bool {
+    fn random(source: &mut (impl RandomSource + ?Sized)) -> Self {
+        u8::random(source) & 1 == 1
+    }
+}
+
+macro_rules! impl_primitive {
+    ($t:ty) => {
+        impl Random for $t {
+            /// Generates a random value.
+            ///
+            /// **Warning:** Be careful when manipulating the resulting value! This
+            /// method samples according to a uniform distribution, so a value of 1 is
+            /// just as likely as [`MAX`](Self::MAX). By using modulo operations, some
+            /// values can become more likely than others. Use audited crates when in
+            /// doubt.
+            fn random(source: &mut (impl RandomSource + ?Sized)) -> Self {
+                let mut bytes = (0 as Self).to_ne_bytes();
+                source.fill_bytes(&mut bytes);
+                Self::from_ne_bytes(bytes)
+            }
+        }
+    };
+}
+
+impl_primitive!(u8);
+impl_primitive!(i8);
+impl_primitive!(u16);
+impl_primitive!(i16);
+impl_primitive!(u32);
+impl_primitive!(i32);
+impl_primitive!(u64);
+impl_primitive!(i64);
+impl_primitive!(u128);
+impl_primitive!(i128);
+impl_primitive!(usize);
+impl_primitive!(isize);
diff --git a/library/std/src/hash/random.rs b/library/std/src/hash/random.rs
index 8ef45172eac..40f3a90f60c 100644
--- a/library/std/src/hash/random.rs
+++ b/library/std/src/hash/random.rs
@@ -10,7 +10,8 @@
 #[allow(deprecated)]
 use super::{BuildHasher, Hasher, SipHasher13};
 use crate::cell::Cell;
-use crate::{fmt, sys};
+use crate::fmt;
+use crate::sys::random::hashmap_random_keys;
 
 /// `RandomState` is the default state for [`HashMap`] types.
 ///
@@ -65,7 +66,7 @@ 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())
+            Cell::new(hashmap_random_keys())
         });
 
         KEYS.with(|keys| {
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index 2add88da9a7..ce05d816604 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -318,6 +318,7 @@
 //
 // Library features (core):
 // tidy-alphabetical-start
+#![feature(array_chunks)]
 #![feature(c_str_module)]
 #![feature(char_internals)]
 #![feature(clone_to_uninit)]
@@ -348,6 +349,7 @@
 #![feature(prelude_2024)]
 #![feature(ptr_as_uninit)]
 #![feature(ptr_mask)]
+#![feature(random)]
 #![feature(slice_internals)]
 #![feature(slice_ptr_get)]
 #![feature(slice_range)]
@@ -595,6 +597,8 @@ pub mod path;
 #[unstable(feature = "anonymous_pipe", issue = "127154")]
 pub mod pipe;
 pub mod process;
+#[unstable(feature = "random", issue = "none")]
+pub mod random;
 pub mod sync;
 pub mod time;
 
diff --git a/library/std/src/random.rs b/library/std/src/random.rs
new file mode 100644
index 00000000000..24d1dac4dae
--- /dev/null
+++ b/library/std/src/random.rs
@@ -0,0 +1,100 @@
+//! Random value generation.
+//!
+//! The [`Random`] trait allows generating a random value for a type using a
+//! given [`RandomSource`].
+
+#[unstable(feature = "random", issue = "none")]
+pub use core::random::*;
+
+use crate::sys::random as sys;
+
+/// The default random source.
+///
+/// This asks the system for random data suitable for cryptographic purposes
+/// such as key generation. If security is a concern, consult the platform
+/// documentation below for the specific guarantees your target provides.
+///
+/// The high quality of randomness provided by this source means it can be quite
+/// slow. If you need a large quantity of random numbers and security is not a
+/// concern,  consider using an alternative random number generator (potentially
+/// seeded from this one).
+///
+/// # Underlying sources
+///
+/// Platform               | Source
+/// -----------------------|---------------------------------------------------------------
+/// Linux                  | [`getrandom`] or [`/dev/urandom`] after polling `/dev/random`
+/// Windows                | [`ProcessPrng`]
+/// macOS and other UNIXes | [`getentropy`]
+/// other Apple platforms  | `CCRandomGenerateBytes`
+/// ESP-IDF                | [`esp_fill_random`]
+/// Fuchsia                | [`cprng_draw`]
+/// Hermit                 | `read_entropy`
+/// Horizon                | `getrandom` shim
+/// Hurd, L4Re, QNX        | `/dev/urandom`
+/// NetBSD before 10.0     | [`kern.arandom`]
+/// Redox                  | `/scheme/rand`
+/// SGX                    | [`rdrand`]
+/// SOLID                  | `SOLID_RNG_SampleRandomBytes`
+/// TEEOS                  | `TEE_GenerateRandom`
+/// UEFI                   | [`EFI_RNG_PROTOCOL`]
+/// VxWorks                | `randABytes` after waiting for `randSecure` to become ready
+/// WASI                   | `random_get`
+/// ZKVM                   | `sys_rand`
+///
+/// **Disclaimer:** The sources used might change over time.
+///
+/// [`getrandom`]: https://www.man7.org/linux/man-pages/man2/getrandom.2.html
+/// [`/dev/urandom`]: https://www.man7.org/linux/man-pages/man4/random.4.html
+/// [`ProcessPrng`]: https://learn.microsoft.com/en-us/windows/win32/seccng/processprng
+/// [`getentropy`]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/getentropy.html
+/// [`esp_fill_random`]: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/random.html#_CPPv415esp_fill_randomPv6size_t
+/// [`cprng_draw`]: https://fuchsia.dev/reference/syscalls/cprng_draw
+/// [`kern.arandom`]: https://man.netbsd.org/rnd.4
+/// [`rdrand`]: https://en.wikipedia.org/wiki/RDRAND
+/// [`EFI_RNG_PROTOCOL`]: https://uefi.org/specs/UEFI/2.10/37_Secure_Technologies.html#random-number-generator-protocol
+#[derive(Default, Debug, Clone, Copy)]
+#[unstable(feature = "random", issue = "none")]
+pub struct DefaultRandomSource;
+
+#[unstable(feature = "random", issue = "none")]
+impl RandomSource for DefaultRandomSource {
+    fn fill_bytes(&mut self, bytes: &mut [u8]) {
+        sys::fill_bytes(bytes)
+    }
+}
+
+/// Generates a random value with the default random source.
+///
+/// This is a convenience function for `T::random(&mut DefaultRandomSource)` and
+/// will sample according to the same distribution as the underlying [`Random`]
+/// trait implementation.
+///
+/// **Warning:** Be careful when manipulating random values! The
+/// [`random`](Random::random) method on integers samples them with a uniform
+/// distribution, so a value of 1 is just as likely as [`i32::MAX`]. By using
+/// modulo operations, some of the resulting values can become more likely than
+/// others. Use audited crates when in doubt.
+///
+/// # Examples
+///
+/// Generating a [version 4/variant 1 UUID] represented as text:
+/// ```
+/// #![feature(random)]
+///
+/// use std::random::random;
+///
+/// let bits = random::<u128>();
+/// let g1 = (bits >> 96) as u32;
+/// let g2 = (bits >> 80) as u16;
+/// let g3 = (0x4000 | (bits >> 64) & 0x0fff) as u16;
+/// let g4 = (0x8000 | (bits >> 48) & 0x3fff) as u16;
+/// let g5 = (bits & 0xffffffffffff) as u64;
+/// let uuid = format!("{g1:08x}-{g2:04x}-{g3:04x}-{g4:04x}-{g5:012x}");
+/// println!("{uuid}");
+/// ```
+///
+/// [version 4/variant 1 UUID]: https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)
+pub fn random<T: Random>() -> T {
+    T::random(&mut DefaultRandomSource)
+}
diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs
index 96d6f2c87c4..df25b84fbbe 100644
--- a/library/std/src/sys/mod.rs
+++ b/library/std/src/sys/mod.rs
@@ -15,6 +15,7 @@ pub mod dbg;
 pub mod exit_guard;
 pub mod os_str;
 pub mod path;
+pub mod random;
 pub mod sync;
 pub mod thread_local;
 
diff --git a/library/std/src/sys/pal/hermit/mod.rs b/library/std/src/sys/pal/hermit/mod.rs
index 1f2e5d9469f..f49ef947174 100644
--- a/library/std/src/sys/pal/hermit/mod.rs
+++ b/library/std/src/sys/pal/hermit/mod.rs
@@ -52,20 +52,6 @@ pub fn abort_internal() -> ! {
     unsafe { hermit_abi::abort() }
 }
 
-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 { hermit_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/pal/sgx/abi/usercalls/mod.rs b/library/std/src/sys/pal/sgx/abi/usercalls/mod.rs
index def1ccdf81a..90b9b59471a 100644
--- a/library/std/src/sys/pal/sgx/abi/usercalls/mod.rs
+++ b/library/std/src/sys/pal/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::random::{DefaultRandomSource, Random};
 use crate::time::{Duration, Instant};
 
 pub(crate) mod alloc;
@@ -164,7 +164,7 @@ pub fn wait(event_mask: u64, mut timeout: u64) -> IoResult<u64> {
         // 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 deviation = i64::random(&mut DefaultRandomSource).checked_rem(tenth).unwrap_or(0);
             timeout = timeout_signed.saturating_add(deviation) as _;
         }
     }
diff --git a/library/std/src/sys/pal/sgx/mod.rs b/library/std/src/sys/pal/sgx/mod.rs
index 8d29b2ec619..586ccd18c2f 100644
--- a/library/std/src/sys/pal/sgx/mod.rs
+++ b/library/std/src/sys/pal/sgx/mod.rs
@@ -132,24 +132,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<Inner>: Sized {
diff --git a/library/std/src/sys/pal/solid/mod.rs b/library/std/src/sys/pal/solid/mod.rs
index 6ebcf5b7c48..d41042be518 100644
--- a/library/std/src/sys/pal/solid/mod.rs
+++ b/library/std/src/sys/pal/solid/mod.rs
@@ -62,13 +62,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/pal/teeos/mod.rs b/library/std/src/sys/pal/teeos/mod.rs
index 00e38604240..60a227afb84 100644
--- a/library/std/src/sys/pal/teeos/mod.rs
+++ b/library/std/src/sys/pal/teeos/mod.rs
@@ -6,8 +6,6 @@
 #![allow(unused_variables)]
 #![allow(dead_code)]
 
-pub use self::rand::hashmap_random_keys;
-
 #[path = "../unsupported/args.rs"]
 pub mod args;
 #[path = "../unsupported/env.rs"]
@@ -23,7 +21,6 @@ pub mod os;
 pub mod pipe;
 #[path = "../unsupported/process.rs"]
 pub mod process;
-mod rand;
 pub mod stdio;
 pub mod thread;
 #[allow(non_upper_case_globals)]
diff --git a/library/std/src/sys/pal/teeos/rand.rs b/library/std/src/sys/pal/teeos/rand.rs
deleted file mode 100644
index b45c3bb40e7..00000000000
--- a/library/std/src/sys/pal/teeos/rand.rs
+++ /dev/null
@@ -1,21 +0,0 @@
-pub fn hashmap_random_keys() -> (u64, u64) {
-    const KEY_LEN: usize = core::mem::size_of::<u64>();
-
-    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))
-}
-
-mod imp {
-    extern "C" {
-        fn TEE_GenerateRandom(randomBuffer: *mut core::ffi::c_void, randomBufferLen: libc::size_t);
-    }
-
-    pub fn fill_bytes(v: &mut [u8]) {
-        unsafe { TEE_GenerateRandom(v.as_mut_ptr() as _, v.len() * crate::mem::size_of::<u8>()) }
-    }
-}
diff --git a/library/std/src/sys/pal/uefi/mod.rs b/library/std/src/sys/pal/uefi/mod.rs
index ac22f4ded88..c0ab52f650a 100644
--- a/library/std/src/sys/pal/uefi/mod.rs
+++ b/library/std/src/sys/pal/uefi/mod.rs
@@ -179,39 +179,6 @@ pub extern "C" fn __rust_abort() {
     abort_internal();
 }
 
-#[inline]
-pub fn hashmap_random_keys() -> (u64, u64) {
-    get_random().unwrap()
-}
-
-fn get_random() -> Option<(u64, u64)> {
-    use r_efi::protocols::rng;
-
-    let mut buf = [0u8; 16];
-    let handles = helpers::locate_handles(rng::PROTOCOL_GUID).ok()?;
-    for handle in handles {
-        if let Ok(protocol) = helpers::open_protocol::<rng::Protocol>(handle, rng::PROTOCOL_GUID) {
-            let r = unsafe {
-                ((*protocol.as_ptr()).get_rng)(
-                    protocol.as_ptr(),
-                    crate::ptr::null_mut(),
-                    buf.len(),
-                    buf.as_mut_ptr(),
-                )
-            };
-            if r.is_error() {
-                continue;
-            } else {
-                return Some((
-                    u64::from_le_bytes(buf[..8].try_into().ok()?),
-                    u64::from_le_bytes(buf[8..].try_into().ok()?),
-                ));
-            }
-        }
-    }
-    None
-}
-
 /// Disable access to BootServices if `EVT_SIGNAL_EXIT_BOOT_SERVICES` is signaled
 extern "efiapi" fn exit_boot_service_handler(_e: r_efi::efi::Event, _ctx: *mut crate::ffi::c_void) {
     uefi::env::disable_boot_services();
diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs
index e8428eccb16..1c9159e5fba 100644
--- a/library/std/src/sys/pal/unix/mod.rs
+++ b/library/std/src/sys/pal/unix/mod.rs
@@ -1,6 +1,5 @@
 #![allow(missing_docs, nonstandard_style)]
 
-pub use self::rand::hashmap_random_keys;
 use crate::io::ErrorKind;
 
 #[cfg(not(target_os = "espidf"))]
@@ -26,7 +25,6 @@ pub use self::l4re::net;
 pub mod os;
 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/pal/unix/rand.rs b/library/std/src/sys/pal/unix/rand.rs
deleted file mode 100644
index cc0852aab43..00000000000
--- a/library/std/src/sys/pal/unix/rand.rs
+++ /dev/null
@@ -1,302 +0,0 @@
-pub fn hashmap_random_keys() -> (u64, u64) {
-    const KEY_LEN: usize = core::mem::size_of::<u64>();
-
-    let mut v = [0u8; KEY_LEN * 2];
-    if let Err(err) = read(&mut v) {
-        panic!("failed to retrieve random hash map seed: {err}");
-    }
-
-    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_if::cfg_if! {
-    if #[cfg(any(
-        target_vendor = "apple",
-        target_os = "openbsd",
-        target_os = "emscripten",
-        target_os = "vita",
-        all(target_os = "netbsd", not(netbsd10)),
-        target_os = "fuchsia",
-        target_os = "vxworks",
-    ))] {
-        // Some systems have a syscall that directly retrieves random data.
-        // If that is guaranteed to be available, use it.
-        use imp::syscall as read;
-    } else {
-        // Otherwise, try the syscall to see if it exists only on some systems
-        // and fall back to reading from the random device otherwise.
-        fn read(bytes: &mut [u8]) -> crate::io::Result<()> {
-            use crate::fs::File;
-            use crate::io::Read;
-            use crate::sync::OnceLock;
-
-            #[cfg(any(
-                target_os = "linux",
-                target_os = "android",
-                target_os = "espidf",
-                target_os = "horizon",
-                target_os = "freebsd",
-                target_os = "dragonfly",
-                target_os = "solaris",
-                target_os = "illumos",
-                netbsd10,
-            ))]
-            if let Some(res) = imp::syscall(bytes) {
-                return res;
-            }
-
-            const PATH: &'static str = if cfg!(target_os = "redox") {
-                "/scheme/rand"
-            } else {
-                "/dev/urandom"
-            };
-
-            static FILE: OnceLock<File> = OnceLock::new();
-
-            FILE.get_or_try_init(|| File::open(PATH))?.read_exact(bytes)
-        }
-    }
-}
-
-// All these systems a `getrandom` syscall.
-//
-// It is not guaranteed to be available, so return None to fallback to the file
-// implementation.
-#[cfg(any(
-    target_os = "linux",
-    target_os = "android",
-    target_os = "espidf",
-    target_os = "horizon",
-    target_os = "freebsd",
-    target_os = "dragonfly",
-    target_os = "solaris",
-    target_os = "illumos",
-    netbsd10,
-))]
-mod imp {
-    use crate::io::{Error, Result};
-    use crate::sync::atomic::{AtomicBool, Ordering};
-    use crate::sys::os::errno;
-
-    #[cfg(any(target_os = "linux", target_os = "android"))]
-    fn getrandom(buf: &mut [u8]) -> libc::ssize_t {
-        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
-        }
-
-        // 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 = "dragonfly",
-        target_os = "espidf",
-        target_os = "horizon",
-        target_os = "freebsd",
-        netbsd10,
-        target_os = "illumos",
-        target_os = "solaris"
-    ))]
-    fn getrandom(buf: &mut [u8]) -> libc::ssize_t {
-        unsafe { libc::getrandom(buf.as_mut_ptr().cast(), buf.len(), 0) }
-    }
-
-    pub fn syscall(v: &mut [u8]) -> Option<Result<()>> {
-        static GETRANDOM_UNAVAILABLE: AtomicBool = AtomicBool::new(false);
-
-        if GETRANDOM_UNAVAILABLE.load(Ordering::Relaxed) {
-            return None;
-        }
-
-        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 {
-                    // `getrandom` is not supported on the current system.
-                    //
-                    // Also fall back in case it is disabled by something like
-                    // seccomp or inside of docker.
-                    //
-                    // If the `getrandom` syscall is not implemented in the current kernel version it should return an
-                    // `ENOSYS` error. Docker also blocks the whole syscall inside unprivileged containers, and
-                    // returns `EPERM` (instead of `ENOSYS`) when a program tries to invoke the syscall. Because of
-                    // that we need to check for *both* `ENOSYS` and `EPERM`.
-                    //
-                    // Note that Docker's behavior is breaking other projects (notably glibc), so they're planning
-                    // to update their filtering to return `ENOSYS` in a future release:
-                    //
-                    //     https://github.com/moby/moby/issues/42680
-                    //
-                    GETRANDOM_UNAVAILABLE.store(true, Ordering::Relaxed);
-                    return None;
-                } else if err == libc::EAGAIN {
-                    // getrandom has failed because it would have blocked as the
-                    // non-blocking pool (urandom) has not been initialized in
-                    // the kernel yet due to a lack of entropy. Fallback to
-                    // reading from `/dev/urandom` which will return potentially
-                    // insecure random data to avoid blocking applications which
-                    // could depend on this call without ever knowing they do and
-                    // don't have a work around.
-                    return None;
-                } else {
-                    return Some(Err(Error::from_raw_os_error(err)));
-                }
-            } else {
-                read += result as usize;
-            }
-        }
-
-        Some(Ok(()))
-    }
-}
-
-#[cfg(any(
-    target_os = "macos", // Supported since macOS 10.12+.
-    target_os = "openbsd",
-    target_os = "emscripten",
-    target_os = "vita",
-))]
-mod imp {
-    use crate::io::{Error, Result};
-
-    pub fn syscall(v: &mut [u8]) -> Result<()> {
-        // 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().cast(), s.len()) };
-            if ret == -1 {
-                return Err(Error::last_os_error());
-            }
-        }
-
-        Ok(())
-    }
-}
-
-// On Apple platforms, `CCRandomGenerateBytes` and `SecRandomCopyBytes` simply
-// call into `CCRandomCopyBytes` with `kCCRandomDefault`. `CCRandomCopyBytes`
-// manages a CSPRNG which is seeded from the kernel's CSPRNG and which runs on
-// its own thread accessed via GCD. This seems needlessly heavyweight for our purposes
-// so we only use it when `getentropy` is blocked, which appears to be the case
-// on all platforms except macOS (see #102643).
-//
-// `CCRandomGenerateBytes` is used instead of `SecRandomCopyBytes` because the former is accessible
-// via `libSystem` (libc) while the other needs to link to `Security.framework`.
-#[cfg(all(target_vendor = "apple", not(target_os = "macos")))]
-mod imp {
-    use libc::size_t;
-
-    use crate::ffi::{c_int, c_void};
-    use crate::io::{Error, Result};
-
-    pub fn syscall(v: &mut [u8]) -> Result<()> {
-        extern "C" {
-            fn CCRandomGenerateBytes(bytes: *mut c_void, count: size_t) -> c_int;
-        }
-
-        let ret = unsafe { CCRandomGenerateBytes(v.as_mut_ptr().cast(), v.len()) };
-        if ret != -1 { Ok(()) } else { Err(Error::last_os_error()) }
-    }
-}
-
-// FIXME: once the 10.x release becomes the minimum, this can be dropped for simplification.
-#[cfg(all(target_os = "netbsd", not(netbsd10)))]
-mod imp {
-    use crate::io::{Error, Result};
-    use crate::ptr;
-
-    pub fn syscall(v: &mut [u8]) -> Result<()> {
-        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 {
-                return Err(Error::last_os_error());
-            } else if s_len != s.len() {
-                // FIXME(joboet): this can't actually happen, can it?
-                panic!("read less bytes than requested from kern.arandom");
-            }
-        }
-
-        Ok(())
-    }
-}
-
-#[cfg(target_os = "fuchsia")]
-mod imp {
-    use crate::io::Result;
-
-    #[link(name = "zircon")]
-    extern "C" {
-        fn zx_cprng_draw(buffer: *mut u8, len: usize);
-    }
-
-    pub fn syscall(v: &mut [u8]) -> Result<()> {
-        unsafe { zx_cprng_draw(v.as_mut_ptr(), v.len()) };
-        Ok(())
-    }
-}
-
-#[cfg(target_os = "vxworks")]
-mod imp {
-    use core::sync::atomic::AtomicBool;
-    use core::sync::atomic::Ordering::Relaxed;
-
-    use crate::io::{Error, Result};
-
-    pub fn syscall(v: &mut [u8]) -> Result<()> {
-        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 ret = unsafe {
-            libc::randABytes(v.as_mut_ptr() as *mut libc::c_uchar, v.len() as libc::c_int)
-        };
-        if ret >= 0 { Ok(()) } else { Err(Error::last_os_error()) }
-    }
-}
diff --git a/library/std/src/sys/pal/unsupported/common.rs b/library/std/src/sys/pal/unsupported/common.rs
index 76f80291f0e..34a76668383 100644
--- a/library/std/src/sys/pal/unsupported/common.rs
+++ b/library/std/src/sys/pal/unsupported/common.rs
@@ -27,7 +27,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/pal/wasi/helpers.rs b/library/std/src/sys/pal/wasi/helpers.rs
index 37ef17858cb..404747f0dc7 100644
--- a/library/std/src/sys/pal/wasi/helpers.rs
+++ b/library/std/src/sys/pal/wasi/helpers.rs
@@ -1,6 +1,6 @@
 #![forbid(unsafe_op_in_unsafe_fn)]
 
-use crate::{io as std_io, mem};
+use crate::io as std_io;
 
 #[inline]
 pub fn is_interrupted(errno: i32) -> bool {
@@ -108,16 +108,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");
-    }
-    ret
-}
-
 #[inline]
 pub(crate) 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/pal/wasi/mod.rs b/library/std/src/sys/pal/wasi/mod.rs
index 8051021a588..5d54c790306 100644
--- a/library/std/src/sys/pal/wasi/mod.rs
+++ b/library/std/src/sys/pal/wasi/mod.rs
@@ -47,4 +47,4 @@ mod helpers;
 // then the compiler complains about conflicts.
 
 use helpers::err2io;
-pub use helpers::{abort_internal, decode_error_kind, hashmap_random_keys, is_interrupted};
+pub use helpers::{abort_internal, decode_error_kind, is_interrupted};
diff --git a/library/std/src/sys/pal/wasip2/mod.rs b/library/std/src/sys/pal/wasip2/mod.rs
index 546fadbe501..17b26543bd7 100644
--- a/library/std/src/sys/pal/wasip2/mod.rs
+++ b/library/std/src/sys/pal/wasip2/mod.rs
@@ -50,6 +50,6 @@ mod helpers;
 // then the compiler complains about conflicts.
 
 use helpers::err2io;
-pub use helpers::{abort_internal, decode_error_kind, hashmap_random_keys, is_interrupted};
+pub use helpers::{abort_internal, decode_error_kind, is_interrupted};
 
 mod cabi_realloc;
diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs
index f5ed3e4628e..1ea253e5e52 100644
--- a/library/std/src/sys/pal/windows/mod.rs
+++ b/library/std/src/sys/pal/windows/mod.rs
@@ -1,7 +1,6 @@
 #![allow(missing_docs, nonstandard_style)]
 #![forbid(unsafe_op_in_unsafe_fn)]
 
-pub use self::rand::hashmap_random_keys;
 use crate::ffi::{OsStr, OsString};
 use crate::io::ErrorKind;
 use crate::mem::MaybeUninit;
@@ -27,7 +26,6 @@ pub mod net;
 pub mod os;
 pub mod pipe;
 pub mod process;
-pub mod rand;
 pub mod stdio;
 pub mod thread;
 pub mod time;
diff --git a/library/std/src/sys/pal/windows/pipe.rs b/library/std/src/sys/pal/windows/pipe.rs
index 7d1b5aca1d5..d8200ef9ca4 100644
--- a/library/std/src/sys/pal/windows/pipe.rs
+++ b/library/std/src/sys/pal/windows/pipe.rs
@@ -2,12 +2,13 @@ use crate::ffi::OsStr;
 use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
 use crate::os::windows::prelude::*;
 use crate::path::Path;
+use crate::random::{DefaultRandomSource, Random};
 use crate::sync::atomic::AtomicUsize;
 use crate::sync::atomic::Ordering::Relaxed;
+use crate::sys::c;
 use crate::sys::fs::{File, OpenOptions};
 use crate::sys::handle::Handle;
 use crate::sys::pal::windows::api::{self, WinError};
-use crate::sys::{c, hashmap_random_keys};
 use crate::sys_common::{FromInner, IntoInner};
 use crate::{mem, ptr};
 
@@ -79,7 +80,7 @@ pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Res
             name = format!(
                 r"\\.\pipe\__rust_anonymous_pipe1__.{}.{}",
                 c::GetCurrentProcessId(),
-                random_number()
+                random_number(),
             );
             let wide_name = OsStr::new(&name).encode_wide().chain(Some(0)).collect::<Vec<_>>();
             let mut flags = c::FILE_FLAG_FIRST_PIPE_INSTANCE | c::FILE_FLAG_OVERLAPPED;
@@ -214,7 +215,7 @@ fn random_number() -> usize {
             return N.fetch_add(1, Relaxed);
         }
 
-        N.store(hashmap_random_keys().0 as usize, Relaxed);
+        N.store(usize::random(&mut DefaultRandomSource), Relaxed);
     }
 }
 
diff --git a/library/std/src/sys/pal/windows/rand.rs b/library/std/src/sys/pal/windows/rand.rs
deleted file mode 100644
index e366bb99562..00000000000
--- a/library/std/src/sys/pal/windows/rand.rs
+++ /dev/null
@@ -1,27 +0,0 @@
-use core::{mem, ptr};
-
-use crate::sys::c;
-
-#[cfg(not(target_vendor = "win7"))]
-#[inline]
-pub fn hashmap_random_keys() -> (u64, u64) {
-    let mut v = (0, 0);
-    let ret = unsafe { c::ProcessPrng(ptr::addr_of_mut!(v).cast::<u8>(), mem::size_of_val(&v)) };
-    // ProcessPrng is documented as always returning `TRUE`.
-    // https://learn.microsoft.com/en-us/windows/win32/seccng/processprng#return-value
-    debug_assert_eq!(ret, c::TRUE);
-    v
-}
-
-#[cfg(target_vendor = "win7")]
-pub fn hashmap_random_keys() -> (u64, u64) {
-    use crate::ffi::c_void;
-    use crate::io;
-
-    let mut v = (0, 0);
-    let ret = unsafe {
-        c::RtlGenRandom(ptr::addr_of_mut!(v).cast::<c_void>(), mem::size_of_val(&v) as u32)
-    };
-
-    if ret != 0 { v } else { panic!("RNG broken: {}", io::Error::last_os_error()) }
-}
diff --git a/library/std/src/sys/pal/zkvm/mod.rs b/library/std/src/sys/pal/zkvm/mod.rs
index 20fdb7468a4..6ea05772029 100644
--- a/library/std/src/sys/pal/zkvm/mod.rs
+++ b/library/std/src/sys/pal/zkvm/mod.rs
@@ -60,11 +60,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) {
-    let mut buf = [0u32; 4];
-    unsafe {
-        abi::sys_rand(buf.as_mut_ptr(), 4);
-    };
-    ((buf[0] as u64) << 32 + buf[1] as u64, (buf[2] as u64) << 32 + buf[3] as u64)
-}
diff --git a/library/std/src/sys/random/apple.rs b/library/std/src/sys/random/apple.rs
new file mode 100644
index 00000000000..09b6d0d51ab
--- /dev/null
+++ b/library/std/src/sys/random/apple.rs
@@ -0,0 +1,22 @@
+//! Random data on non-macOS Apple platforms.
+//!
+//! Apple recommends the usage of `getentropy` in their security documentation[^1]
+//! and mark it as being available in iOS 10.0, but we cannot use it on non-macOS
+//! platforms as Apple in their *infinite wisdom* decided to consider this API
+//! private, meaning its use will lead to App Store rejections (see #102643).
+//!
+//! Thus, we need to do the next best thing:
+//!
+//! Both `CCRandomGenerateBytes` and `SecRandomCopyBytes` simply call into
+//! `CCRandomCopyBytes` with `kCCRandomDefault`. `CCRandomCopyBytes` manages a
+//! CSPRNG which is seeded from the kernel's CSPRNG and which runs on its own
+//! thread accessed via GCD (this is so wasteful...). Both are available on
+//! iOS, but we use `CCRandomGenerateBytes` because it is accessible via
+//! `libSystem` (libc) while the other needs to link to `Security.framework`.
+//!
+//! [^1]: <https://support.apple.com/en-gb/guide/security/seca0c73a75b/web>
+
+pub fn fill_bytes(bytes: &mut [u8]) {
+    let ret = unsafe { libc::CCRandomGenerateBytes(bytes.as_mut_ptr().cast(), bytes.len()) };
+    assert_eq!(ret, libc::kCCSuccess, "failed to generate random data");
+}
diff --git a/library/std/src/sys/random/espidf.rs b/library/std/src/sys/random/espidf.rs
new file mode 100644
index 00000000000..fd52cb5559c
--- /dev/null
+++ b/library/std/src/sys/random/espidf.rs
@@ -0,0 +1,9 @@
+use crate::ffi::c_void;
+
+extern "C" {
+    fn esp_fill_random(buf: *mut c_void, len: usize);
+}
+
+pub fn fill_bytes(bytes: &mut [u8]) {
+    unsafe { esp_fill_random(bytes.as_mut_ptr().cast(), bytes.len()) }
+}
diff --git a/library/std/src/sys/random/fuchsia.rs b/library/std/src/sys/random/fuchsia.rs
new file mode 100644
index 00000000000..77d72b3c5b7
--- /dev/null
+++ b/library/std/src/sys/random/fuchsia.rs
@@ -0,0 +1,13 @@
+//! Random data generation using the Zircon kernel.
+//!
+//! Fuchsia, as always, is quite nice and provides exactly the API we need:
+//! <https://fuchsia.dev/reference/syscalls/cprng_draw>.
+
+#[link(name = "zircon")]
+extern "C" {
+    fn zx_cprng_draw(buffer: *mut u8, len: usize);
+}
+
+pub fn fill_bytes(bytes: &mut [u8]) {
+    unsafe { zx_cprng_draw(bytes.as_mut_ptr(), bytes.len()) }
+}
diff --git a/library/std/src/sys/random/hermit.rs b/library/std/src/sys/random/hermit.rs
new file mode 100644
index 00000000000..92c0550d2d5
--- /dev/null
+++ b/library/std/src/sys/random/hermit.rs
@@ -0,0 +1,7 @@
+pub fn fill_bytes(mut bytes: &mut [u8]) {
+    while !bytes.is_empty() {
+        let res = unsafe { hermit_abi::read_entropy(bytes.as_mut_ptr(), bytes.len(), 0) };
+        assert_ne!(res, -1, "failed to generate random data");
+        bytes = &mut bytes[res as usize..];
+    }
+}
diff --git a/library/std/src/sys/random/horizon.rs b/library/std/src/sys/random/horizon.rs
new file mode 100644
index 00000000000..0be2eae20a7
--- /dev/null
+++ b/library/std/src/sys/random/horizon.rs
@@ -0,0 +1,7 @@
+pub fn fill_bytes(mut bytes: &mut [u8]) {
+    while !bytes.is_empty() {
+        let r = unsafe { libc::getrandom(bytes.as_mut_ptr().cast(), bytes.len(), 0) };
+        assert_ne!(r, -1, "failed to generate random data");
+        bytes = &mut bytes[r as usize..];
+    }
+}
diff --git a/library/std/src/sys/random/linux.rs b/library/std/src/sys/random/linux.rs
new file mode 100644
index 00000000000..4ede0af6494
--- /dev/null
+++ b/library/std/src/sys/random/linux.rs
@@ -0,0 +1,170 @@
+//! Random data generation with the Linux kernel.
+//!
+//! The first interface random data interface to be introduced on Linux were
+//! the `/dev/random` and `/dev/urandom` special files. As paths can become
+//! unreachable when inside a chroot and when the file descriptors are exhausted,
+//! this was not enough to provide userspace with a reliable source of randomness,
+//! so when the OpenBSD 5.6 introduced the `getentropy` syscall, Linux 3.17 got
+//! its very own `getrandom`  syscall to match.[^1] Unfortunately, even if our
+//! minimum supported version were high enough, we still couldn't rely on the
+//! syscall being available, as it is blocked in `seccomp` by default.
+//!
+//! The question is therefore which of the random sources to use. Historically,
+//! the kernel contained two pools: the blocking and non-blocking pool. The
+//! blocking pool used entropy estimation to limit the amount of available
+//! bytes, while the non-blocking pool, once initialized using the blocking
+//! pool, uses a CPRNG to return an unlimited number of random bytes. With a
+//! strong enough CPRNG however, the entropy estimation didn't contribute that
+//! much towards security while being an excellent vector for DoS attacs. Thus,
+//! the blocking pool was removed in kernel version 5.6.[^2] That patch did not
+//! magically increase the quality of the non-blocking pool, however, so we can
+//! safely consider it strong enough even in older kernel versions and use it
+//! unconditionally.
+//!
+//! One additional consideration to make is that the non-blocking pool is not
+//! always initialized during early boot. We want the best quality of randomness
+//! for the output of `DefaultRandomSource` so we simply wait until it is
+//! initialized. When `HashMap` keys however, this represents a potential source
+//! of deadlocks, as the additional entropy may only be generated once the
+//! program makes forward progress. In that case, we just use the best random
+//! data the system has available at the time.
+//!
+//! So in conclusion, we always want the output of the non-blocking pool, but
+//! may need to wait until it is initalized. The default behaviour of `getrandom`
+//! is to wait until the non-blocking pool is initialized and then draw from there,
+//! so if `getrandom` is available, we use its default to generate the bytes. For
+//! `HashMap`, however, we need to specify the `GRND_INSECURE` flags, but that
+//! is only available starting with kernel version 5.6. Thus, if we detect that
+//! the flag is unsupported, we try `GRND_NONBLOCK` instead, which will only
+//! succeed if the pool is initialized. If it isn't, we fall back to the file
+//! access method.
+//!
+//! The behaviour of `/dev/urandom` is inverse to that of `getrandom`: it always
+//! yields data, even when the pool is not initialized. For generating `HashMap`
+//! keys, this is not important, so we can use it directly. For secure data
+//! however, we need to wait until initialization, which we can do by `poll`ing
+//! `/dev/random`.
+//!
+//! TLDR: our fallback strategies are:
+//!
+//! Secure data                                 | `HashMap` keys
+//! --------------------------------------------|------------------
+//! getrandom(0)                                | getrandom(GRND_INSECURE)
+//! poll("/dev/random") && read("/dev/urandom") | getrandom(GRND_NONBLOCK)
+//!                                             | read("/dev/urandom")
+//!
+//! [^1]: <https://lwn.net/Articles/606141/>
+//! [^2]: <https://lwn.net/Articles/808575/>
+//!
+// FIXME(in 2040 or so): once the minimum kernel version is 5.6, remove the
+// `GRND_NONBLOCK` fallback and use `/dev/random` instead of `/dev/urandom`
+// when secure data is required.
+
+use crate::fs::File;
+use crate::io::Read;
+use crate::os::fd::AsRawFd;
+use crate::sync::atomic::AtomicBool;
+use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release};
+use crate::sync::OnceLock;
+use crate::sys::pal::os::errno;
+use crate::sys::pal::weak::syscall;
+
+fn getrandom(mut bytes: &mut [u8], insecure: bool) {
+    // 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 GETRANDOM_AVAILABLE: AtomicBool = AtomicBool::new(true);
+    static GRND_INSECURE_AVAILABLE: AtomicBool = AtomicBool::new(true);
+    static URANDOM_READY: AtomicBool = AtomicBool::new(false);
+    static DEVICE: OnceLock<File> = OnceLock::new();
+
+    if GETRANDOM_AVAILABLE.load(Relaxed) {
+        loop {
+            if bytes.is_empty() {
+                return;
+            }
+
+            let flags = if insecure {
+                if GRND_INSECURE_AVAILABLE.load(Relaxed) {
+                    libc::GRND_INSECURE
+                } else {
+                    libc::GRND_NONBLOCK
+                }
+            } else {
+                0
+            };
+
+            let ret = unsafe { getrandom(bytes.as_mut_ptr().cast(), bytes.len(), flags) };
+            if ret != -1 {
+                bytes = &mut bytes[ret as usize..];
+            } else {
+                match errno() {
+                    libc::EINTR => continue,
+                    // `GRND_INSECURE` is not available, try
+                    // `GRND_NONBLOCK`.
+                    libc::EINVAL if flags == libc::GRND_INSECURE => {
+                        GRND_INSECURE_AVAILABLE.store(false, Relaxed);
+                        continue;
+                    }
+                    // The pool is not initialized yet, fall back to
+                    // /dev/urandom for now.
+                    libc::EAGAIN if flags == libc::GRND_NONBLOCK => break,
+                    // `getrandom` is unavailable or blocked by seccomp.
+                    // Don't try it again and fall back to /dev/urandom.
+                    libc::ENOSYS | libc::EPERM => {
+                        GETRANDOM_AVAILABLE.store(false, Relaxed);
+                        break;
+                    }
+                    _ => panic!("failed to generate random data"),
+                }
+            }
+        }
+    }
+
+    // When we want cryptographic strength, we need to wait for the CPRNG-pool
+    // to become initialized. Do this by polling `/dev/random` until it is ready.
+    if !insecure {
+        if !URANDOM_READY.load(Acquire) {
+            let random = File::open("/dev/random").expect("failed to open /dev/random");
+            let mut fd = libc::pollfd { fd: random.as_raw_fd(), events: libc::POLLIN, revents: 0 };
+
+            while !URANDOM_READY.load(Acquire) {
+                let ret = unsafe { libc::poll(&mut fd, 1, -1) };
+                match ret {
+                    1 => {
+                        assert_eq!(fd.revents, libc::POLLIN);
+                        URANDOM_READY.store(true, Release);
+                        break;
+                    }
+                    -1 if errno() == libc::EINTR => continue,
+                    _ => panic!("poll(\"/dev/random\") failed"),
+                }
+            }
+        }
+    }
+
+    DEVICE
+        .get_or_try_init(|| File::open("/dev/urandom"))
+        .and_then(|mut dev| dev.read_exact(bytes))
+        .expect("failed to generate random data");
+}
+
+pub fn fill_bytes(bytes: &mut [u8]) {
+    getrandom(bytes, false);
+}
+
+pub fn hashmap_random_keys() -> (u64, u64) {
+    let mut bytes = [0; 16];
+    getrandom(&mut bytes, true);
+    let k1 = u64::from_ne_bytes(bytes[..8].try_into().unwrap());
+    let k2 = u64::from_ne_bytes(bytes[8..].try_into().unwrap());
+    (k1, k2)
+}
diff --git a/library/std/src/sys/random/mod.rs b/library/std/src/sys/random/mod.rs
new file mode 100644
index 00000000000..d39e78bb7b3
--- /dev/null
+++ b/library/std/src/sys/random/mod.rs
@@ -0,0 +1,98 @@
+cfg_if::cfg_if! {
+    // Tier 1
+    if #[cfg(any(target_os = "linux", target_os = "android"))] {
+        mod linux;
+        pub use linux::{fill_bytes, hashmap_random_keys};
+    } else if #[cfg(target_os = "windows")] {
+        mod windows;
+        pub use windows::fill_bytes;
+    } else if #[cfg(any(
+        target_os = "openbsd",
+        target_os = "freebsd",
+        target_os = "macos",
+        all(target_os = "netbsd", netbsd10),
+        target_os = "dragonfly",
+        target_os = "illumos",
+        target_os = "solaris",
+        target_os = "emscripten",
+        target_os = "vita",
+        target_os = "haiku",
+    ))] {
+        mod unix;
+        pub use unix::fill_bytes;
+    // Others, in alphabetical ordering.
+    } else if #[cfg(all(target_vendor = "apple", not(target_os = "macos")))] {
+        mod apple;
+        pub use apple::fill_bytes;
+    } else if #[cfg(target_os = "espidf")] {
+        mod espidf;
+        pub use espidf::fill_bytes;
+    } else if #[cfg(target_os = "fuchsia")] {
+        mod fuchsia;
+        pub use fuchsia::fill_bytes;
+    } else if #[cfg(target_os = "hermit")] {
+        mod hermit;
+        pub use hermit::fill_bytes;
+    } else if #[cfg(target_os = "horizon")] {
+        // FIXME: add getentropy to shim-3ds
+        mod horizon;
+        pub use horizon::fill_bytes;
+    } else if #[cfg(any(
+        target_os = "hurd",
+        target_os = "l4re",
+        target_os = "nto",
+    ))] {
+        mod unix_legacy;
+        pub use unix_legacy::fill_bytes;
+    } else if #[cfg(all(target_os = "netbsd", not(netbsd10)))] {
+        // FIXME: remove once NetBSD 10 is the minimum
+        mod netbsd;
+        pub use netbsd::fill_bytes;
+    } else if #[cfg(target_os = "redox")] {
+        mod redox;
+        pub use redox::fill_bytes;
+    } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] {
+        mod sgx;
+        pub use sgx::fill_bytes;
+    } else if #[cfg(target_os = "solid_asp3")] {
+        mod solid;
+        pub use solid::fill_bytes;
+    } else if #[cfg(target_os = "teeos")] {
+        mod teeos;
+        pub use teeos::fill_bytes;
+    } else if #[cfg(target_os = "uefi")] {
+        mod uefi;
+        pub use uefi::fill_bytes;
+    } else if #[cfg(target_os = "vxworks")] {
+        mod vxworks;
+        pub use vxworks::fill_bytes;
+    } else if #[cfg(target_os = "wasi")] {
+        mod wasi;
+        pub use wasi::fill_bytes;
+    } else if #[cfg(target_os = "zkvm")] {
+        mod zkvm;
+        pub use zkvm::fill_bytes;
+    } else if #[cfg(any(
+        all(target_family = "wasm", target_os = "unknown"),
+        target_os = "xous",
+    ))] {
+        // FIXME: finally remove std support for wasm32-unknown-unknown
+        // FIXME: add random data generation to xous
+        mod unsupported;
+        pub use unsupported::{fill_bytes, hashmap_random_keys};
+    }
+}
+
+#[cfg(not(any(
+    target_os = "linux",
+    target_os = "android",
+    all(target_family = "wasm", target_os = "unknown"),
+    target_os = "xous",
+)))]
+pub fn hashmap_random_keys() -> (u64, u64) {
+    let mut buf = [0; 16];
+    fill_bytes(&mut buf);
+    let k1 = u64::from_ne_bytes(buf[..8].try_into().unwrap());
+    let k2 = u64::from_ne_bytes(buf[8..].try_into().unwrap());
+    (k1, k2)
+}
diff --git a/library/std/src/sys/random/netbsd.rs b/library/std/src/sys/random/netbsd.rs
new file mode 100644
index 00000000000..2c5d9c72f30
--- /dev/null
+++ b/library/std/src/sys/random/netbsd.rs
@@ -0,0 +1,19 @@
+use crate::ptr;
+
+pub fn fill_bytes(bytes: &mut [u8]) {
+    let mib = [libc::CTL_KERN, libc::KERN_ARND];
+    for chunk in bytes.chunks_mut(256) {
+        let mut len = chunk.len();
+        let ret = unsafe {
+            libc::sysctl(
+                mib.as_ptr(),
+                mib.len() as libc::c_uint,
+                chunk.as_mut_ptr().cast(),
+                &mut len,
+                ptr::null(),
+                0,
+            )
+        };
+        assert!(ret != -1 && len == chunk.len(), "failed to generate random data");
+    }
+}
diff --git a/library/std/src/sys/random/redox.rs b/library/std/src/sys/random/redox.rs
new file mode 100644
index 00000000000..b004335a351
--- /dev/null
+++ b/library/std/src/sys/random/redox.rs
@@ -0,0 +1,12 @@
+use crate::fs::File;
+use crate::io::Read;
+use crate::sync::OnceLock;
+
+static SCHEME: OnceLock<File> = OnceLock::new();
+
+pub fn fill_bytes(bytes: &mut [u8]) {
+    SCHEME
+        .get_or_try_init(|| File::open("/scheme/rand"))
+        .and_then(|mut scheme| scheme.read_exact(bytes))
+        .expect("failed to generate random data");
+}
diff --git a/library/std/src/sys/random/sgx.rs b/library/std/src/sys/random/sgx.rs
new file mode 100644
index 00000000000..c3647a8df22
--- /dev/null
+++ b/library/std/src/sys/random/sgx.rs
@@ -0,0 +1,67 @@
+use crate::arch::x86_64::{_rdrand16_step, _rdrand32_step, _rdrand64_step};
+
+const RETRIES: u32 = 10;
+
+fn fail() -> ! {
+    panic!("failed to generate random data");
+}
+
+fn rdrand64() -> u64 {
+    unsafe {
+        let mut ret: u64 = 0;
+        for _ in 0..RETRIES {
+            if _rdrand64_step(&mut ret) == 1 {
+                return ret;
+            }
+        }
+
+        fail();
+    }
+}
+
+fn rdrand32() -> u32 {
+    unsafe {
+        let mut ret: u32 = 0;
+        for _ in 0..RETRIES {
+            if _rdrand32_step(&mut ret) == 1 {
+                return ret;
+            }
+        }
+
+        fail();
+    }
+}
+
+fn rdrand16() -> u16 {
+    unsafe {
+        let mut ret: u16 = 0;
+        for _ in 0..RETRIES {
+            if _rdrand16_step(&mut ret) == 1 {
+                return ret;
+            }
+        }
+
+        fail();
+    }
+}
+
+pub fn fill_bytes(bytes: &mut [u8]) {
+    let mut chunks = bytes.array_chunks_mut();
+    for chunk in &mut chunks {
+        *chunk = rdrand64().to_ne_bytes();
+    }
+
+    let mut chunks = chunks.into_remainder().array_chunks_mut();
+    for chunk in &mut chunks {
+        *chunk = rdrand32().to_ne_bytes();
+    }
+
+    let mut chunks = chunks.into_remainder().array_chunks_mut();
+    for chunk in &mut chunks {
+        *chunk = rdrand16().to_ne_bytes();
+    }
+
+    if let [byte] = chunks.into_remainder() {
+        *byte = rdrand16() as u8;
+    }
+}
diff --git a/library/std/src/sys/random/solid.rs b/library/std/src/sys/random/solid.rs
new file mode 100644
index 00000000000..545771150e2
--- /dev/null
+++ b/library/std/src/sys/random/solid.rs
@@ -0,0 +1,8 @@
+use crate::sys::pal::abi;
+
+pub fn fill_bytes(bytes: &mut [u8]) {
+    unsafe {
+        let result = abi::SOLID_RNG_SampleRandomBytes(bytes.as_mut_ptr(), bytes.len());
+        assert_eq!(result, 0, "failed to generate random data");
+    }
+}
diff --git a/library/std/src/sys/random/teeos.rs b/library/std/src/sys/random/teeos.rs
new file mode 100644
index 00000000000..fd6b24e19e9
--- /dev/null
+++ b/library/std/src/sys/random/teeos.rs
@@ -0,0 +1,7 @@
+extern "C" {
+    fn TEE_GenerateRandom(randomBuffer: *mut core::ffi::c_void, randomBufferLen: libc::size_t);
+}
+
+pub fn fill_bytes(bytes: &mut [u8]) {
+    unsafe { TEE_GenerateRandom(bytes.as_mut_ptr().cast(), bytes.len()) }
+}
diff --git a/library/std/src/sys/random/uefi.rs b/library/std/src/sys/random/uefi.rs
new file mode 100644
index 00000000000..a4d29e66f38
--- /dev/null
+++ b/library/std/src/sys/random/uefi.rs
@@ -0,0 +1,27 @@
+use r_efi::protocols::rng;
+
+use crate::sys::pal::helpers;
+
+pub fn fill_bytes(bytes: &mut [u8]) {
+    let handles =
+        helpers::locate_handles(rng::PROTOCOL_GUID).expect("failed to generate random data");
+    for handle in handles {
+        if let Ok(protocol) = helpers::open_protocol::<rng::Protocol>(handle, rng::PROTOCOL_GUID) {
+            let r = unsafe {
+                ((*protocol.as_ptr()).get_rng)(
+                    protocol.as_ptr(),
+                    crate::ptr::null_mut(),
+                    bytes.len(),
+                    bytes.as_mut_ptr(),
+                )
+            };
+            if r.is_error() {
+                continue;
+            } else {
+                return;
+            }
+        }
+    }
+
+    panic!("failed to generate random data");
+}
diff --git a/library/std/src/sys/random/unix.rs b/library/std/src/sys/random/unix.rs
new file mode 100644
index 00000000000..a56847e5541
--- /dev/null
+++ b/library/std/src/sys/random/unix.rs
@@ -0,0 +1,33 @@
+//! Random data generation through `getentropy`.
+//!
+//! Since issue 8 (2024), the POSIX specification mandates the existence of the
+//! `getentropy` function, which fills a slice of up to `GETENTROPY_MAX` bytes
+//! (256 on all known platforms) with random data. Luckily, this function has
+//! already been available on quite some BSDs before that, having appeared with
+//! OpenBSD 5.7 and spread from there:
+//!
+//! platform   | version | man-page
+//! -----------|---------|----------
+//! OpenBSD    | 5.6     | <https://man.openbsd.org/getentropy.2>
+//! FreeBSD    | 12.0    | <https://man.freebsd.org/cgi/man.cgi?query=getentropy&manpath=FreeBSD+15.0-CURRENT>
+//! macOS      | 10.12   | <https://github.com/apple-oss-distributions/xnu/blob/94d3b452840153a99b38a3a9659680b2a006908e/bsd/man/man2/getentropy.2#L4>
+//! NetBSD     | 10.0    | <https://man.netbsd.org/getentropy.3>
+//! DragonFly  | 6.1     | <https://man.dragonflybsd.org/?command=getentropy&section=3>
+//! Illumos    | ?       | <https://www.illumos.org/man/3C/getentropy>
+//! Solaris    | ?       | <https://docs.oracle.com/cd/E88353_01/html/E37841/getentropy-2.html>
+//!
+//! As it is standardized we use it whereever possible, even when `getrandom` is
+//! also available. NetBSD even warns that "Applications should avoid getrandom
+//! and use getentropy(2) instead; getrandom may be removed from a later
+//! release."[^1].
+//!
+//! [^1]: <https://man.netbsd.org/getrandom.2>
+
+pub fn fill_bytes(bytes: &mut [u8]) {
+    // GETENTROPY_MAX isn't defined yet on most platforms, but it's mandated
+    // to be at least 256, so just use that as limit.
+    for chunk in bytes.chunks_mut(256) {
+        let r = unsafe { libc::getentropy(chunk.as_mut_ptr().cast(), chunk.len()) };
+        assert_ne!(r, -1, "failed to generate random data");
+    }
+}
diff --git a/library/std/src/sys/random/unix_legacy.rs b/library/std/src/sys/random/unix_legacy.rs
new file mode 100644
index 00000000000..dd6be43c173
--- /dev/null
+++ b/library/std/src/sys/random/unix_legacy.rs
@@ -0,0 +1,20 @@
+//! Random data from `/dev/urandom`
+//!
+//! Before `getentropy` was standardized in 2024, UNIX didn't have a standardized
+//! way of getting random data, so systems just followed the precedent set by
+//! Linux and exposed random devices at `/dev/random` and `/dev/urandom`. Thus,
+//! for the few systems that do not support `getentropy` yet, we just read from
+//! the file.
+
+use crate::fs::File;
+use crate::io::Read;
+use crate::sync::OnceLock;
+
+static DEVICE: OnceLock<File> = OnceLock::new();
+
+pub fn fill_bytes(bytes: &mut [u8]) {
+    DEVICE
+        .get_or_try_init(|| File::open("/dev/urandom"))
+        .and_then(|mut dev| dev.read_exact(bytes))
+        .expect("failed to generate random data");
+}
diff --git a/library/std/src/sys/random/unsupported.rs b/library/std/src/sys/random/unsupported.rs
new file mode 100644
index 00000000000..d68ce4a9e87
--- /dev/null
+++ b/library/std/src/sys/random/unsupported.rs
@@ -0,0 +1,15 @@
+use crate::ptr;
+
+pub fn fill_bytes(_: &mut [u8]) {
+    panic!("this target does not support random data generation");
+}
+
+pub fn hashmap_random_keys() -> (u64, u64) {
+    // Use allocation addresses for a bit of randomness. This isn't
+    // particularily secure, but there isn't really an alternative.
+    let stack = 0u8;
+    let heap = Box::new(0u8);
+    let k1 = ptr::from_ref(&stack).addr() as u64;
+    let k2 = ptr::from_ref(&*heap).addr() as u64;
+    (k1, k2)
+}
diff --git a/library/std/src/sys/random/vxworks.rs b/library/std/src/sys/random/vxworks.rs
new file mode 100644
index 00000000000..d549ccebdb2
--- /dev/null
+++ b/library/std/src/sys/random/vxworks.rs
@@ -0,0 +1,25 @@
+use crate::sync::atomic::AtomicBool;
+use crate::sync::atomic::Ordering::Relaxed;
+
+static RNG_INIT: AtomicBool = AtomicBool::new(false);
+
+pub fn fill_bytes(mut bytes: &mut [u8]) {
+    while !RNG_INIT.load(Relaxed) {
+        let ret = unsafe { libc::randSecure() };
+        if ret < 0 {
+            panic!("failed to generate random data");
+        } else if ret > 0 {
+            RNG_INIT.store(true, Relaxed);
+            break;
+        }
+
+        unsafe { libc::usleep(10) };
+    }
+
+    while !bytes.is_empty() {
+        let len = bytes.len().try_into().unwrap_or(libc::c_int::MAX);
+        let ret = unsafe { libc::randABytes(bytes.as_mut_ptr(), len) };
+        assert!(ret >= 0, "failed to generate random data");
+        bytes = &mut bytes[len as usize..];
+    }
+}
diff --git a/library/std/src/sys/random/wasi.rs b/library/std/src/sys/random/wasi.rs
new file mode 100644
index 00000000000..d41da3751fc
--- /dev/null
+++ b/library/std/src/sys/random/wasi.rs
@@ -0,0 +1,5 @@
+pub fn fill_bytes(bytes: &mut [u8]) {
+    unsafe {
+        wasi::random_get(bytes.as_mut_ptr(), bytes.len()).expect("failed to generate random data")
+    }
+}
diff --git a/library/std/src/sys/random/windows.rs b/library/std/src/sys/random/windows.rs
new file mode 100644
index 00000000000..7566000f9e6
--- /dev/null
+++ b/library/std/src/sys/random/windows.rs
@@ -0,0 +1,20 @@
+use crate::sys::c;
+
+#[cfg(not(target_vendor = "win7"))]
+#[inline]
+pub fn fill_bytes(bytes: &mut [u8]) {
+    let ret = unsafe { c::ProcessPrng(bytes.as_mut_ptr(), bytes.len()) };
+    // ProcessPrng is documented as always returning `TRUE`.
+    // https://learn.microsoft.com/en-us/windows/win32/seccng/processprng#return-value
+    debug_assert_eq!(ret, c::TRUE);
+}
+
+#[cfg(target_vendor = "win7")]
+pub fn fill_bytes(mut bytes: &mut [u8]) {
+    while !bytes.is_empty() {
+        let len = bytes.len().try_into().unwrap_or(u32::MAX);
+        let ret = unsafe { c::RtlGenRandom(bytes.as_mut_ptr().cast(), len) };
+        assert_ne!(ret, 0, "failed to generate random data");
+        bytes = &mut bytes[len as usize..];
+    }
+}
diff --git a/library/std/src/sys/random/zkvm.rs b/library/std/src/sys/random/zkvm.rs
new file mode 100644
index 00000000000..3011942f6b2
--- /dev/null
+++ b/library/std/src/sys/random/zkvm.rs
@@ -0,0 +1,21 @@
+use crate::sys::pal::abi;
+
+pub fn fill_bytes(bytes: &mut [u8]) {
+    let (pre, words, post) = unsafe { bytes.align_to_mut::<u32>() };
+    if !words.is_empty() {
+        unsafe {
+            abi::sys_rand(words.as_mut_ptr(), words.len());
+        }
+    }
+
+    let mut buf = [0u32; 2];
+    let len = (pre.len() + post.len() + size_of::<u32>() - 1) / size_of::<u32>();
+    if len != 0 {
+        unsafe { abi::sys_rand(buf.as_mut_ptr(), len) };
+    }
+
+    let buf = buf.map(u32::to_ne_bytes);
+    let buf = buf.as_flattened();
+    pre.copy_from_slice(&buf[..pre.len()]);
+    post.copy_from_slice(&buf[pre.len()..pre.len() + post.len()]);
+}