11//! Implementation for Linux / Android with `/dev/urandom` fallback
22use super :: { sanitizer, use_file} ;
3- use crate :: Error ;
3+ use crate :: { Error , lazy , util_libc } ;
44use core:: {
55 ffi:: c_void,
66 mem:: { MaybeUninit , transmute} ,
7- ptr:: NonNull ,
8- sync:: atomic:: { AtomicPtr , Ordering } ,
7+ ptr,
98} ;
10- use use_file:: util_libc;
119
1210pub use crate :: util:: { inner_u32, inner_u64} ;
1311
1412type GetRandomFn = unsafe extern "C" fn ( * mut c_void , libc:: size_t , libc:: c_uint ) -> libc:: ssize_t ;
1513
1614/// Sentinel value which indicates that `libc::getrandom` either not available,
1715/// or not supported by kernel.
18- const NOT_AVAILABLE : NonNull < c_void > = unsafe { NonNull :: new_unchecked ( usize:: MAX as * mut c_void ) } ;
19-
20- static GETRANDOM_FN : AtomicPtr < c_void > = AtomicPtr :: new ( core:: ptr:: null_mut ( ) ) ;
16+ const NOT_AVAILABLE : usize = usize:: MAX ;
2117
2218#[ cold]
2319#[ inline( never) ]
24- fn init ( ) -> NonNull < c_void > {
20+ fn init ( ) -> usize {
2521 // Use static linking to `libc::getrandom` on MUSL targets and `dlsym` everywhere else
2622 #[ cfg( not( target_env = "musl" ) ) ]
27- let raw_ptr = {
28- static NAME : & [ u8 ] = b"getrandom\0 " ;
29- let name_ptr = NAME . as_ptr ( ) . cast :: < libc:: c_char > ( ) ;
30- unsafe { libc:: dlsym ( libc:: RTLD_DEFAULT , name_ptr) }
31- } ;
23+ let fptr = unsafe { libc:: dlsym ( libc:: RTLD_DEFAULT , c"getrandom" . as_ptr ( ) ) } as usize ;
3224 #[ cfg( target_env = "musl" ) ]
33- let raw_ptr = {
25+ let fptr = {
3426 let fptr: GetRandomFn = libc:: getrandom;
35- unsafe { transmute :: < GetRandomFn , * mut c_void > ( fptr) }
27+ unsafe { transmute :: < GetRandomFn , usize > ( fptr) }
3628 } ;
3729
38- let res_ptr = match NonNull :: new ( raw_ptr) {
39- Some ( fptr) => {
40- let getrandom_fn = unsafe { transmute :: < NonNull < c_void > , GetRandomFn > ( fptr) } ;
41- let dangling_ptr = NonNull :: dangling ( ) . as_ptr ( ) ;
42- // Check that `getrandom` syscall is supported by kernel
43- let res = unsafe { getrandom_fn ( dangling_ptr, 0 , 0 ) } ;
44- if cfg ! ( getrandom_test_linux_fallback) {
45- NOT_AVAILABLE
46- } else if res. is_negative ( ) {
47- match util_libc:: last_os_error ( ) . raw_os_error ( ) {
48- Some ( libc:: ENOSYS ) => NOT_AVAILABLE , // No kernel support
49- // The fallback on EPERM is intentionally not done on Android since this workaround
50- // seems to be needed only for specific Linux-based products that aren't based
51- // on Android. See https://github.com/rust-random/getrandom/issues/229.
52- #[ cfg( target_os = "linux" ) ]
53- Some ( libc:: EPERM ) => NOT_AVAILABLE , // Blocked by seccomp
54- _ => fptr,
55- }
56- } else {
57- fptr
30+ let res_ptr = if fptr != 0 {
31+ let getrandom_fn = unsafe { transmute :: < usize , GetRandomFn > ( fptr) } ;
32+ // Check that `getrandom` syscall is supported by kernel
33+ let res = unsafe { getrandom_fn ( ptr:: dangling_mut ( ) , 0 , 0 ) } ;
34+ if cfg ! ( getrandom_test_linux_fallback) {
35+ NOT_AVAILABLE
36+ } else if res. is_negative ( ) {
37+ match util_libc:: last_os_error ( ) . raw_os_error ( ) {
38+ Some ( libc:: ENOSYS ) => NOT_AVAILABLE , // No kernel support
39+ // The fallback on EPERM is intentionally not done on Android since this workaround
40+ // seems to be needed only for specific Linux-based products that aren't based
41+ // on Android. See https://github.com/rust-random/getrandom/issues/229.
42+ #[ cfg( target_os = "linux" ) ]
43+ Some ( libc:: EPERM ) => NOT_AVAILABLE , // Blocked by seccomp
44+ _ => fptr,
5845 }
46+ } else {
47+ fptr
5948 }
60- None => NOT_AVAILABLE ,
49+ } else {
50+ NOT_AVAILABLE
6151 } ;
6252
6353 #[ cfg( getrandom_test_linux_without_fallback) ]
6454 if res_ptr == NOT_AVAILABLE {
6555 panic ! ( "Fallback is triggered with enabled `getrandom_test_linux_without_fallback`" )
6656 }
6757
68- GETRANDOM_FN . store ( res_ptr. as_ptr ( ) , Ordering :: Release ) ;
6958 res_ptr
7059}
7160
@@ -77,23 +66,14 @@ fn use_file_fallback(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
7766
7867#[ inline]
7968pub fn fill_inner ( dest : & mut [ MaybeUninit < u8 > ] ) -> Result < ( ) , Error > {
80- // Despite being only a single atomic variable, we still cannot always use
81- // Ordering::Relaxed, as we need to make sure a successful call to `init`
82- // is "ordered before" any data read through the returned pointer (which
83- // occurs when the function is called). Our implementation mirrors that of
84- // the one in libstd, meaning that the use of non-Relaxed operations is
85- // probably unnecessary.
86- let raw_ptr = GETRANDOM_FN . load ( Ordering :: Acquire ) ;
87- let fptr = match NonNull :: new ( raw_ptr) {
88- Some ( p) => p,
89- None => init ( ) ,
90- } ;
69+ static GETRANDOM_FN : lazy:: LazyUsize = lazy:: LazyUsize :: new ( ) ;
70+ let fptr = GETRANDOM_FN . unsync_init ( init) ;
9171
9272 if fptr == NOT_AVAILABLE {
9373 use_file_fallback ( dest)
9474 } else {
9575 // note: `transmute` is currently the only way to convert a pointer into a function reference
96- let getrandom_fn = unsafe { transmute :: < NonNull < c_void > , GetRandomFn > ( fptr) } ;
76+ let getrandom_fn = unsafe { transmute :: < usize , GetRandomFn > ( fptr) } ;
9777 util_libc:: sys_fill_exact ( dest, |buf| unsafe {
9878 let ret = getrandom_fn ( buf. as_mut_ptr ( ) . cast ( ) , buf. len ( ) , 0 ) ;
9979 sanitizer:: unpoison_linux_getrandom_result ( buf, ret) ;
0 commit comments