1 | //! Implementations that just need to read from a file |
2 | use crate::{ |
3 | util_libc::{open_readonly, sys_fill_exact}, |
4 | Error, |
5 | }; |
6 | use core::{ |
7 | cell::UnsafeCell, |
8 | mem::MaybeUninit, |
9 | sync::atomic::{AtomicUsize, Ordering::Relaxed}, |
10 | }; |
11 | |
12 | // We prefer using /dev/urandom and only use /dev/random if the OS |
13 | // documentation indicates that /dev/urandom is insecure. |
14 | // On Solaris/Illumos, see src/solaris_illumos.rs |
15 | // On Dragonfly, Haiku, and QNX Neutrino the devices are identical. |
16 | #[cfg (any(target_os = "solaris" , target_os = "illumos" ))] |
17 | const FILE_PATH: &str = "/dev/random \0" ; |
18 | #[cfg (any( |
19 | target_os = "aix" , |
20 | target_os = "android" , |
21 | target_os = "linux" , |
22 | target_os = "redox" , |
23 | target_os = "dragonfly" , |
24 | target_os = "haiku" , |
25 | target_os = "nto" , |
26 | ))] |
27 | const FILE_PATH: &str = "/dev/urandom \0" ; |
28 | const FD_UNINIT: usize = usize::max_value(); |
29 | |
30 | pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> { |
31 | let fd: i32 = get_rng_fd()?; |
32 | sys_fill_exact(buf:dest, |buf: &mut [MaybeUninit]| unsafe { |
33 | libc::read(fd, buf:buf.as_mut_ptr() as *mut libc::c_void, count:buf.len()) |
34 | }) |
35 | } |
36 | |
37 | // Returns the file descriptor for the device file used to retrieve random |
38 | // bytes. The file will be opened exactly once. All subsequent calls will |
39 | // return the same file descriptor. This file descriptor is never closed. |
40 | fn get_rng_fd() -> Result<libc::c_int, Error> { |
41 | static FD: AtomicUsize = AtomicUsize::new(FD_UNINIT); |
42 | fn get_fd() -> Option<libc::c_int> { |
43 | match FD.load(Relaxed) { |
44 | FD_UNINIT => None, |
45 | val => Some(val as libc::c_int), |
46 | } |
47 | } |
48 | |
49 | // Use double-checked locking to avoid acquiring the lock if possible. |
50 | if let Some(fd) = get_fd() { |
51 | return Ok(fd); |
52 | } |
53 | |
54 | // SAFETY: We use the mutex only in this method, and we always unlock it |
55 | // before returning, making sure we don't violate the pthread_mutex_t API. |
56 | static MUTEX: Mutex = Mutex::new(); |
57 | unsafe { MUTEX.lock() }; |
58 | let _guard = DropGuard(|| unsafe { MUTEX.unlock() }); |
59 | |
60 | if let Some(fd) = get_fd() { |
61 | return Ok(fd); |
62 | } |
63 | |
64 | // On Linux, /dev/urandom might return insecure values. |
65 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
66 | wait_until_rng_ready()?; |
67 | |
68 | let fd = unsafe { open_readonly(FILE_PATH)? }; |
69 | // The fd always fits in a usize without conflicting with FD_UNINIT. |
70 | debug_assert!(fd >= 0 && (fd as usize) < FD_UNINIT); |
71 | FD.store(fd as usize, Relaxed); |
72 | |
73 | Ok(fd) |
74 | } |
75 | |
76 | // Succeeds once /dev/urandom is safe to read from |
77 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
78 | fn wait_until_rng_ready() -> Result<(), Error> { |
79 | // Poll /dev/random to make sure it is ok to read from /dev/urandom. |
80 | let fd = unsafe { open_readonly("/dev/random \0" )? }; |
81 | let mut pfd = libc::pollfd { |
82 | fd, |
83 | events: libc::POLLIN, |
84 | revents: 0, |
85 | }; |
86 | let _guard = DropGuard(|| unsafe { |
87 | libc::close(fd); |
88 | }); |
89 | |
90 | loop { |
91 | // A negative timeout means an infinite timeout. |
92 | let res = unsafe { libc::poll(&mut pfd, 1, -1) }; |
93 | if res >= 0 { |
94 | debug_assert_eq!(res, 1); // We only used one fd, and cannot timeout. |
95 | return Ok(()); |
96 | } |
97 | let err = crate::util_libc::last_os_error(); |
98 | match err.raw_os_error() { |
99 | Some(libc::EINTR) | Some(libc::EAGAIN) => continue, |
100 | _ => return Err(err), |
101 | } |
102 | } |
103 | } |
104 | |
105 | struct Mutex(UnsafeCell<libc::pthread_mutex_t>); |
106 | |
107 | impl Mutex { |
108 | const fn new() -> Self { |
109 | Self(UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER)) |
110 | } |
111 | unsafe fn lock(&self) { |
112 | let r: i32 = libc::pthread_mutex_lock(self.0.get()); |
113 | debug_assert_eq!(r, 0); |
114 | } |
115 | unsafe fn unlock(&self) { |
116 | let r: i32 = libc::pthread_mutex_unlock(self.0.get()); |
117 | debug_assert_eq!(r, 0); |
118 | } |
119 | } |
120 | |
121 | unsafe impl Sync for Mutex {} |
122 | |
123 | struct DropGuard<F: FnMut()>(F); |
124 | |
125 | impl<F: FnMut()> Drop for DropGuard<F> { |
126 | fn drop(&mut self) { |
127 | self.0() |
128 | } |
129 | } |
130 | |