about summary refs log tree commit diff
path: root/library/std/src/sys
diff options
context:
space:
mode:
authorjoboet <jonasboettiger@icloud.com>2024-08-17 16:54:01 +0200
committerjoboet <jonasboettiger@icloud.com>2024-09-23 10:36:16 +0200
commitb9d47cfa9b8f0805bfab2c254a02e598a906f102 (patch)
treee4c681065c6a064adba9ba0d784db8b59a8ad182 /library/std/src/sys
parent5c1c72572479afe98734d5f78fa862abe662c41a (diff)
downloadrust-b9d47cfa9b8f0805bfab2c254a02e598a906f102.tar.gz
rust-b9d47cfa9b8f0805bfab2c254a02e598a906f102.zip
std: switch to faster random sources on macOS and most BSDs
Diffstat (limited to 'library/std/src/sys')
-rw-r--r--library/std/src/sys/random/apple.rs21
-rw-r--r--library/std/src/sys/random/arc4random.rs34
-rw-r--r--library/std/src/sys/random/getentropy.rs17
-rw-r--r--library/std/src/sys/random/linux.rs2
-rw-r--r--library/std/src/sys/random/mod.rs31
-rw-r--r--library/std/src/sys/random/netbsd.rs19
-rw-r--r--library/std/src/sys/random/unix.rs33
-rw-r--r--library/std/src/sys/random/unix_legacy.rs4
8 files changed, 75 insertions, 86 deletions
diff --git a/library/std/src/sys/random/apple.rs b/library/std/src/sys/random/apple.rs
index 09b6d0d51ab..417198c9d85 100644
--- a/library/std/src/sys/random/apple.rs
+++ b/library/std/src/sys/random/apple.rs
@@ -1,20 +1,13 @@
-//! Random data on non-macOS Apple platforms.
+//! Random data on 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
+//! `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`.
 //!
-//! [^1]: <https://support.apple.com/en-gb/guide/security/seca0c73a75b/web>
+//! 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()) };
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/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/linux.rs b/library/std/src/sys/random/linux.rs
index 4ede0af6494..073fdc45e61 100644
--- a/library/std/src/sys/random/linux.rs
+++ b/library/std/src/sys/random/linux.rs
@@ -63,9 +63,9 @@
 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::sync::OnceLock;
 use crate::sys::pal::os::errno;
 use crate::sys::pal::weak::syscall;
 
diff --git a/library/std/src/sys/random/mod.rs b/library/std/src/sys/random/mod.rs
index d39e78bb7b3..16fb8c64c9b 100644
--- a/library/std/src/sys/random/mod.rs
+++ b/library/std/src/sys/random/mod.rs
@@ -6,24 +6,25 @@ cfg_if::cfg_if! {
     } 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 = "openbsd",
-        target_os = "freebsd",
-        target_os = "macos",
-        all(target_os = "netbsd", netbsd10),
         target_os = "dragonfly",
+        target_os = "freebsd",
+        target_os = "haiku",
         target_os = "illumos",
+        target_os = "netbsd",
+        target_os = "openbsd",
         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;
+        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;
@@ -34,7 +35,7 @@ cfg_if::cfg_if! {
         mod hermit;
         pub use hermit::fill_bytes;
     } else if #[cfg(target_os = "horizon")] {
-        // FIXME: add getentropy to shim-3ds
+        // FIXME: add arc4random_buf to shim-3ds
         mod horizon;
         pub use horizon::fill_bytes;
     } else if #[cfg(any(
@@ -44,10 +45,6 @@ cfg_if::cfg_if! {
     ))] {
         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;
diff --git a/library/std/src/sys/random/netbsd.rs b/library/std/src/sys/random/netbsd.rs
deleted file mode 100644
index 2c5d9c72f30..00000000000
--- a/library/std/src/sys/random/netbsd.rs
+++ /dev/null
@@ -1,19 +0,0 @@
-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/unix.rs b/library/std/src/sys/random/unix.rs
deleted file mode 100644
index a56847e5541..00000000000
--- a/library/std/src/sys/random/unix.rs
+++ /dev/null
@@ -1,33 +0,0 @@
-//! 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
index dd6be43c173..587068b0d66 100644
--- a/library/std/src/sys/random/unix_legacy.rs
+++ b/library/std/src/sys/random/unix_legacy.rs
@@ -3,8 +3,8 @@
 //! 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.
+//! 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;