]> git.lizzy.rs Git - rust.git/blob - src/libstd/sys/unix/rand.rs
Fix an endless loop when `getrandom` is not available
[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 use mem;
12 use slice;
13
14 pub fn hashmap_random_keys() -> (u64, u64) {
15     let mut v = (0, 0);
16     unsafe {
17         let view = slice::from_raw_parts_mut(&mut v as *mut _ as *mut u8,
18                                              mem::size_of_val(&v));
19         imp::fill_bytes(view);
20     }
21     return v
22 }
23
24 #[cfg(all(unix,
25           not(target_os = "ios"),
26           not(target_os = "openbsd"),
27           not(target_os = "freebsd"),
28           not(target_os = "fuchsia")))]
29 mod imp {
30     use fs::File;
31     use io::Read;
32     #[cfg(any(target_os = "linux", target_os = "android"))]
33     use libc;
34
35     #[cfg(any(target_os = "linux", target_os = "android"))]
36     fn getrandom(buf: &mut [u8]) -> libc::c_long {
37         unsafe {
38             libc::syscall(libc::SYS_getrandom, buf.as_mut_ptr(), buf.len(), libc::GRND_NONBLOCK)
39         }
40     }
41
42     #[cfg(not(any(target_os = "linux", target_os = "android")))]
43     fn getrandom_fill_bytes(_buf: &mut [u8]) -> bool { false }
44
45     #[cfg(any(target_os = "linux", target_os = "android"))]
46     fn getrandom_fill_bytes(v: &mut [u8]) -> bool {
47         use sync::atomic::{AtomicBool, Ordering};
48         use sys::os::errno;
49
50         static GETRANDOM_UNAVAILABLE: AtomicBool = AtomicBool::new(false);
51         if GETRANDOM_UNAVAILABLE.load(Ordering::Relaxed) {
52             return false;
53         }
54
55         let mut read = 0;
56         while read < v.len() {
57             let result = getrandom(&mut v[read..]);
58             if result == -1 {
59                 let err = errno() as libc::c_int;
60                 if err == libc::EINTR {
61                     continue;
62                 } else if err == libc::ENOSYS {
63                     GETRANDOM_UNAVAILABLE.store(true, Ordering::Relaxed);
64                     return false;
65                 } else if err == libc::EAGAIN {
66                     return false;
67                 } else {
68                     panic!("unexpected getrandom error: {}", err);
69                 }
70             } else {
71                 read += result as usize;
72             }
73         }
74         true
75     }
76
77     pub fn fill_bytes(v: &mut [u8]) {
78         // getrandom_fill_bytes here can fail if getrandom() returns EAGAIN,
79         // meaning it would have blocked because the non-blocking pool (urandom)
80         // has not initialized in the kernel yet due to a lack of entropy. The
81         // fallback we do here is to avoid blocking applications which could
82         // depend on this call without ever knowing they do and don't have a
83         // work around. The PRNG of /dev/urandom will still be used but over a
84         // possibly predictable entropy pool.
85         if getrandom_fill_bytes(v) {
86             return;
87         }
88
89         // getrandom failed because it is permanently or temporarily (because
90         // of missing entropy) unavailable. Open /dev/urandom, read from it,
91         // and close it again.
92         let mut file = File::open("/dev/urandom").expect("failed to open /dev/urandom");
93         file.read_exact(v).expect("failed to read /dev/urandom")
94     }
95 }
96
97 #[cfg(target_os = "openbsd")]
98 mod imp {
99     use libc;
100     use sys::os::errno;
101
102     pub fn fill_bytes(v: &mut [u8]) {
103         // getentropy(2) permits a maximum buffer size of 256 bytes
104         for s in v.chunks_mut(256) {
105             let ret = unsafe {
106                 libc::getentropy(s.as_mut_ptr() as *mut libc::c_void, s.len())
107             };
108             if ret == -1 {
109                 panic!("unexpected getentropy error: {}", errno());
110             }
111         }
112     }
113 }
114
115 #[cfg(target_os = "ios")]
116 mod imp {
117     use io;
118     use libc::{c_int, size_t};
119     use ptr;
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 libc;
148     use ptr;
149
150     pub fn fill_bytes(v: &mut [u8]) {
151         let mib = [libc::CTL_KERN, libc::KERN_ARND];
152         // kern.arandom permits a maximum buffer size of 256 bytes
153         for s in v.chunks_mut(256) {
154             let mut s_len = s.len();
155             let ret = unsafe {
156                 libc::sysctl(mib.as_ptr(), mib.len() as libc::c_uint,
157                              s.as_mut_ptr() as *mut _, &mut s_len,
158                              ptr::null(), 0)
159             };
160             if ret == -1 || s_len != s.len() {
161                 panic!("kern.arandom sysctl failed! (returned {}, s.len() {}, oldlenp {})",
162                        ret, s.len(), s_len);
163             }
164         }
165     }
166 }
167
168 #[cfg(target_os = "fuchsia")]
169 mod imp {
170     #[link(name = "zircon")]
171     extern {
172         fn zx_cprng_draw(buffer: *mut u8, len: usize);
173     }
174
175     pub fn fill_bytes(v: &mut [u8]) {
176         unsafe { zx_cprng_draw(v.as_mut_ptr(), v.len()) }
177     }
178 }