]> git.lizzy.rs Git - rust.git/blob - src/libstd/sys/unix/rand.rs
Auto merge of #64819 - Manishearth:clippyup, r=Manishearth
[rust.git] / src / libstd / sys / unix / rand.rs
1 use crate::mem;
2 use crate::slice;
3
4 pub fn hashmap_random_keys() -> (u64, u64) {
5     let mut v = (0, 0);
6     unsafe {
7         let view = slice::from_raw_parts_mut(&mut v as *mut _ as *mut u8,
8                                              mem::size_of_val(&v));
9         imp::fill_bytes(view);
10     }
11     return v
12 }
13
14 #[cfg(all(unix,
15           not(target_os = "ios"),
16           not(target_os = "openbsd"),
17           not(target_os = "freebsd"),
18           not(target_os = "fuchsia"),
19           not(target_os = "redox")))]
20 mod imp {
21     use crate::fs::File;
22     use crate::io::Read;
23
24     #[cfg(any(target_os = "linux", target_os = "android"))]
25     fn getrandom(buf: &mut [u8]) -> libc::c_long {
26         unsafe {
27             libc::syscall(libc::SYS_getrandom, buf.as_mut_ptr(), buf.len(), libc::GRND_NONBLOCK)
28         }
29     }
30
31     #[cfg(not(any(target_os = "linux", target_os = "android")))]
32     fn getrandom_fill_bytes(_buf: &mut [u8]) -> bool { false }
33
34     #[cfg(any(target_os = "linux", target_os = "android"))]
35     fn getrandom_fill_bytes(v: &mut [u8]) -> bool {
36         use crate::sync::atomic::{AtomicBool, Ordering};
37         use crate::sys::os::errno;
38
39         static GETRANDOM_UNAVAILABLE: AtomicBool = AtomicBool::new(false);
40         if GETRANDOM_UNAVAILABLE.load(Ordering::Relaxed) {
41             return false;
42         }
43
44         let mut read = 0;
45         while read < v.len() {
46             let result = getrandom(&mut v[read..]);
47             if result == -1 {
48                 let err = errno() as libc::c_int;
49                 if err == libc::EINTR {
50                     continue;
51                 } else if err == libc::ENOSYS || err == libc::EPERM {
52                     // Fall back to reading /dev/urandom if `getrandom` is not
53                     // supported on the current kernel.
54                     //
55                     // Also fall back in case it is disabled by something like
56                     // seccomp or inside of virtual machines.
57                     GETRANDOM_UNAVAILABLE.store(true, Ordering::Relaxed);
58                     return false;
59                 } else if err == libc::EAGAIN {
60                     return false;
61                 } else {
62                     panic!("unexpected getrandom error: {}", err);
63                 }
64             } else {
65                 read += result as usize;
66             }
67         }
68         true
69     }
70
71     pub fn fill_bytes(v: &mut [u8]) {
72         // getrandom_fill_bytes here can fail if getrandom() returns EAGAIN,
73         // meaning it would have blocked because the non-blocking pool (urandom)
74         // has not initialized in the kernel yet due to a lack of entropy. The
75         // fallback we do here is to avoid blocking applications which could
76         // depend on this call without ever knowing they do and don't have a
77         // work around. The PRNG of /dev/urandom will still be used but over a
78         // possibly predictable entropy pool.
79         if getrandom_fill_bytes(v) {
80             return;
81         }
82
83         // getrandom failed because it is permanently or temporarily (because
84         // of missing entropy) unavailable. Open /dev/urandom, read from it,
85         // and close it again.
86         let mut file = File::open("/dev/urandom").expect("failed to open /dev/urandom");
87         file.read_exact(v).expect("failed to read /dev/urandom")
88     }
89 }
90
91 #[cfg(target_os = "openbsd")]
92 mod imp {
93     use crate::sys::os::errno;
94
95     pub fn fill_bytes(v: &mut [u8]) {
96         // getentropy(2) permits a maximum buffer size of 256 bytes
97         for s in v.chunks_mut(256) {
98             let ret = unsafe {
99                 libc::getentropy(s.as_mut_ptr() as *mut libc::c_void, s.len())
100             };
101             if ret == -1 {
102                 panic!("unexpected getentropy error: {}", errno());
103             }
104         }
105     }
106 }
107
108 // On iOS and MacOS `SecRandomCopyBytes` calls `CCRandomCopyBytes` with
109 // `kCCRandomDefault`. `CCRandomCopyBytes` manages a CSPRNG which is seeded
110 // from `/dev/random` and which runs on its own thread accessed via GCD.
111 // This seems needlessly heavyweight for the purposes of generating two u64s
112 // once per thread in `hashmap_random_keys`. Therefore `SecRandomCopyBytes` is
113 // only used on iOS where direct access to `/dev/urandom` is blocked by the
114 // sandbox.
115 #[cfg(target_os = "ios")]
116 mod imp {
117     use crate::io;
118     use crate::ptr;
119     use libc::{c_int, size_t};
120
121     enum SecRandom {}
122
123     #[allow(non_upper_case_globals)]
124     const kSecRandomDefault: *const SecRandom = ptr::null();
125
126     extern {
127         fn SecRandomCopyBytes(rnd: *const SecRandom,
128                               count: size_t,
129                               bytes: *mut u8) -> c_int;
130     }
131
132     pub fn fill_bytes(v: &mut [u8]) {
133         let ret = unsafe {
134             SecRandomCopyBytes(kSecRandomDefault,
135                                v.len(),
136                                v.as_mut_ptr())
137         };
138         if ret == -1 {
139             panic!("couldn't generate random bytes: {}",
140                    io::Error::last_os_error());
141         }
142     }
143 }
144
145 #[cfg(target_os = "freebsd")]
146 mod imp {
147     use crate::ptr;
148
149     pub fn fill_bytes(v: &mut [u8]) {
150         let mib = [libc::CTL_KERN, libc::KERN_ARND];
151         // kern.arandom permits a maximum buffer size of 256 bytes
152         for s in v.chunks_mut(256) {
153             let mut s_len = s.len();
154             let ret = unsafe {
155                 libc::sysctl(mib.as_ptr(), mib.len() as libc::c_uint,
156                              s.as_mut_ptr() as *mut _, &mut s_len,
157                              ptr::null(), 0)
158             };
159             if ret == -1 || s_len != s.len() {
160                 panic!("kern.arandom sysctl failed! (returned {}, s.len() {}, oldlenp {})",
161                        ret, s.len(), s_len);
162             }
163         }
164     }
165 }
166
167 #[cfg(target_os = "fuchsia")]
168 mod imp {
169     #[link(name = "zircon")]
170     extern {
171         fn zx_cprng_draw(buffer: *mut u8, len: usize);
172     }
173
174     pub fn fill_bytes(v: &mut [u8]) {
175         unsafe { zx_cprng_draw(v.as_mut_ptr(), v.len()) }
176     }
177 }
178
179 #[cfg(target_os = "redox")]
180 mod imp {
181     use crate::fs::File;
182     use crate::io::Read;
183
184     pub fn fill_bytes(v: &mut [u8]) {
185         // Open rand:, read from it, and close it again.
186         let mut file = File::open("rand:").expect("failed to open rand:");
187         file.read_exact(v).expect("failed to read rand:")
188     }
189 }