1pub fn hashmap_random_keys() -> (u64, u64) {
2 const KEY_LEN: usize = core::mem::size_of::<u64>();
3
4 let mut v: [u8; 16] = [0u8; KEY_LEN * 2];
5 imp::fill_bytes(&mut v);
6
7 let key1: [u8; 8] = v[0..KEY_LEN].try_into().unwrap();
8 let key2: [u8; 8] = v[KEY_LEN..].try_into().unwrap();
9
10 (u64::from_ne_bytes(key1), u64::from_ne_bytes(key2))
11}
12
13#[cfg(all(
14 unix,
15 not(target_os = "macos"),
16 not(target_os = "ios"),
17 not(target_os = "tvos"),
18 not(target_os = "watchos"),
19 not(target_os = "visionos"),
20 not(target_os = "openbsd"),
21 not(target_os = "netbsd"),
22 not(target_os = "fuchsia"),
23 not(target_os = "redox"),
24 not(target_os = "vxworks"),
25 not(target_os = "emscripten"),
26 not(target_os = "vita"),
27))]
28mod imp {
29 use crate::fs::File;
30 use crate::io::Read;
31
32 #[cfg(any(target_os = "linux", target_os = "android"))]
33 use crate::sys::weak::syscall;
34
35 #[cfg(any(target_os = "linux", target_os = "android"))]
36 fn getrandom(buf: &mut [u8]) -> libc::ssize_t {
37 use crate::sync::atomic::{AtomicBool, Ordering};
38 use crate::sys::os::errno;
39
40 // A weak symbol allows interposition, e.g. for perf measurements that want to
41 // disable randomness for consistency. Otherwise, we'll try a raw syscall.
42 // (`getrandom` was added in glibc 2.25, musl 1.1.20, android API level 28)
43 syscall! {
44 fn getrandom(
45 buffer: *mut libc::c_void,
46 length: libc::size_t,
47 flags: libc::c_uint
48 ) -> libc::ssize_t
49 }
50
51 // This provides the best quality random numbers available at the given moment
52 // without ever blocking, and is preferable to falling back to /dev/urandom.
53 static GRND_INSECURE_AVAILABLE: AtomicBool = AtomicBool::new(true);
54 if GRND_INSECURE_AVAILABLE.load(Ordering::Relaxed) {
55 let ret = unsafe { getrandom(buf.as_mut_ptr().cast(), buf.len(), libc::GRND_INSECURE) };
56 if ret == -1 && errno() as libc::c_int == libc::EINVAL {
57 GRND_INSECURE_AVAILABLE.store(false, Ordering::Relaxed);
58 } else {
59 return ret;
60 }
61 }
62
63 unsafe { getrandom(buf.as_mut_ptr().cast(), buf.len(), libc::GRND_NONBLOCK) }
64 }
65
66 #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "freebsd", netbsd10))]
67 fn getrandom(buf: &mut [u8]) -> libc::ssize_t {
68 unsafe { libc::getrandom(buf.as_mut_ptr().cast(), buf.len(), 0) }
69 }
70
71 #[cfg(target_os = "dragonfly")]
72 fn getrandom(buf: &mut [u8]) -> libc::ssize_t {
73 extern "C" {
74 fn getrandom(
75 buf: *mut libc::c_void,
76 buflen: libc::size_t,
77 flags: libc::c_uint,
78 ) -> libc::ssize_t;
79 }
80 unsafe { getrandom(buf.as_mut_ptr().cast(), buf.len(), 0) }
81 }
82
83 #[cfg(not(any(
84 target_os = "linux",
85 target_os = "android",
86 target_os = "espidf",
87 target_os = "horizon",
88 target_os = "freebsd",
89 target_os = "dragonfly",
90 netbsd10
91 )))]
92 fn getrandom_fill_bytes(_buf: &mut [u8]) -> bool {
93 false
94 }
95
96 #[cfg(any(
97 target_os = "linux",
98 target_os = "android",
99 target_os = "espidf",
100 target_os = "horizon",
101 target_os = "freebsd",
102 target_os = "dragonfly",
103 netbsd10
104 ))]
105 fn getrandom_fill_bytes(v: &mut [u8]) -> bool {
106 use crate::sync::atomic::{AtomicBool, Ordering};
107 use crate::sys::os::errno;
108
109 static GETRANDOM_UNAVAILABLE: AtomicBool = AtomicBool::new(false);
110 if GETRANDOM_UNAVAILABLE.load(Ordering::Relaxed) {
111 return false;
112 }
113
114 let mut read = 0;
115 while read < v.len() {
116 let result = getrandom(&mut v[read..]);
117 if result == -1 {
118 let err = errno() as libc::c_int;
119 if err == libc::EINTR {
120 continue;
121 } else if err == libc::ENOSYS || err == libc::EPERM {
122 // Fall back to reading /dev/urandom if `getrandom` is not
123 // supported on the current kernel.
124 //
125 // Also fall back in case it is disabled by something like
126 // seccomp or inside of docker.
127 //
128 // If the `getrandom` syscall is not implemented in the current kernel version it should return an
129 // `ENOSYS` error. Docker also blocks the whole syscall inside unprivileged containers, and
130 // returns `EPERM` (instead of `ENOSYS`) when a program tries to invoke the syscall. Because of
131 // that we need to check for *both* `ENOSYS` and `EPERM`.
132 //
133 // Note that Docker's behavior is breaking other projects (notably glibc), so they're planning
134 // to update their filtering to return `ENOSYS` in a future release:
135 //
136 // https://github.com/moby/moby/issues/42680
137 //
138 GETRANDOM_UNAVAILABLE.store(true, Ordering::Relaxed);
139 return false;
140 } else if err == libc::EAGAIN {
141 return false;
142 } else {
143 panic!("unexpected getrandom error: {err}");
144 }
145 } else {
146 read += result as usize;
147 }
148 }
149 true
150 }
151
152 pub fn fill_bytes(v: &mut [u8]) {
153 // getrandom_fill_bytes here can fail if getrandom() returns EAGAIN,
154 // meaning it would have blocked because the non-blocking pool (urandom)
155 // has not initialized in the kernel yet due to a lack of entropy. The
156 // fallback we do here is to avoid blocking applications which could
157 // depend on this call without ever knowing they do and don't have a
158 // work around. The PRNG of /dev/urandom will still be used but over a
159 // possibly predictable entropy pool.
160 if getrandom_fill_bytes(v) {
161 return;
162 }
163
164 // getrandom failed because it is permanently or temporarily (because
165 // of missing entropy) unavailable. Open /dev/urandom, read from it,
166 // and close it again.
167 let mut file = File::open("/dev/urandom").expect("failed to open /dev/urandom");
168 file.read_exact(v).expect("failed to read /dev/urandom")
169 }
170}
171
172#[cfg(target_vendor = "apple")]
173mod imp {
174 use crate::io;
175 use libc::{c_int, c_void, size_t};
176
177 #[inline(always)]
178 fn random_failure() -> ! {
179 panic!("unexpected random generation error: {}", io::Error::last_os_error());
180 }
181
182 #[cfg(target_os = "macos")]
183 fn getentropy_fill_bytes(v: &mut [u8]) {
184 extern "C" {
185 fn getentropy(bytes: *mut c_void, count: size_t) -> c_int;
186 }
187
188 // getentropy(2) permits a maximum buffer size of 256 bytes
189 for s in v.chunks_mut(256) {
190 let ret = unsafe { getentropy(s.as_mut_ptr().cast(), s.len()) };
191 if ret == -1 {
192 random_failure()
193 }
194 }
195 }
196
197 #[cfg(not(target_os = "macos"))]
198 fn ccrandom_fill_bytes(v: &mut [u8]) {
199 extern "C" {
200 fn CCRandomGenerateBytes(bytes: *mut c_void, count: size_t) -> c_int;
201 }
202
203 let ret = unsafe { CCRandomGenerateBytes(v.as_mut_ptr().cast(), v.len()) };
204 if ret == -1 {
205 random_failure()
206 }
207 }
208
209 pub fn fill_bytes(v: &mut [u8]) {
210 // All supported versions of macOS (10.12+) support getentropy.
211 //
212 // `getentropy` is measurably faster (via Divan) then the other alternatives so its preferred
213 // when usable.
214 #[cfg(target_os = "macos")]
215 getentropy_fill_bytes(v);
216
217 // On Apple platforms, `CCRandomGenerateBytes` and `SecRandomCopyBytes` simply
218 // call into `CCRandomCopyBytes` with `kCCRandomDefault`. `CCRandomCopyBytes`
219 // manages a CSPRNG which is seeded from the kernel's CSPRNG and which runs on
220 // its own thread accessed via GCD. This seems needlessly heavyweight for our purposes
221 // so we only use it on non-Mac OSes where the better entrypoints are blocked.
222 //
223 // `CCRandomGenerateBytes` is used instead of `SecRandomCopyBytes` because the former is accessible
224 // via `libSystem` (libc) while the other needs to link to `Security.framework`.
225 //
226 // Note that while `getentropy` has a available attribute in the macOS headers, the lack
227 // of a header in the iOS (and others) SDK means that its can cause app store rejections.
228 // Just use `CCRandomGenerateBytes` instead.
229 #[cfg(not(target_os = "macos"))]
230 ccrandom_fill_bytes(v);
231 }
232}
233
234#[cfg(any(target_os = "openbsd", target_os = "emscripten", target_os = "vita"))]
235mod imp {
236 use crate::sys::os::errno;
237
238 pub fn fill_bytes(v: &mut [u8]) {
239 // getentropy(2) permits a maximum buffer size of 256 bytes
240 for s in v.chunks_mut(256) {
241 let ret = unsafe { libc::getentropy(s.as_mut_ptr() as *mut libc::c_void, s.len()) };
242 if ret == -1 {
243 panic!("unexpected getentropy error: {}", errno());
244 }
245 }
246 }
247}
248
249// FIXME: once the 10.x release becomes the minimum, this can be dropped for simplification.
250#[cfg(all(target_os = "netbsd", not(netbsd10)))]
251mod imp {
252 use crate::ptr;
253
254 pub fn fill_bytes(v: &mut [u8]) {
255 let mib = [libc::CTL_KERN, libc::KERN_ARND];
256 // kern.arandom permits a maximum buffer size of 256 bytes
257 for s in v.chunks_mut(256) {
258 let mut s_len = s.len();
259 let ret = unsafe {
260 libc::sysctl(
261 mib.as_ptr(),
262 mib.len() as libc::c_uint,
263 s.as_mut_ptr() as *mut _,
264 &mut s_len,
265 ptr::null(),
266 0,
267 )
268 };
269 if ret == -1 || s_len != s.len() {
270 panic!(
271 "kern.arandom sysctl failed! (returned {}, s.len() {}, oldlenp {})",
272 ret,
273 s.len(),
274 s_len
275 );
276 }
277 }
278 }
279}
280
281#[cfg(target_os = "fuchsia")]
282mod imp {
283 #[link(name = "zircon")]
284 extern "C" {
285 fn zx_cprng_draw(buffer: *mut u8, len: usize);
286 }
287
288 pub fn fill_bytes(v: &mut [u8]) {
289 unsafe { zx_cprng_draw(v.as_mut_ptr(), v.len()) }
290 }
291}
292
293#[cfg(target_os = "redox")]
294mod imp {
295 use crate::fs::File;
296 use crate::io::Read;
297
298 pub fn fill_bytes(v: &mut [u8]) {
299 // Open rand:, read from it, and close it again.
300 let mut file = File::open("rand:").expect("failed to open rand:");
301 file.read_exact(v).expect("failed to read rand:")
302 }
303}
304
305#[cfg(target_os = "vxworks")]
306mod imp {
307 use crate::io;
308 use core::sync::atomic::{AtomicBool, Ordering::Relaxed};
309
310 pub fn fill_bytes(v: &mut [u8]) {
311 static RNG_INIT: AtomicBool = AtomicBool::new(false);
312 while !RNG_INIT.load(Relaxed) {
313 let ret = unsafe { libc::randSecure() };
314 if ret < 0 {
315 panic!("couldn't generate random bytes: {}", io::Error::last_os_error());
316 } else if ret > 0 {
317 RNG_INIT.store(true, Relaxed);
318 break;
319 }
320 unsafe { libc::usleep(10) };
321 }
322 let ret = unsafe {
323 libc::randABytes(v.as_mut_ptr() as *mut libc::c_uchar, v.len() as libc::c_int)
324 };
325 if ret < 0 {
326 panic!("couldn't generate random bytes: {}", io::Error::last_os_error());
327 }
328 }
329}
330