]> git.lizzy.rs Git - rust.git/blob - src/libstd/sys/unix/rand.rs
Unignore u128 test for stage 0,1
[rust.git] / src / libstd / sys / unix / rand.rs
1 // Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 pub use self::imp::OsRng;
12
13 use mem;
14
15 fn next_u32(mut fill_buf: &mut FnMut(&mut [u8])) -> u32 {
16     let mut buf: [u8; 4] = [0; 4];
17     fill_buf(&mut buf);
18     unsafe { mem::transmute::<[u8; 4], u32>(buf) }
19 }
20
21 fn next_u64(mut fill_buf: &mut FnMut(&mut [u8])) -> u64 {
22     let mut buf: [u8; 8] = [0; 8];
23     fill_buf(&mut buf);
24     unsafe { mem::transmute::<[u8; 8], u64>(buf) }
25 }
26
27 #[cfg(all(unix,
28           not(target_os = "ios"),
29           not(target_os = "openbsd"),
30           not(target_os = "freebsd"),
31           not(target_os = "fuchsia")))]
32 mod imp {
33     use self::OsRngInner::*;
34     use super::{next_u32, next_u64};
35
36     use fs::File;
37     use io;
38     use libc;
39     use rand::Rng;
40     use rand::reader::ReaderRng;
41     use sys::os::errno;
42
43     #[cfg(all(target_os = "linux",
44               any(target_arch = "x86_64",
45                   target_arch = "x86",
46                   target_arch = "arm",
47                   target_arch = "aarch64",
48                   target_arch = "powerpc",
49                   target_arch = "powerpc64",
50                   target_arch = "s390x")))]
51     fn getrandom(buf: &mut [u8]) -> libc::c_long {
52         #[cfg(target_arch = "x86_64")]
53         const NR_GETRANDOM: libc::c_long = 318;
54         #[cfg(target_arch = "x86")]
55         const NR_GETRANDOM: libc::c_long = 355;
56         #[cfg(target_arch = "arm")]
57         const NR_GETRANDOM: libc::c_long = 384;
58         #[cfg(target_arch = "s390x")]
59         const NR_GETRANDOM: libc::c_long = 349;
60         #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))]
61         const NR_GETRANDOM: libc::c_long = 359;
62         #[cfg(target_arch = "aarch64")]
63         const NR_GETRANDOM: libc::c_long = 278;
64
65         const GRND_NONBLOCK: libc::c_uint = 0x0001;
66
67         unsafe {
68             libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)
69         }
70     }
71
72     #[cfg(not(all(target_os = "linux",
73                   any(target_arch = "x86_64",
74                       target_arch = "x86",
75                       target_arch = "arm",
76                       target_arch = "aarch64",
77                       target_arch = "powerpc",
78                       target_arch = "powerpc64",
79                       target_arch = "s390x"))))]
80     fn getrandom(_buf: &mut [u8]) -> libc::c_long { -1 }
81
82     fn getrandom_fill_bytes(v: &mut [u8]) {
83         let mut read = 0;
84         while read < v.len() {
85             let result = getrandom(&mut v[read..]);
86             if result == -1 {
87                 let err = errno() as libc::c_int;
88                 if err == libc::EINTR {
89                     continue;
90                 } else if err == libc::EAGAIN {
91                     // if getrandom() returns EAGAIN it would have blocked
92                     // because the non-blocking pool (urandom) has not
93                     // initialized in the kernel yet due to a lack of entropy
94                     // the fallback we do here is to avoid blocking applications
95                     // which could depend on this call without ever knowing
96                     // they do and don't have a work around. The PRNG of
97                     // /dev/urandom will still be used but not over a completely
98                     // full entropy pool
99                     let reader = File::open("/dev/urandom").expect("Unable to open /dev/urandom");
100                     let mut reader_rng = ReaderRng::new(reader);
101                     reader_rng.fill_bytes(&mut v[read..]);
102                     read += v.len();
103                 } else {
104                     panic!("unexpected getrandom error: {}", err);
105                 }
106             } else {
107                 read += result as usize;
108             }
109         }
110     }
111
112     #[cfg(all(target_os = "linux",
113               any(target_arch = "x86_64",
114                   target_arch = "x86",
115                   target_arch = "arm",
116                   target_arch = "aarch64",
117                   target_arch = "powerpc",
118                   target_arch = "powerpc64",
119                   target_arch = "s390x")))]
120     fn is_getrandom_available() -> bool {
121         use sync::atomic::{AtomicBool, Ordering};
122         use sync::Once;
123
124         static CHECKER: Once = Once::new();
125         static AVAILABLE: AtomicBool = AtomicBool::new(false);
126
127         CHECKER.call_once(|| {
128             let mut buf: [u8; 0] = [];
129             let result = getrandom(&mut buf);
130             let available = if result == -1 {
131                 let err = io::Error::last_os_error().raw_os_error();
132                 err != Some(libc::ENOSYS)
133             } else {
134                 true
135             };
136             AVAILABLE.store(available, Ordering::Relaxed);
137         });
138
139         AVAILABLE.load(Ordering::Relaxed)
140     }
141
142     #[cfg(not(all(target_os = "linux",
143                   any(target_arch = "x86_64",
144                       target_arch = "x86",
145                       target_arch = "arm",
146                       target_arch = "aarch64",
147                       target_arch = "powerpc",
148                       target_arch = "powerpc64",
149                       target_arch = "s390x"))))]
150     fn is_getrandom_available() -> bool { false }
151
152     pub struct OsRng {
153         inner: OsRngInner,
154     }
155
156     enum OsRngInner {
157         OsGetrandomRng,
158         OsReaderRng(ReaderRng<File>),
159     }
160
161     impl OsRng {
162         /// Create a new `OsRng`.
163         pub fn new() -> io::Result<OsRng> {
164             if is_getrandom_available() {
165                 return Ok(OsRng { inner: OsGetrandomRng });
166             }
167
168             let reader = File::open("/dev/urandom")?;
169             let reader_rng = ReaderRng::new(reader);
170
171             Ok(OsRng { inner: OsReaderRng(reader_rng) })
172         }
173     }
174
175     impl Rng for OsRng {
176         fn next_u32(&mut self) -> u32 {
177             match self.inner {
178                 OsGetrandomRng => next_u32(&mut getrandom_fill_bytes),
179                 OsReaderRng(ref mut rng) => rng.next_u32(),
180             }
181         }
182         fn next_u64(&mut self) -> u64 {
183             match self.inner {
184                 OsGetrandomRng => next_u64(&mut getrandom_fill_bytes),
185                 OsReaderRng(ref mut rng) => rng.next_u64(),
186             }
187         }
188         fn fill_bytes(&mut self, v: &mut [u8]) {
189             match self.inner {
190                 OsGetrandomRng => getrandom_fill_bytes(v),
191                 OsReaderRng(ref mut rng) => rng.fill_bytes(v)
192             }
193         }
194     }
195 }
196
197 #[cfg(target_os = "openbsd")]
198 mod imp {
199     use super::{next_u32, next_u64};
200
201     use io;
202     use libc;
203     use sys::os::errno;
204     use rand::Rng;
205
206     pub struct OsRng {
207         // dummy field to ensure that this struct cannot be constructed outside
208         // of this module
209         _dummy: (),
210     }
211
212     impl OsRng {
213         /// Create a new `OsRng`.
214         pub fn new() -> io::Result<OsRng> {
215             Ok(OsRng { _dummy: () })
216         }
217     }
218
219     impl Rng for OsRng {
220         fn next_u32(&mut self) -> u32 {
221             next_u32(&mut |v| self.fill_bytes(v))
222         }
223         fn next_u64(&mut self) -> u64 {
224             next_u64(&mut |v| self.fill_bytes(v))
225         }
226         fn fill_bytes(&mut self, v: &mut [u8]) {
227             // getentropy(2) permits a maximum buffer size of 256 bytes
228             for s in v.chunks_mut(256) {
229                 let ret = unsafe {
230                     libc::getentropy(s.as_mut_ptr() as *mut libc::c_void, s.len())
231                 };
232                 if ret == -1 {
233                     panic!("unexpected getentropy error: {}", errno());
234                 }
235             }
236         }
237     }
238 }
239
240 #[cfg(target_os = "ios")]
241 mod imp {
242     use super::{next_u32, next_u64};
243
244     use io;
245     use ptr;
246     use rand::Rng;
247     use libc::{c_int, size_t};
248
249     pub struct OsRng {
250         // dummy field to ensure that this struct cannot be constructed outside
251         // of this module
252         _dummy: (),
253     }
254
255     enum SecRandom {}
256
257     #[allow(non_upper_case_globals)]
258     const kSecRandomDefault: *const SecRandom = ptr::null();
259
260     #[link(name = "Security", kind = "framework")]
261     #[cfg(not(cargobuild))]
262     extern {}
263
264     extern {
265         fn SecRandomCopyBytes(rnd: *const SecRandom,
266                               count: size_t, bytes: *mut u8) -> c_int;
267     }
268
269     impl OsRng {
270         /// Create a new `OsRng`.
271         pub fn new() -> io::Result<OsRng> {
272             Ok(OsRng { _dummy: () })
273         }
274     }
275
276     impl Rng for OsRng {
277         fn next_u32(&mut self) -> u32 {
278             next_u32(&mut |v| self.fill_bytes(v))
279         }
280         fn next_u64(&mut self) -> u64 {
281             next_u64(&mut |v| self.fill_bytes(v))
282         }
283         fn fill_bytes(&mut self, v: &mut [u8]) {
284             let ret = unsafe {
285                 SecRandomCopyBytes(kSecRandomDefault, v.len(),
286                                    v.as_mut_ptr())
287             };
288             if ret == -1 {
289                 panic!("couldn't generate random bytes: {}",
290                        io::Error::last_os_error());
291             }
292         }
293     }
294 }
295
296 #[cfg(target_os = "freebsd")]
297 mod imp {
298     use super::{next_u32, next_u64};
299
300     use io;
301     use libc;
302     use rand::Rng;
303     use ptr;
304
305     pub struct OsRng {
306         // dummy field to ensure that this struct cannot be constructed outside
307         // of this module
308         _dummy: (),
309     }
310
311     impl OsRng {
312         /// Create a new `OsRng`.
313         pub fn new() -> io::Result<OsRng> {
314             Ok(OsRng { _dummy: () })
315         }
316     }
317
318     impl Rng for OsRng {
319         fn next_u32(&mut self) -> u32 {
320             next_u32(&mut |v| self.fill_bytes(v))
321         }
322         fn next_u64(&mut self) -> u64 {
323             next_u64(&mut |v| self.fill_bytes(v))
324         }
325         fn fill_bytes(&mut self, v: &mut [u8]) {
326             let mib = [libc::CTL_KERN, libc::KERN_ARND];
327             // kern.arandom permits a maximum buffer size of 256 bytes
328             for s in v.chunks_mut(256) {
329                 let mut s_len = s.len();
330                 let ret = unsafe {
331                     libc::sysctl(mib.as_ptr(), mib.len() as libc::c_uint,
332                                  s.as_mut_ptr() as *mut _, &mut s_len,
333                                  ptr::null(), 0)
334                 };
335                 if ret == -1 || s_len != s.len() {
336                     panic!("kern.arandom sysctl failed! (returned {}, s.len() {}, oldlenp {})",
337                            ret, s.len(), s_len);
338                 }
339             }
340         }
341     }
342 }
343
344 #[cfg(target_os = "fuchsia")]
345 mod imp {
346     use super::{next_u32, next_u64};
347
348     use io;
349     use rand::Rng;
350
351     #[link(name = "magenta")]
352     extern {
353         fn mx_cprng_draw(buffer: *mut u8, len: usize, actual: *mut usize) -> i32;
354     }
355
356     fn getrandom(buf: &mut [u8]) -> Result<usize, i32> {
357         unsafe {
358             let mut actual = 0;
359             let status = mx_cprng_draw(buf.as_mut_ptr(), buf.len(), &mut actual);
360             if status == 0 {
361                 Ok(actual)
362             } else {
363                 Err(status)
364             }
365         }
366     }
367
368     pub struct OsRng {
369         // dummy field to ensure that this struct cannot be constructed outside
370         // of this module
371         _dummy: (),
372     }
373
374     impl OsRng {
375         /// Create a new `OsRng`.
376         pub fn new() -> io::Result<OsRng> {
377             Ok(OsRng { _dummy: () })
378         }
379     }
380
381     impl Rng for OsRng {
382         fn next_u32(&mut self) -> u32 {
383             next_u32(&mut |v| self.fill_bytes(v))
384         }
385         fn next_u64(&mut self) -> u64 {
386             next_u64(&mut |v| self.fill_bytes(v))
387         }
388         fn fill_bytes(&mut self, v: &mut [u8]) {
389             let mut buf = v;
390             while !buf.is_empty() {
391                 let ret = getrandom(buf);
392                 match ret {
393                     Err(err) => {
394                         panic!("kernel mx_cprng_draw call failed! (returned {}, buf.len() {})",
395                             err, buf.len())
396                     }
397                     Ok(actual) => {
398                         let move_buf = buf;
399                         buf = &mut move_buf[(actual as usize)..];
400                     }
401                 }
402             }
403         }
404     }
405 }