11//! Implementation for Linux / Android with `/dev/urandom` fallback
22use super :: use_file;
33use crate :: Error ;
4- use core:: {
5- ffi:: c_void,
6- mem:: { self , MaybeUninit } ,
7- ptr:: { self , NonNull } ,
8- sync:: atomic:: { AtomicPtr , Ordering } ,
9- } ;
4+ use core:: { ffi:: c_void, mem:: MaybeUninit , ptr} ;
105use use_file:: util_libc;
116
7+ #[ path = "../lazy.rs" ]
8+ mod lazy;
9+
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
16- /// Sentinel value which indicates that `libc::getrandom` either not available,
17- /// or not supported by kernel.
18- const NOT_AVAILABLE : NonNull < c_void > = unsafe { NonNull :: new_unchecked ( usize:: MAX as * mut c_void ) } ;
14+ #[ cfg( target_os = "android" ) ]
15+ #[ cold]
16+ #[ inline( never) ]
17+ fn get_getrandom_fn ( ) -> Option < GetRandomFn > {
18+ static GETRANDOM_FN : lazy:: LazyUsize = lazy:: LazyUsize :: new ( ) ;
1919
20- static GETRANDOM_FN : AtomicPtr < c_void > = AtomicPtr :: new ( ptr:: null_mut ( ) ) ;
20+ let raw_ptr = GETRANDOM_FN . unsync_init ( || {
21+ static NAME : & [ u8 ] = b"getrandom\0 " ;
22+ let name_ptr = NAME . as_ptr ( ) . cast :: < libc:: c_char > ( ) ;
23+ unsafe { libc:: dlsym ( libc:: RTLD_DEFAULT , name_ptr) as usize }
24+ } ) as * mut c_void ;
25+
26+ ( !raw_ptr. is_null ( ) ) . then ( || {
27+ // note: `transmute` is currently the only way to convert a pointer into a function reference
28+ unsafe { core:: mem:: transmute :: < * mut c_void , GetRandomFn > ( raw_ptr) }
29+ } )
30+ }
2131
2232#[ cold]
2333#[ inline( never) ]
24- fn init ( ) -> NonNull < c_void > {
25- static NAME : & [ u8 ] = b"getrandom\0 " ;
26- let name_ptr = NAME . as_ptr ( ) . cast :: < libc:: c_char > ( ) ;
27- let raw_ptr = unsafe { libc:: dlsym ( libc:: RTLD_DEFAULT , name_ptr) } ;
28- let res_ptr = match NonNull :: new ( raw_ptr) {
29- Some ( fptr) => {
30- let getrandom_fn = unsafe { mem:: transmute :: < NonNull < c_void > , GetRandomFn > ( fptr) } ;
31- let dangling_ptr = ptr:: NonNull :: dangling ( ) . as_ptr ( ) ;
32- // Check that `getrandom` syscall is supported by kernel
33- let res = unsafe { getrandom_fn ( dangling_ptr, 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,
45- }
46- } else {
47- fptr
48- }
34+ fn check_getrandom ( getrandom_fn : GetRandomFn ) -> bool {
35+ let dangling_ptr = ptr:: NonNull :: dangling ( ) . as_ptr ( ) ;
36+ // Check that `getrandom` syscall is supported by kernel
37+ let res = unsafe { getrandom_fn ( dangling_ptr, 0 , 0 ) } ;
38+ if cfg ! ( getrandom_test_linux_fallback) {
39+ false
40+ } else if res. is_negative ( ) {
41+ match util_libc:: last_os_error ( ) . raw_os_error ( ) {
42+ Some ( libc:: ENOSYS ) => false , // No kernel support
43+ // The fallback on EPERM is intentionally not done on Android since this workaround
44+ // seems to be needed only for specific Linux-based products that aren't based
45+ // on Android. See https://github.com/rust-random/getrandom/issues/229.
46+ #[ cfg( target_os = "linux" ) ]
47+ Some ( libc:: EPERM ) => false , // Blocked by seccomp
48+ _ => true ,
4949 }
50- None => NOT_AVAILABLE ,
51- } ;
52-
53- GETRANDOM_FN . store ( res_ptr. as_ptr ( ) , Ordering :: Release ) ;
54- res_ptr
50+ } else {
51+ true
52+ }
5553}
5654
5755// prevent inlining of the fallback implementation
@@ -62,23 +60,20 @@ fn use_file_fallback(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
6260
6361#[ inline]
6462pub fn fill_inner ( dest : & mut [ MaybeUninit < u8 > ] ) -> Result < ( ) , Error > {
65- // Despite being only a single atomic variable, we still cannot always use
66- // Ordering::Relaxed, as we need to make sure a successful call to `init`
67- // is "ordered before" any data read through the returned pointer (which
68- // occurs when the function is called). Our implementation mirrors that of
69- // the one in libstd, meaning that the use of non-Relaxed operations is
70- // probably unnecessary.
71- let raw_ptr = GETRANDOM_FN . load ( Ordering :: Acquire ) ;
72- let fptr = match NonNull :: new ( raw_ptr) {
73- Some ( p) => p,
74- None => init ( ) ,
63+ static GETRANDOM_GOOD : lazy:: LazyBool = lazy:: LazyBool :: new ( ) ;
64+
65+ #[ cfg( not( target_os = "android" ) ) ]
66+ let getrandom_fn = libc:: getrandom;
67+
68+ #[ cfg( target_os = "android" ) ]
69+ let getrandom_fn = match get_getrandom_fn ( ) {
70+ Some ( f) => f,
71+ None => return use_file_fallback ( dest) ,
7572 } ;
7673
77- if fptr == NOT_AVAILABLE {
74+ if ! GETRANDOM_GOOD . unsync_init ( || check_getrandom ( getrandom_fn ) ) {
7875 use_file_fallback ( dest)
7976 } else {
80- // note: `transmute` is currently the only way to convert a pointer into a function reference
81- let getrandom_fn = unsafe { mem:: transmute :: < NonNull < c_void > , GetRandomFn > ( fptr) } ;
8277 util_libc:: sys_fill_exact ( dest, |buf| unsafe {
8378 getrandom_fn ( buf. as_mut_ptr ( ) . cast ( ) , buf. len ( ) , 0 )
8479 } )
0 commit comments