Skip to content

Commit 3fc35b5

Browse files
committed
Use getentropy when possible on all Apple platforms
1 parent 6c943ba commit 3fc35b5

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)