@@ -30,8 +30,23 @@ mod imp {
3030 use fs:: File ;
3131 use io:: Read ;
3232 use libc;
33+ use sync:: atomic:: { AtomicBool , AtomicI32 , Ordering } ;
3334 use sys:: os:: errno;
3435
36+ static GETRANDOM_URANDOM_FD : AtomicI32 = AtomicI32 :: new ( -1 ) ;
37+ #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
38+ static GETRANDOM_UNAVAILABLE : AtomicBool = AtomicBool :: new ( false ) ;
39+
40+ #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
41+ fn is_getrandom_permanently_unavailable ( ) -> bool {
42+ GETRANDOM_UNAVAILABLE . load ( Ordering :: Relaxed )
43+ }
44+
45+ #[ cfg( not( any( target_os = "linux" , target_os = "android" ) ) ) ]
46+ fn is_getrandom_permanently_unavailable ( ) -> bool {
47+ true
48+ }
49+
3550 #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
3651 fn getrandom ( buf : & mut [ u8 ] ) -> libc:: c_long {
3752 unsafe {
@@ -40,71 +55,74 @@ mod imp {
4055 }
4156
4257 #[ cfg( not( any( target_os = "linux" , target_os = "android" ) ) ) ]
43- fn getrandom ( _buf : & mut [ u8 ] ) -> libc :: c_long { - 1 }
58+ fn getrandom_fill_bytes ( _buf : & mut [ u8 ] ) -> bool { false }
4459
60+ #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
4561 fn getrandom_fill_bytes ( v : & mut [ u8 ] ) -> bool {
62+ if is_getrandom_permanently_unavailable ( ) {
63+ return false ;
64+ }
65+
4666 let mut read = 0 ;
4767 while read < v. len ( ) {
4868 let result = getrandom ( & mut v[ read..] ) ;
4969 if result == -1 {
5070 let err = errno ( ) as libc:: c_int ;
5171 if err == libc:: EINTR {
5272 continue ;
73+ } else if err == libc:: ENOSYS {
74+ GETRANDOM_UNAVAILABLE . store ( true , Ordering :: Relaxed ) ;
5375 } else if err == libc:: EAGAIN {
54- return false
76+ return false ;
5577 } else {
5678 panic ! ( "unexpected getrandom error: {}" , err) ;
5779 }
5880 } else {
5981 read += result as usize ;
6082 }
6183 }
62-
63- return true
84+ true
6485 }
6586
66- #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
67- fn is_getrandom_available ( ) -> bool {
68- use io;
69- use sync:: atomic:: { AtomicBool , Ordering } ;
70- use sync:: Once ;
71-
72- static CHECKER : Once = Once :: new ( ) ;
73- static AVAILABLE : AtomicBool = AtomicBool :: new ( false ) ;
74-
75- CHECKER . call_once ( || {
76- let mut buf: [ u8 ; 0 ] = [ ] ;
77- let result = getrandom ( & mut buf) ;
78- let available = if result == -1 {
79- let err = io:: Error :: last_os_error ( ) . raw_os_error ( ) ;
80- err != Some ( libc:: ENOSYS )
81- } else {
82- true
83- } ;
84- AVAILABLE . store ( available, Ordering :: Relaxed ) ;
85- } ) ;
86-
87- AVAILABLE . load ( Ordering :: Relaxed )
88- }
89-
90- #[ cfg( not( any( target_os = "linux" , target_os = "android" ) ) ) ]
91- fn is_getrandom_available ( ) -> bool { false }
92-
9387 pub fn fill_bytes ( v : & mut [ u8 ] ) {
9488 // getrandom_fill_bytes here can fail if getrandom() returns EAGAIN,
9589 // meaning it would have blocked because the non-blocking pool (urandom)
96- // has not initialized in the kernel yet due to a lack of entropy the
90+ // has not initialized in the kernel yet due to a lack of entropy. The
9791 // fallback we do here is to avoid blocking applications which could
9892 // depend on this call without ever knowing they do and don't have a
99- // work around. The PRNG of /dev/urandom will still be used but not
100- // over a completely full entropy pool
101- if is_getrandom_available ( ) && getrandom_fill_bytes ( v) {
102- return
93+ // work around. The PRNG of /dev/urandom will still be used but over a
94+ // possibly predictable entropy pool.
95+ if getrandom_fill_bytes ( v) {
96+ return ;
10397 }
10498
105- let mut file = File :: open ( "/dev/urandom" )
106- . expect ( "failed to open /dev/urandom" ) ;
107- file. read_exact ( v) . expect ( "failed to read /dev/urandom" ) ;
99+ // getrandom failed for some reason. If the getrandom call is
100+ // permanently unavailable (OS without getrandom, or OS version without
101+ // getrandom), we'll keep around the fd for /dev/urandom for future
102+ // requests, to avoid re-opening the file on every call.
103+ //
104+ // Otherwise, open /dev/urandom, read from it, and close it again.
105+ use super :: super :: ext:: io:: { FromRawFd , IntoRawFd } ;
106+ let mut fd = GETRANDOM_URANDOM_FD . load ( Ordering :: Relaxed ) ;
107+ let mut close_fd = false ;
108+ if fd == -1 {
109+ if !is_getrandom_permanently_unavailable ( ) {
110+ close_fd = true ;
111+ }
112+ let file = File :: open ( "/dev/urandom" ) . expect ( "failed to open /dev/urandom" ) ;
113+ fd = file. into_raw_fd ( ) ;
114+ // If some other thread also opened /dev/urandom and set the global
115+ // fd already, we close our fd at the end of the function.
116+ if !close_fd && GETRANDOM_URANDOM_FD . compare_and_swap ( -1 , fd, Ordering :: Relaxed ) != -1 {
117+ close_fd = true ;
118+ }
119+ }
120+ let mut file = unsafe { File :: from_raw_fd ( fd) } ;
121+ let res = file. read_exact ( v) ;
122+ if !close_fd {
123+ let _ = file. into_raw_fd ( ) ;
124+ }
125+ res. expect ( "failed to read /dev/urandom" ) ;
108126 }
109127}
110128
0 commit comments