Skip to content

Commit

Permalink
Rollup merge of rust-lang#101011 - BlackHoleFox:apple-random-improvem…
Browse files Browse the repository at this point in the history
…ents, r=thomcc

Use getentropy when possible on all Apple platforms

As the current code comments say, `SecRandomCopyBytes` is very heavyweight (regardless of purpose) compared to just asking the kernel directly for bytes from its own CSPRNG. We were not previously making an attempt to use the more efficient `getentropy` call on other Apple targets, instead solely using it on macOS. As the function is available on newer versions of Apple's different OSes, this changes the random filling to always attempt it first everywhere, only falling back to the less ideal alternatives after. This also cleans up the multiple Apple `imp` blocks into one.

It also should give a perf improvement, even if its likely unnoticeably small.

Refed XCode header for `getentropy` in the SDK:
```h
int getentropy(void* buffer, size_t size) __OSX_AVAILABLE(10.12) __IOS_AVAILABLE(10.0) __TVOS_AVAILABLE(10.0) __WATCHOS_AVAILABLE(3.0);
```

r? `@thomcc`
  • Loading branch information
matthiaskrgr authored Aug 31, 2022
2 parents d00693b + 3fc35b5 commit 765c777
Showing 1 changed file with 56 additions and 38 deletions.
94 changes: 56 additions & 38 deletions library/std/src/sys/unix/rand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,9 @@ mod imp {
}
}

#[cfg(target_os = "macos")]
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))]
mod imp {
use crate::fs::File;
use crate::io::Read;
use crate::sys::os::errno;
use crate::io;
use crate::sys::weak::weak;
use libc::{c_int, c_void, size_t};

Expand All @@ -155,22 +153,72 @@ mod imp {
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());
panic!("unexpected getentropy error: {}", io::Error::last_os_error());
}
}
true
})
.unwrap_or(false)
}

#[cfg(target_os = "macos")]
fn fallback_fill_bytes(v: &mut [u8]) {
use crate::fs::File;
use crate::io::Read;

let mut file = File::open("/dev/urandom").expect("failed to open /dev/urandom");
file.read_exact(v).expect("failed to read /dev/urandom")
}

// 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 is very heavyweight compared to the alternatives, but they may not be usable:
// - `getentropy` was added in iOS 10, but we support a minimum of iOS 7
// - `/dev/urandom` is not accessible inside the iOS app sandbox.
//
// Therefore `SecRandomCopyBytes` is only used on older iOS versions where no
// better options are present.
#[cfg(target_os = "ios")]
fn fallback_fill_bytes(v: &mut [u8]) {
use crate::ptr;

enum SecRandom {}

#[allow(non_upper_case_globals)]
const kSecRandomDefault: *const SecRandom = ptr::null();

extern "C" {
fn SecRandomCopyBytes(rnd: *const SecRandom, count: size_t, bytes: *mut u8) -> c_int;
}

let ret = unsafe { SecRandomCopyBytes(kSecRandomDefault, v.len(), v.as_mut_ptr()) };
if ret == -1 {
panic!("couldn't generate random bytes: {}", io::Error::last_os_error());
}
}

// All supported versions of watchOS (>= 5) have support for `getentropy`.
#[cfg(target_os = "watchos")]
#[cold]
fn fallback_fill_bytes(_: &mut [u8]) {
unreachable!()
}

pub fn fill_bytes(v: &mut [u8]) {
if getentropy_fill_bytes(v) {
return;
}

// for older macos which doesn't support getentropy
let mut file = File::open("/dev/urandom").expect("failed to open /dev/urandom");
file.read_exact(v).expect("failed to read /dev/urandom")
// Older macOS versions (< 10.12) don't support `getentropy`. Fallback to
// reading from `/dev/urandom` on these systems.
//
// Older iOS versions (< 10) don't support it either. Fallback to
// `SecRandomCopyBytes` on these systems. On watchOS, this is unreachable
// because the minimum supported version is 5 while `getentropy` became accessible
// in 3.
fallback_fill_bytes(v)
}
}

Expand All @@ -189,36 +237,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 = "watchos"))]
mod imp {
use crate::io;
use crate::ptr;
use libc::{c_int, size_t};

enum SecRandom {}

#[allow(non_upper_case_globals)]
const kSecRandomDefault: *const SecRandom = ptr::null();

extern "C" {
fn SecRandomCopyBytes(rnd: *const SecRandom, count: size_t, bytes: *mut u8) -> c_int;
}

pub fn fill_bytes(v: &mut [u8]) {
let ret = unsafe { SecRandomCopyBytes(kSecRandomDefault, v.len(), v.as_mut_ptr()) };
if ret == -1 {
panic!("couldn't generate random bytes: {}", io::Error::last_os_error());
}
}
}

#[cfg(any(target_os = "freebsd", target_os = "netbsd"))]
mod imp {
use crate::ptr;
Expand Down

0 comments on commit 765c777

Please sign in to comment.