1
1
//! Implementation for Linux / Android with `/dev/urandom` fallback
2
2
use super :: use_file;
3
3
use 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} ;
10
5
use use_file:: util_libc;
11
6
7
+ #[ path = "../lazy.rs" ]
8
+ mod lazy;
9
+
12
10
pub use crate :: util:: { inner_u32, inner_u64} ;
13
11
14
12
type GetRandomFn = unsafe extern "C" fn ( * mut c_void , libc:: size_t , libc:: c_uint ) -> libc:: ssize_t ;
15
13
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( not( target_env = "musl" ) ) ]
15
+ #[ cold]
16
+ #[ inline( never) ]
17
+ fn get_getrandom_fn ( ) -> Option < GetRandomFn > {
18
+ static GETRANDOM_FN : lazy:: LazyUsize = lazy:: LazyUsize :: new ( ) ;
19
19
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
+ }
21
31
22
32
#[ cold]
23
33
#[ 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 ,
49
49
}
50
- None => NOT_AVAILABLE ,
51
- } ;
52
-
53
- GETRANDOM_FN . store ( res_ptr. as_ptr ( ) , Ordering :: Release ) ;
54
- res_ptr
50
+ } else {
51
+ true
52
+ }
55
53
}
56
54
57
55
// prevent inlining of the fallback implementation
@@ -62,23 +60,20 @@ fn use_file_fallback(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
62
60
63
61
#[ inline]
64
62
pub 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( target_env = "musl" ) ]
66
+ let getrandom_fn = libc:: getrandom;
67
+
68
+ #[ cfg( not( target_env = "musl" ) ) ]
69
+ let getrandom_fn = match get_getrandom_fn ( ) {
70
+ Some ( f) => f,
71
+ None => return use_file_fallback ( dest) ,
75
72
} ;
76
73
77
- if fptr == NOT_AVAILABLE {
74
+ if ! GETRANDOM_GOOD . unsync_init ( || check_getrandom ( getrandom_fn ) ) {
78
75
use_file_fallback ( dest)
79
76
} 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) } ;
82
77
util_libc:: sys_fill_exact ( dest, |buf| unsafe {
83
78
getrandom_fn ( buf. as_mut_ptr ( ) . cast ( ) , buf. len ( ) , 0 )
84
79
} )
0 commit comments