Skip to content

Commit 765c777

Browse files
authored
Rollup merge of rust-lang#101011 - BlackHoleFox:apple-random-improvements, 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`
2 parents d00693b + 3fc35b5 commit 765c777

File tree

1 file changed

+56
-38
lines changed

1 file changed

+56
-38
lines changed

library/std/src/sys/unix/rand.rs

+56-38
Original file line numberDiff line numberDiff line change
@@ -137,11 +137,9 @@ mod imp {
137137
}
138138
}
139139

140-
#[cfg(target_os = "macos")]
140+
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))]
141141
mod imp {
142-
use crate::fs::File;
143-
use crate::io::Read;
144-
use crate::sys::os::errno;
142+
use crate::io;
145143
use crate::sys::weak::weak;
146144
use libc::{c_int, c_void, size_t};
147145

@@ -155,22 +153,72 @@ mod imp {
155153
for s in v.chunks_mut(256) {
156154
let ret = unsafe { f(s.as_mut_ptr() as *mut c_void, s.len()) };
157155
if ret == -1 {
158-
panic!("unexpected getentropy error: {}", errno());
156+
panic!("unexpected getentropy error: {}", io::Error::last_os_error());
159157
}
160158
}
161159
true
162160
})
163161
.unwrap_or(false)
164162
}
165163

164+
#[cfg(target_os = "macos")]
165+
fn fallback_fill_bytes(v: &mut [u8]) {
166+
use crate::fs::File;
167+
use crate::io::Read;
168+
169+
let mut file = File::open("/dev/urandom").expect("failed to open /dev/urandom");
170+
file.read_exact(v).expect("failed to read /dev/urandom")
171+
}
172+
173+
// On iOS and MacOS `SecRandomCopyBytes` calls `CCRandomCopyBytes` with
174+
// `kCCRandomDefault`. `CCRandomCopyBytes` manages a CSPRNG which is seeded
175+
// from `/dev/random` and which runs on its own thread accessed via GCD.
176+
//
177+
// This is very heavyweight compared to the alternatives, but they may not be usable:
178+
// - `getentropy` was added in iOS 10, but we support a minimum of iOS 7
179+
// - `/dev/urandom` is not accessible inside the iOS app sandbox.
180+
//
181+
// Therefore `SecRandomCopyBytes` is only used on older iOS versions where no
182+
// better options are present.
183+
#[cfg(target_os = "ios")]
184+
fn fallback_fill_bytes(v: &mut [u8]) {
185+
use crate::ptr;
186+
187+
enum SecRandom {}
188+
189+
#[allow(non_upper_case_globals)]
190+
const kSecRandomDefault: *const SecRandom = ptr::null();
191+
192+
extern "C" {
193+
fn SecRandomCopyBytes(rnd: *const SecRandom, count: size_t, bytes: *mut u8) -> c_int;
194+
}
195+
196+
let ret = unsafe { SecRandomCopyBytes(kSecRandomDefault, v.len(), v.as_mut_ptr()) };
197+
if ret == -1 {
198+
panic!("couldn't generate random bytes: {}", io::Error::last_os_error());
199+
}
200+
}
201+
202+
// All supported versions of watchOS (>= 5) have support for `getentropy`.
203+
#[cfg(target_os = "watchos")]
204+
#[cold]
205+
fn fallback_fill_bytes(_: &mut [u8]) {
206+
unreachable!()
207+
}
208+
166209
pub fn fill_bytes(v: &mut [u8]) {
167210
if getentropy_fill_bytes(v) {
168211
return;
169212
}
170213

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

@@ -189,36 +237,6 @@ mod imp {
189237
}
190238
}
191239

192-
// On iOS and MacOS `SecRandomCopyBytes` calls `CCRandomCopyBytes` with
193-
// `kCCRandomDefault`. `CCRandomCopyBytes` manages a CSPRNG which is seeded
194-
// from `/dev/random` and which runs on its own thread accessed via GCD.
195-
// This seems needlessly heavyweight for the purposes of generating two u64s
196-
// once per thread in `hashmap_random_keys`. Therefore `SecRandomCopyBytes` is
197-
// only used on iOS where direct access to `/dev/urandom` is blocked by the
198-
// sandbox.
199-
#[cfg(any(target_os = "ios", target_os = "watchos"))]
200-
mod imp {
201-
use crate::io;
202-
use crate::ptr;
203-
use libc::{c_int, size_t};
204-
205-
enum SecRandom {}
206-
207-
#[allow(non_upper_case_globals)]
208-
const kSecRandomDefault: *const SecRandom = ptr::null();
209-
210-
extern "C" {
211-
fn SecRandomCopyBytes(rnd: *const SecRandom, count: size_t, bytes: *mut u8) -> c_int;
212-
}
213-
214-
pub fn fill_bytes(v: &mut [u8]) {
215-
let ret = unsafe { SecRandomCopyBytes(kSecRandomDefault, v.len(), v.as_mut_ptr()) };
216-
if ret == -1 {
217-
panic!("couldn't generate random bytes: {}", io::Error::last_os_error());
218-
}
219-
}
220-
}
221-
222240
#[cfg(any(target_os = "freebsd", target_os = "netbsd"))]
223241
mod imp {
224242
use crate::ptr;

0 commit comments

Comments
 (0)