@@ -4,10 +4,9 @@ use crate::{
4
4
Error ,
5
5
} ;
6
6
use core:: {
7
- cell:: UnsafeCell ,
8
7
ffi:: c_void,
9
8
mem:: MaybeUninit ,
10
- sync:: atomic:: { AtomicUsize , Ordering :: Relaxed } ,
9
+ sync:: atomic:: { AtomicUsize , Ordering } ,
11
10
} ;
12
11
13
12
/// For all platforms, we use `/dev/urandom` rather than `/dev/random`.
@@ -18,6 +17,7 @@ use core::{
18
17
/// - On Haiku and QNX Neutrino they are identical.
19
18
const FILE_PATH : & [ u8 ] = b"/dev/urandom\0 " ;
20
19
const FD_UNINIT : usize = usize:: MAX ;
20
+ const FD_ONGOING_INIT : usize = usize:: MAX - 1 ;
21
21
22
22
// Do not inline this when it is the fallback implementation, but don't mark it
23
23
// `#[cold]` because it is hot when it is actually used.
@@ -35,42 +35,70 @@ pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
35
35
fn get_rng_fd ( ) -> Result < libc:: c_int , Error > {
36
36
static FD : AtomicUsize = AtomicUsize :: new ( FD_UNINIT ) ;
37
37
38
- fn get_fd ( ) -> Option < libc:: c_int > {
39
- match FD . load ( Relaxed ) {
40
- FD_UNINIT => None ,
41
- val => Some ( val as libc:: c_int ) ,
42
- }
43
- }
44
-
45
38
#[ cold]
46
- fn get_fd_locked ( ) -> Result < libc:: c_int , Error > {
47
- // SAFETY: We use the mutex only in this method, and we always unlock it
48
- // before returning, making sure we don't violate the pthread_mutex_t API.
49
- static MUTEX : Mutex = Mutex :: new ( ) ;
50
- unsafe { MUTEX . lock ( ) } ;
51
- let _guard = DropGuard ( || unsafe { MUTEX . unlock ( ) } ) ;
52
-
53
- if let Some ( fd) = get_fd ( ) {
54
- return Ok ( fd) ;
39
+ fn init_or_wait_fd ( ) -> Result < libc:: c_int , Error > {
40
+ // Maximum sleep time (~268 milliseconds)
41
+ let max_sleep_ns = 1 << 28 ;
42
+ // Starting sleep time (~4 microseconds)
43
+ let mut timeout_ns = 1 << 12 ;
44
+ loop {
45
+ match FD . load ( Ordering :: Acquire ) {
46
+ FD_UNINIT => { }
47
+ FD_ONGOING_INIT => {
48
+ let rqtp = libc:: timespec {
49
+ tv_sec : 0 ,
50
+ tv_nsec : timeout_ns,
51
+ } ;
52
+ let mut rmtp = libc:: timespec {
53
+ tv_sec : 0 ,
54
+ tv_nsec : 0 ,
55
+ } ;
56
+ unsafe {
57
+ libc:: nanosleep ( & rqtp, & mut rmtp) ;
58
+ }
59
+ if timeout_ns < max_sleep_ns {
60
+ timeout_ns *= 2 ;
61
+ }
62
+ continue ;
63
+ }
64
+ val => return Ok ( val as libc:: c_int ) ,
65
+ }
66
+
67
+ let xch_res = FD . compare_exchange_weak (
68
+ FD_UNINIT ,
69
+ FD_ONGOING_INIT ,
70
+ Ordering :: AcqRel ,
71
+ Ordering :: Relaxed ,
72
+ ) ;
73
+ if xch_res. is_err ( ) {
74
+ continue ;
75
+ }
76
+
77
+ let res = open_fd ( ) ;
78
+ let val = match res {
79
+ Ok ( fd) => fd as usize ,
80
+ Err ( _) => FD_UNINIT ,
81
+ } ;
82
+ FD . store ( val, Ordering :: Release ) ;
83
+ return res;
55
84
}
85
+ }
56
86
87
+ fn open_fd ( ) -> Result < libc:: c_int , Error > {
57
88
// On Linux, /dev/urandom might return insecure values.
58
89
#[ cfg( any( target_os = "android" , target_os = "linux" ) ) ]
59
90
wait_until_rng_ready ( ) ?;
60
91
61
92
let fd = open_readonly ( FILE_PATH ) ?;
62
93
// The fd always fits in a usize without conflicting with FD_UNINIT.
63
- debug_assert ! ( fd >= 0 && ( fd as usize ) < FD_UNINIT ) ;
64
- FD . store ( fd as usize , Relaxed ) ;
94
+ debug_assert ! ( fd >= 0 && ( fd as usize ) < FD_ONGOING_INIT ) ;
65
95
66
96
Ok ( fd)
67
97
}
68
98
69
- // Use double-checked locking to avoid acquiring the lock if possible.
70
- if let Some ( fd) = get_fd ( ) {
71
- Ok ( fd)
72
- } else {
73
- get_fd_locked ( )
99
+ match FD . load ( Ordering :: Relaxed ) {
100
+ FD_UNINIT | FD_ONGOING_INIT => init_or_wait_fd ( ) ,
101
+ val => Ok ( val as libc:: c_int ) ,
74
102
}
75
103
}
76
104
@@ -104,6 +132,14 @@ fn get_rng_fd() -> Result<libc::c_int, Error> {
104
132
// libsodium uses `libc::poll` similarly to this.
105
133
#[ cfg( any( target_os = "android" , target_os = "linux" ) ) ]
106
134
fn wait_until_rng_ready ( ) -> Result < ( ) , Error > {
135
+ struct DropGuard < F : FnMut ( ) > ( F ) ;
136
+
137
+ impl < F : FnMut ( ) > Drop for DropGuard < F > {
138
+ fn drop ( & mut self ) {
139
+ self . 0 ( )
140
+ }
141
+ }
142
+
107
143
let fd = open_readonly ( b"/dev/random\0 " ) ?;
108
144
let mut pfd = libc:: pollfd {
109
145
fd,
@@ -128,29 +164,3 @@ fn wait_until_rng_ready() -> Result<(), Error> {
128
164
}
129
165
}
130
166
}
131
-
132
- struct Mutex ( UnsafeCell < libc:: pthread_mutex_t > ) ;
133
-
134
- impl Mutex {
135
- const fn new ( ) -> Self {
136
- Self ( UnsafeCell :: new ( libc:: PTHREAD_MUTEX_INITIALIZER ) )
137
- }
138
- unsafe fn lock ( & self ) {
139
- let r = libc:: pthread_mutex_lock ( self . 0 . get ( ) ) ;
140
- debug_assert_eq ! ( r, 0 ) ;
141
- }
142
- unsafe fn unlock ( & self ) {
143
- let r = libc:: pthread_mutex_unlock ( self . 0 . get ( ) ) ;
144
- debug_assert_eq ! ( r, 0 ) ;
145
- }
146
- }
147
-
148
- unsafe impl Sync for Mutex { }
149
-
150
- struct DropGuard < F : FnMut ( ) > ( F ) ;
151
-
152
- impl < F : FnMut ( ) > Drop for DropGuard < F > {
153
- fn drop ( & mut self ) {
154
- self . 0 ( )
155
- }
156
- }
0 commit comments