@@ -12,141 +12,55 @@ use core::{
1212
1313/// For all platforms, we use `/dev/urandom` rather than `/dev/random`.
1414/// For more information see the linked man pages in lib.rs.
15- /// - On Linux, "/dev/urandom is preferred and sufficient in all use cases".
1615/// - On Redox, only /dev/urandom is provided.
1716/// - On AIX, /dev/urandom will "provide cryptographically secure output".
1817/// - On Haiku and QNX Neutrino they are identical.
1918const FILE_PATH : & [ u8 ] = b"/dev/urandom\0 " ;
2019
21- // Do not inline this when it is the fallback implementation, but don't mark it
22- // `#[cold]` because it is hot when it is actually used.
23- #[ cfg_attr( any( target_os = "android" , target_os = "linux" ) , inline( never) ) ]
20+ // std::os::fd::{BorrowedFd, OwnedFd} guarantee that -1 is not a valid file descriptor.
21+ const FD_UNINIT : libc:: c_int = -1 ;
22+
23+ // In theory `libc::c_int` could be something other than `i32`, but for the
24+ // targets we currently support that use `use_file`, it is always `i32`.
25+ // If/when we add support for a target where that isn't the case, we may
26+ // need to use a different atomic type or make other accomodations. The
27+ // compiler will let us know if/when that is the case, because the
28+ // `FD.store(fd)` would fail to compile.
29+ //
30+ // The opening of the file, by libc/libstd/etc. may write some unknown
31+ // state into in-process memory. (Such state may include some sanitizer
32+ // bookkeeping, or we might be operating in a unikernal-like environment
33+ // where all the "kernel" file descriptor bookkeeping is done in our
34+ // process.) `get_fd_locked` stores into FD using `Ordering::Release` to
35+ // ensure any such state is synchronized. `get_fd` loads from `FD` with
36+ // `Ordering::Acquire` to synchronize with it.
37+ static FD : AtomicI32 = AtomicI32 :: new ( FD_UNINIT ) ;
38+
39+ static FD_MUTEX : Mutex = Mutex :: new ( ) ;
40+
2441pub fn getrandom_inner ( dest : & mut [ MaybeUninit < u8 > ] ) -> Result < ( ) , Error > {
25- let fd = get_rng_fd ( ) ?;
42+ let mut fd = FD . load ( Ordering :: Acquire ) ;
43+ if fd == FD_UNINIT {
44+ fd = open_or_wait ( ) ?;
45+ }
2646 sys_fill_exact ( dest, |buf| unsafe {
2747 libc:: read ( fd, buf. as_mut_ptr ( ) . cast :: < c_void > ( ) , buf. len ( ) )
2848 } )
2949}
3050
31- // Returns the file descriptor for the device file used to retrieve random
32- // bytes. The file will be opened exactly once. All subsequent calls will
33- // return the same file descriptor. This file descriptor is never closed.
34- fn get_rng_fd ( ) -> Result < libc:: c_int , Error > {
35- // std::os::fd::{BorrowedFd, OwnedFd} guarantee that -1 is not a valid file descriptor.
36- const FD_UNINIT : libc:: c_int = -1 ;
37-
38- // In theory `libc::c_int` could be something other than `i32`, but for the
39- // targets we currently support that use `use_file`, it is always `i32`.
40- // If/when we add support for a target where that isn't the case, we may
41- // need to use a different atomic type or make other accomodations. The
42- // compiler will let us know if/when that is the case, because the
43- // `FD.store(fd)` would fail to compile.
44- //
45- // The opening of the file, by libc/libstd/etc. may write some unknown
46- // state into in-process memory. (Such state may include some sanitizer
47- // bookkeeping, or we might be operating in a unikernal-like environment
48- // where all the "kernel" file descriptor bookkeeping is done in our
49- // process.) `get_fd_locked` stores into FD using `Ordering::Release` to
50- // ensure any such state is synchronized. `get_fd` loads from `FD` with
51- // `Ordering::Acquire` to synchronize with it.
52- static FD : AtomicI32 = AtomicI32 :: new ( FD_UNINIT ) ;
53-
54- fn get_fd ( ) -> Option < libc:: c_int > {
55- match FD . load ( Ordering :: Acquire ) {
56- FD_UNINIT => None ,
57- val => Some ( val) ,
51+ #[ cold]
52+ fn open_or_wait ( ) -> Result < libc:: c_int , Error > {
53+ let _guard = FD_MUTEX . lock ( ) ;
54+ let fd = match FD . load ( Ordering :: Acquire ) {
55+ FD_UNINIT => {
56+ let fd = open_readonly ( FILE_PATH ) ?;
57+ FD . store ( fd, Ordering :: Release ) ;
58+ fd
5859 }
59- }
60-
61- #[ cold]
62- fn get_fd_locked ( ) -> Result < libc:: c_int , Error > {
63- // This mutex is used to prevent multiple threads from opening file
64- // descriptors concurrently, which could run into the limit on the
65- // number of open file descriptors. Our goal is to have no more than one
66- // file descriptor open, ever.
67- //
68- // SAFETY: We use the mutex only in this method, and we always unlock it
69- // before returning, making sure we don't violate the pthread_mutex_t API.
70- static MUTEX : Mutex = Mutex :: new ( ) ;
71- unsafe { MUTEX . lock ( ) } ;
72- let _guard = DropGuard ( || unsafe { MUTEX . unlock ( ) } ) ;
73-
74- if let Some ( fd) = get_fd ( ) {
75- return Ok ( fd) ;
76- }
77-
78- // On Linux, /dev/urandom might return insecure values.
79- #[ cfg( any( target_os = "android" , target_os = "linux" ) ) ]
80- wait_until_rng_ready ( ) ?;
81-
82- let fd = open_readonly ( FILE_PATH ) ?;
83- debug_assert ! ( fd != FD_UNINIT ) ;
84- FD . store ( fd, Ordering :: Release ) ;
85-
86- Ok ( fd)
87- }
88-
89- // Use double-checked locking to avoid acquiring the lock if possible.
90- if let Some ( fd) = get_fd ( ) {
91- Ok ( fd)
92- } else {
93- get_fd_locked ( )
94- }
95- }
96-
97- // Polls /dev/random to make sure it is ok to read from /dev/urandom.
98- //
99- // Polling avoids draining the estimated entropy from /dev/random;
100- // short-lived processes reading even a single byte from /dev/random could
101- // be problematic if they are being executed faster than entropy is being
102- // collected.
103- //
104- // OTOH, reading a byte instead of polling is more compatible with
105- // sandboxes that disallow `poll()` but which allow reading /dev/random,
106- // e.g. sandboxes that assume that `poll()` is for network I/O. This way,
107- // fewer applications will have to insert pre-sandbox-initialization logic.
108- // Often (blocking) file I/O is not allowed in such early phases of an
109- // application for performance and/or security reasons.
110- //
111- // It is hard to write a sandbox policy to support `libc::poll()` because
112- // it may invoke the `poll`, `ppoll`, `ppoll_time64` (since Linux 5.1, with
113- // newer versions of glibc), and/or (rarely, and probably only on ancient
114- // systems) `select`. depending on the libc implementation (e.g. glibc vs
115- // musl), libc version, potentially the kernel version at runtime, and/or
116- // the target architecture.
117- //
118- // BoringSSL and libstd don't try to protect against insecure output from
119- // `/dev/urandom'; they don't open `/dev/random` at all.
120- //
121- // OpenSSL uses `libc::select()` unless the `dev/random` file descriptor
122- // is too large; if it is too large then it does what we do here.
123- //
124- // libsodium uses `libc::poll` similarly to this.
125- #[ cfg( any( target_os = "android" , target_os = "linux" ) ) ]
126- fn wait_until_rng_ready ( ) -> Result < ( ) , Error > {
127- let fd = open_readonly ( b"/dev/random\0 " ) ?;
128- let mut pfd = libc:: pollfd {
129- fd,
130- events : libc:: POLLIN ,
131- revents : 0 ,
60+ fd => fd,
13261 } ;
133- let _guard = DropGuard ( || unsafe {
134- libc:: close ( fd) ;
135- } ) ;
136-
137- loop {
138- // A negative timeout means an infinite timeout.
139- let res = unsafe { libc:: poll ( & mut pfd, 1 , -1 ) } ;
140- if res >= 0 {
141- debug_assert_eq ! ( res, 1 ) ; // We only used one fd, and cannot timeout.
142- return Ok ( ( ) ) ;
143- }
144- let err = crate :: util_libc:: last_os_error ( ) ;
145- match err. raw_os_error ( ) {
146- Some ( libc:: EINTR ) | Some ( libc:: EAGAIN ) => continue ,
147- _ => return Err ( err) ,
148- }
149- }
62+ debug_assert ! ( fd >= 0 ) ;
63+ Ok ( fd)
15064}
15165
15266struct Mutex ( UnsafeCell < libc:: pthread_mutex_t > ) ;
@@ -155,22 +69,21 @@ impl Mutex {
15569 const fn new ( ) -> Self {
15670 Self ( UnsafeCell :: new ( libc:: PTHREAD_MUTEX_INITIALIZER ) )
15771 }
158- unsafe fn lock ( & self ) {
159- let r = libc:: pthread_mutex_lock ( self . 0 . get ( ) ) ;
160- debug_assert_eq ! ( r, 0 ) ;
161- }
162- unsafe fn unlock ( & self ) {
163- let r = libc:: pthread_mutex_unlock ( self . 0 . get ( ) ) ;
72+
73+ fn lock ( & self ) -> MutexGuard < ' _ > {
74+ let r = unsafe { libc:: pthread_mutex_lock ( self . 0 . get ( ) ) } ;
16475 debug_assert_eq ! ( r, 0 ) ;
76+ MutexGuard ( self )
16577 }
16678}
16779
16880unsafe impl Sync for Mutex { }
16981
170- struct DropGuard < F : FnMut ( ) > ( F ) ;
82+ struct MutexGuard < ' a > ( & ' a Mutex ) ;
17183
172- impl < F : FnMut ( ) > Drop for DropGuard < F > {
84+ impl < ' a > Drop for MutexGuard < ' a > {
17385 fn drop ( & mut self ) {
174- self . 0 ( )
86+ let r = unsafe { libc:: pthread_mutex_unlock ( self . 0 . 0 . get ( ) ) } ;
87+ debug_assert_eq ! ( r, 0 ) ;
17588 }
17689}
0 commit comments