about summary refs log tree commit diff
path: root/library/std/src/sys/random
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-09-23 16:40:39 +0000
committerbors <bors@rust-lang.org>2024-09-23 16:40:39 +0000
commit9d6039ccae68a2f1930ed9c1542d387b2c0c0ba6 (patch)
treed0c8632af11209d8b85d840c1fcae8695f81aac1 /library/std/src/sys/random
parent648d024a7859e1ab7fdffe5e419b6e35ccb16a4a (diff)
parentc7abc8518175a088b922d88a2b58908b4149a6b0 (diff)
Auto merge of #130755 - workingjubilee:rollup-zpja9b3, r=workingjubilee
Rollup of 7 pull requests

Successful merges:

 - #129201 (std: implement the `random` feature (alternative version))
 - #130536 (bootstrap: Set the dylib path when building books with rustdoc)
 - #130551 (Fix `break_last_token`.)
 - #130657 (Remove x86_64-fuchsia and aarch64-fuchsia target aliases)
 - #130721 (Add more test cases for block-no-opening-brace)
 - #130736 (Add rustfmt 2024 reformatting to git blame ignore)
 - #130746 (readd `@tgross35` and `@joboet` to the review rotation)

r? `@ghost`
`@rustbot` modify labels: rollup
Diffstat (limited to 'library/std/src/sys/random')
-rw-r--r--library/std/src/sys/random/apple.rs15
-rw-r--r--library/std/src/sys/random/arc4random.rs34
-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/getentropy.rs17
-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.rs95
-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_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
20 files changed, 594 insertions, 0 deletions
diff --git a/library/std/src/sys/random/apple.rs b/library/std/src/sys/random/apple.rs
new file mode 100644
index 00000000000..417198c9d85
--- /dev/null
+++ b/library/std/src/sys/random/apple.rs
@@ -0,0 +1,15 @@
+//! Random data on Apple platforms.
+//!
+//! `CCRandomGenerateBytes` calls into `CCRandomCopyBytes` with `kCCRandomDefault`.
+//! `CCRandomCopyBytes` manages a CSPRNG which is seeded from the kernel's CSPRNG.
+//! We use `CCRandomGenerateBytes` instead of `SecCopyBytes` because it is accessible via
+//! `libSystem` (libc) while the other needs to link to `Security.framework`.
+//!
+//! Note that technically, `arc4random_buf` is available as well, but that calls
+//! into the same system service anyway, and `CCRandomGenerateBytes` has been
+//! proven to be App Store-compatible.
+
+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/arc4random.rs b/library/std/src/sys/random/arc4random.rs
new file mode 100644
index 00000000000..32467e9ebaa
--- /dev/null
+++ b/library/std/src/sys/random/arc4random.rs
@@ -0,0 +1,34 @@
+//! Random data generation with `arc4random_buf`.
+//!
+//! Contrary to its name, `arc4random` doesn't actually use the horribly-broken
+//! RC4 cypher anymore, at least not on modern systems, but rather something
+//! like ChaCha20 with continual reseeding from the OS. That makes it an ideal
+//! source of large quantities of cryptographically secure data, which is exactly
+//! what we need for `DefaultRandomSource`. Unfortunately, it's not available
+//! on all UNIX systems, most notably Linux (until recently, but it's just a
+//! wrapper for `getrandom`. Since we need to hook into `getrandom` directly
+//! for `HashMap` keys anyway, we just keep our version).
+
+#[cfg(not(any(
+    target_os = "haiku",
+    target_os = "illumos",
+    target_os = "solaris",
+    target_os = "vita",
+)))]
+use libc::arc4random_buf;
+
+// FIXME: move these to libc (except Haiku, that one needs to link to libbsd.so).
+#[cfg(any(
+    target_os = "haiku", // See https://git.haiku-os.org/haiku/tree/headers/compatibility/bsd/stdlib.h
+    target_os = "illumos", // See https://www.illumos.org/man/3C/arc4random
+    target_os = "solaris", // See https://docs.oracle.com/cd/E88353_01/html/E37843/arc4random-3c.html
+    target_os = "vita", // See https://github.com/vitasdk/newlib/blob/b89e5bc183b516945f9ee07eef483ecb916e45ff/newlib/libc/include/stdlib.h#L74
+))]
+#[cfg_attr(target_os = "haiku", link(name = "bsd"))]
+extern "C" {
+    fn arc4random_buf(buf: *mut core::ffi::c_void, nbytes: libc::size_t);
+}
+
+pub fn fill_bytes(bytes: &mut [u8]) {
+    unsafe { arc4random_buf(bytes.as_mut_ptr().cast(), bytes.len()) }
+}
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/getentropy.rs b/library/std/src/sys/random/getentropy.rs
new file mode 100644
index 00000000000..110ac134c1f
--- /dev/null
+++ b/library/std/src/sys/random/getentropy.rs
@@ -0,0 +1,17 @@
+//! 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. Unfortunately, it's only
+//! meant to be used to seed other CPRNGs, which we don't have, so we only use
+//! it where `arc4random_buf` and friends aren't available or secure (currently
+//! that's only the case on Emscripten).
+
+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/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..073fdc45e61
--- /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::OnceLock;
+use crate::sync::atomic::AtomicBool;
+use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release};
+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..16fb8c64c9b
--- /dev/null
+++ b/library/std/src/sys/random/mod.rs
@@ -0,0 +1,95 @@
+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(target_vendor = "apple")] {
+        mod apple;
+        pub use apple::fill_bytes;
+    // Others, in alphabetical ordering.
+    } else if #[cfg(any(
+        target_os = "dragonfly",
+        target_os = "freebsd",
+        target_os = "haiku",
+        target_os = "illumos",
+        target_os = "netbsd",
+        target_os = "openbsd",
+        target_os = "solaris",
+        target_os = "vita",
+    ))] {
+        mod arc4random;
+        pub use arc4random::fill_bytes;
+    } else if #[cfg(target_os = "emscripten")] {
+        mod getentropy;
+        pub use getentropy::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 arc4random_buf 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(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/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_legacy.rs b/library/std/src/sys/random/unix_legacy.rs
new file mode 100644
index 00000000000..587068b0d66
--- /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 support neither `arc4random_buf` nor `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()]);
+}