3
3
extern crate std;
4
4
5
5
use crate :: { util_libc:: sys_fill_exact, Error } ;
6
- use core:: {
7
- cell:: UnsafeCell ,
8
- ffi:: c_void,
9
- mem:: MaybeUninit ,
10
- sync:: atomic:: { AtomicI32 , Ordering } ,
11
- } ;
12
- use std:: {
13
- fs, io,
14
- os:: fd:: { IntoRawFd as _, RawFd } ,
15
- } ;
6
+ use core:: { ffi:: c_void, mem:: MaybeUninit } ;
7
+ use std:: { fs:: File , io, os:: unix:: io:: AsRawFd as _} ;
8
+ // TODO(MSRV feature(once_cell_try)): Use std::sync::OnceLock instead.
9
+ use once_cell:: sync:: OnceCell as OnceLock ;
16
10
17
11
/// For all platforms, we use `/dev/urandom` rather than `/dev/random`.
18
12
/// For more information see the linked man pages in lib.rs.
@@ -26,78 +20,41 @@ const FILE_PATH: &str = "/dev/urandom";
26
20
// `#[cold]` because it is hot when it is actually used.
27
21
#[ cfg_attr( any( target_os = "android" , target_os = "linux" ) , inline( never) ) ]
28
22
pub fn getrandom_inner ( dest : & mut [ MaybeUninit < u8 > ] ) -> Result < ( ) , Error > {
29
- let fd = get_rng_fd ( ) ?;
30
- sys_fill_exact ( dest, |buf| unsafe {
31
- libc:: read ( fd, buf. as_mut_ptr ( ) . cast :: < c_void > ( ) , buf. len ( ) )
32
- } )
33
- }
34
-
35
- // Returns the file descriptor for the device file used to retrieve random
36
- // bytes. The file will be opened exactly once. All subsequent calls will
37
- // return the same file descriptor. This file descriptor is never closed.
38
- fn get_rng_fd ( ) -> Result < RawFd , Error > {
39
- // std::os::fd::{BorrowedFd, OwnedFd} guarantee that -1 is not a valid file descriptor.
40
- const FD_UNINIT : RawFd = -1 ;
41
-
42
- // In theory `RawFd` could be something other than `i32`, but for the
43
- // targets we currently support that use `use_file`, it is always `i32`.
44
- // If/when we add support for a target where that isn't the case, we may
45
- // need to use a different atomic type or make other accomodations. The
46
- // compiler will let us know if/when that is the case, because the
47
- // `FD.store(fd)` would fail to compile.
48
- //
49
23
// The opening of the file, by libc/libstd/etc. may write some unknown
50
24
// state into in-process memory. (Such state may include some sanitizer
51
25
// bookkeeping, or we might be operating in a unikernal-like environment
52
26
// where all the "kernel" file descriptor bookkeeping is done in our
53
- // process.) `get_fd_locked` stores into FD using `Ordering::Release` to
54
- // ensure any such state is synchronized. `get_fd` loads from `FD` with
55
- // `Ordering::Acquire` to synchronize with it.
56
- static FD : AtomicI32 = AtomicI32 :: new ( FD_UNINIT ) ;
57
-
58
- fn get_fd ( ) -> Option < RawFd > {
59
- match FD . load ( Ordering :: Acquire ) {
60
- FD_UNINIT => None ,
61
- val => Some ( val) ,
62
- }
63
- }
64
-
65
- #[ cold]
66
- fn get_fd_locked ( ) -> Result < RawFd , Error > {
67
- // This mutex is used to prevent multiple threads from opening file
68
- // descriptors concurrently, which could run into the limit on the
69
- // number of open file descriptors. Our goal is to have no more than one
70
- // file descriptor open, ever.
71
- //
72
- // SAFETY: We use the mutex only in this method, and we always unlock it
73
- // before returning, making sure we don't violate the pthread_mutex_t API.
74
- static MUTEX : Mutex = Mutex :: new ( ) ;
75
- unsafe { MUTEX . lock ( ) } ;
76
- let _guard = DropGuard ( || unsafe { MUTEX . unlock ( ) } ) ;
77
-
78
- if let Some ( fd) = get_fd ( ) {
79
- return Ok ( fd) ;
80
- }
81
-
82
- // On Linux, /dev/urandom might return insecure values.
83
- #[ cfg( any( target_os = "android" , target_os = "linux" ) ) ]
84
- wait_until_rng_ready ( ) ?;
85
-
86
- let file = fs:: File :: open ( FILE_PATH ) . map_err ( map_io_error) ?;
87
-
88
- let fd = file. into_raw_fd ( ) ;
89
- debug_assert ! ( fd != FD_UNINIT ) ;
90
- FD . store ( fd, Ordering :: Release ) ;
27
+ // process.) Thus we avoid using (relaxed) atomics like we use in other
28
+ // parts of the library.
29
+ //
30
+ // We prevent multiple threads from opening file descriptors concurrently,
31
+ // which could run into the limit on the number of open file descriptors.
32
+ // Our goal is to have no more than one file descriptor open, ever.
33
+ //
34
+ // We assume any call to `OnceLock::get_or_try_init` synchronizes-with
35
+ // (Ordering::Acquire) the preceding call to `OnceLock::get_or_try_init`
36
+ // after `init()` returns an `Ok` result (Ordering::Release). See
37
+ // https://github.com/rust-lang/rust/issues/126239.
38
+ static FILE : OnceLock < File > = OnceLock :: new ( ) ;
39
+ let file = FILE . get_or_try_init ( init) ?;
40
+
41
+ // TODO(MSRV feature(read_buf)): Use `std::io::Read::read_buf`
42
+ sys_fill_exact ( dest, |buf| unsafe {
43
+ libc:: read (
44
+ file. as_raw_fd ( ) ,
45
+ buf. as_mut_ptr ( ) . cast :: < c_void > ( ) ,
46
+ buf. len ( ) ,
47
+ )
48
+ } )
49
+ }
91
50
92
- Ok ( fd)
93
- }
51
+ #[ cold]
52
+ fn init ( ) -> Result < File , Error > {
53
+ // On Linux, /dev/urandom might return insecure values.
54
+ #[ cfg( any( target_os = "android" , target_os = "linux" ) ) ]
55
+ wait_until_rng_ready ( ) ?;
94
56
95
- // Use double-checked locking to avoid acquiring the lock if possible.
96
- if let Some ( fd) = get_fd ( ) {
97
- Ok ( fd)
98
- } else {
99
- get_fd_locked ( )
100
- }
57
+ File :: open ( FILE_PATH ) . map_err ( map_io_error)
101
58
}
102
59
103
60
// Polls /dev/random to make sure it is ok to read from /dev/urandom.
@@ -130,9 +87,7 @@ fn get_rng_fd() -> Result<RawFd, Error> {
130
87
// libsodium uses `libc::poll` similarly to this.
131
88
#[ cfg( any( target_os = "android" , target_os = "linux" ) ) ]
132
89
fn wait_until_rng_ready ( ) -> Result < ( ) , Error > {
133
- use std:: os:: unix:: io:: AsRawFd as _;
134
-
135
- let file = fs:: File :: open ( "/dev/random" ) . map_err ( map_io_error) ?;
90
+ let file = File :: open ( "/dev/random" ) . map_err ( map_io_error) ?;
136
91
let mut pfd = libc:: pollfd {
137
92
fd : file. as_raw_fd ( ) ,
138
93
events : libc:: POLLIN ,
@@ -171,29 +126,3 @@ fn map_io_error(err: io::Error) -> Error {
171
126
}
172
127
} )
173
128
}
174
-
175
- struct Mutex ( UnsafeCell < libc:: pthread_mutex_t > ) ;
176
-
177
- impl Mutex {
178
- const fn new ( ) -> Self {
179
- Self ( UnsafeCell :: new ( libc:: PTHREAD_MUTEX_INITIALIZER ) )
180
- }
181
- unsafe fn lock ( & self ) {
182
- let r = libc:: pthread_mutex_lock ( self . 0 . get ( ) ) ;
183
- debug_assert_eq ! ( r, 0 ) ;
184
- }
185
- unsafe fn unlock ( & self ) {
186
- let r = libc:: pthread_mutex_unlock ( self . 0 . get ( ) ) ;
187
- debug_assert_eq ! ( r, 0 ) ;
188
- }
189
- }
190
-
191
- unsafe impl Sync for Mutex { }
192
-
193
- struct DropGuard < F : FnMut ( ) > ( F ) ;
194
-
195
- impl < F : FnMut ( ) > Drop for DropGuard < F > {
196
- fn drop ( & mut self ) {
197
- self . 0 ( )
198
- }
199
- }
0 commit comments