From 090e9de5708388a21d66adbec91c871a48ac355e Mon Sep 17 00:00:00 2001 From: BlackHoleFox Date: Sun, 1 Oct 2023 10:55:24 -0500 Subject: [PATCH] Remove Apple RNG fallbacks and simplify implementation --- library/std/src/sys/unix/mod.rs | 1 - library/std/src/sys/unix/rand.rs | 109 +++++++++++++++---------------- 2 files changed, 52 insertions(+), 58 deletions(-) diff --git a/library/std/src/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs index da05620fd16e2..4b28f6feba5a5 100644 --- a/library/std/src/sys/unix/mod.rs +++ b/library/std/src/sys/unix/mod.rs @@ -415,7 +415,6 @@ cfg_if::cfg_if! { } else if #[cfg(any(target_os = "ios", target_os = "tvos", target_os = "watchos"))] { #[link(name = "System")] #[link(name = "objc")] - #[link(name = "Security", kind = "framework")] #[link(name = "Foundation", kind = "framework")] extern "C" {} } else if #[cfg(target_os = "fuchsia")] { diff --git a/library/std/src/sys/unix/rand.rs b/library/std/src/sys/unix/rand.rs index bdf725fbe5aca..2825d16774272 100644 --- a/library/std/src/sys/unix/rand.rs +++ b/library/std/src/sys/unix/rand.rs @@ -151,40 +151,65 @@ mod imp { } } -#[cfg(target_os = "macos")] +#[cfg(target_vendor = "apple")] mod imp { - use crate::fs::File; - use crate::io::Read; - use crate::sys::os::errno; - use crate::sys::weak::weak; + use crate::io; use libc::{c_int, c_void, size_t}; - fn getentropy_fill_bytes(v: &mut [u8]) -> bool { - weak!(fn getentropy(*mut c_void, size_t) -> c_int); - - getentropy - .get() - .map(|f| { - // getentropy(2) permits a maximum buffer size of 256 bytes - for s in v.chunks_mut(256) { - let ret = unsafe { f(s.as_mut_ptr() as *mut c_void, s.len()) }; - if ret == -1 { - panic!("unexpected getentropy error: {}", errno()); - } - } - true - }) - .unwrap_or(false) + #[inline(always)] + fn random_failure() -> ! { + panic!("unexpected random generation error: {}", io::Error::last_os_error()); } - pub fn fill_bytes(v: &mut [u8]) { - if getentropy_fill_bytes(v) { - return; + #[cfg(target_os = "macos")] + fn getentropy_fill_bytes(v: &mut [u8]) { + extern "C" { + fn getentropy(bytes: *mut c_void, count: size_t) -> c_int; } - // for older macos which doesn't support getentropy - let mut file = File::open("/dev/urandom").expect("failed to open /dev/urandom"); - file.read_exact(v).expect("failed to read /dev/urandom") + // getentropy(2) permits a maximum buffer size of 256 bytes + for s in v.chunks_mut(256) { + let ret = unsafe { getentropy(s.as_mut_ptr().cast(), s.len()) }; + if ret == -1 { + random_failure() + } + } + } + + #[cfg(not(target_os = "macos"))] + fn ccrandom_fill_bytes(v: &mut [u8]) { + 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 { + random_failure() + } + } + + pub fn fill_bytes(v: &mut [u8]) { + // All supported versions of macOS (10.12+) support getentropy. + // + // `getentropy` is measurably faster (via Divan) then the other alternatives so its preferred + // when usable. + #[cfg(target_os = "macos")] + getentropy_fill_bytes(v); + + // 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 on non-Mac OSes where the better entrypoints are blocked. + // + // `CCRandomGenerateBytes` is used instead of `SecRandomCopyBytes` because the former is accessible + // via `libSystem` (libc) while the other needs to link to `Security.framework`. + // + // Note that while `getentropy` has a available attribute in the macOS headers, the lack + // of a header in the iOS (and others) SDK means that its can cause app store rejections. + // Just use `CCRandomGenerateBytes` instead. + #[cfg(not(target_os = "macos"))] + ccrandom_fill_bytes(v); } } @@ -203,36 +228,6 @@ mod imp { } } -// On iOS and MacOS `SecRandomCopyBytes` calls `CCRandomCopyBytes` with -// `kCCRandomDefault`. `CCRandomCopyBytes` manages a CSPRNG which is seeded -// from `/dev/random` and which runs on its own thread accessed via GCD. -// This seems needlessly heavyweight for the purposes of generating two u64s -// once per thread in `hashmap_random_keys`. Therefore `SecRandomCopyBytes` is -// only used on iOS where direct access to `/dev/urandom` is blocked by the -// sandbox. -#[cfg(any(target_os = "ios", target_os = "tvos", target_os = "watchos"))] -mod imp { - use crate::io; - use crate::ptr; - use libc::{c_int, size_t}; - - enum SecRandom {} - - #[allow(non_upper_case_globals)] - const kSecRandomDefault: *const SecRandom = ptr::null(); - - extern "C" { - fn SecRandomCopyBytes(rnd: *const SecRandom, count: size_t, bytes: *mut u8) -> c_int; - } - - pub fn fill_bytes(v: &mut [u8]) { - let ret = unsafe { SecRandomCopyBytes(kSecRandomDefault, v.len(), v.as_mut_ptr()) }; - if ret == -1 { - panic!("couldn't generate random bytes: {}", io::Error::last_os_error()); - } - } -} - // FIXME: once the 10.x release becomes the minimum, this can be dropped for simplification. #[cfg(target_os = "netbsd")] mod imp {