@@ -4,10 +4,9 @@ use crate::{
44 Error ,
55} ;
66use core:: {
7- cell:: UnsafeCell ,
87 ffi:: c_void,
98 mem:: MaybeUninit ,
10- sync:: atomic:: { AtomicUsize , Ordering :: Relaxed } ,
9+ sync:: atomic:: { AtomicUsize , Ordering } ,
1110} ;
1211
1312/// For all platforms, we use `/dev/urandom` rather than `/dev/random`.
@@ -18,6 +17,7 @@ use core::{
1817/// - On Haiku and QNX Neutrino they are identical.
1918const FILE_PATH : & [ u8 ] = b"/dev/urandom\0 " ;
2019const FD_UNINIT : usize = usize:: MAX ;
20+ const FD_ONGOING_INIT : usize = usize:: MAX - 1 ;
2121
2222// Do not inline this when it is the fallback implementation, but don't mark it
2323// `#[cold]` because it is hot when it is actually used.
@@ -35,42 +35,70 @@ pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
3535fn get_rng_fd ( ) -> Result < libc:: c_int , Error > {
3636 static FD : AtomicUsize = AtomicUsize :: new ( FD_UNINIT ) ;
3737
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-
4538 #[ 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;
5584 }
85+ }
5686
87+ fn open_fd ( ) -> Result < libc:: c_int , Error > {
5788 // On Linux, /dev/urandom might return insecure values.
5889 #[ cfg( any( target_os = "android" , target_os = "linux" ) ) ]
5990 wait_until_rng_ready ( ) ?;
6091
6192 let fd = open_readonly ( FILE_PATH ) ?;
6293 // 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 ) ;
6595
6696 Ok ( fd)
6797 }
6898
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 ) ,
74102 }
75103}
76104
@@ -104,6 +132,14 @@ fn get_rng_fd() -> Result<libc::c_int, Error> {
104132// libsodium uses `libc::poll` similarly to this.
105133#[ cfg( any( target_os = "android" , target_os = "linux" ) ) ]
106134fn 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+
107143 let fd = open_readonly ( b"/dev/random\0 " ) ?;
108144 let mut pfd = libc:: pollfd {
109145 fd,
@@ -128,29 +164,3 @@ fn wait_until_rng_ready() -> Result<(), Error> {
128164 }
129165 }
130166}
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