Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove Apple RNG fallbacks and simplify implementation #116319

Merged
merged 1 commit into from
Oct 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion library/std/src/sys/unix/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")] {
Expand Down
109 changes: 52 additions & 57 deletions library/std/src/sys/unix/rand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand All @@ -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 {
Expand Down
Loading