33extern crate std;
44
55use 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 ;
1610
1711/// For all platforms, we use `/dev/urandom` rather than `/dev/random`.
1812/// For more information see the linked man pages in lib.rs.
@@ -26,78 +20,41 @@ const FILE_PATH: &str = "/dev/urandom";
2620// `#[cold]` because it is hot when it is actually used.
2721#[ cfg_attr( any( target_os = "android" , target_os = "linux" ) , inline( never) ) ]
2822pub 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- //
4923 // The opening of the file, by libc/libstd/etc. may write some unknown
5024 // state into in-process memory. (Such state may include some sanitizer
5125 // bookkeeping, or we might be operating in a unikernal-like environment
5226 // 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+ return 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+ }
9150
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 ( ) ?;
9456
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)
10158}
10259
10360// 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> {
13087// libsodium uses `libc::poll` similarly to this.
13188#[ cfg( any( target_os = "android" , target_os = "linux" ) ) ]
13289fn 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) ?;
13691 let mut pfd = libc:: pollfd {
13792 fd : file. as_raw_fd ( ) ,
13893 events : libc:: POLLIN ,
@@ -171,29 +126,3 @@ fn map_io_error(err: io::Error) -> Error {
171126 }
172127 } )
173128}
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