1
1
//! Implementation for NetBSD
2
- use crate :: { lazy:: LazyPtr , util_libc:: sys_fill_exact, Error } ;
3
- use core:: { ffi:: c_void, mem:: MaybeUninit , ptr} ;
2
+ //!
3
+ //! `getrandom(2)` was introduced in NetBSD 10. To support older versions we
4
+ //! implement our own weak linkage to it, and provide a fallback based on the
5
+ //! KERN_ARND sysctl.
6
+ use crate :: { util_libc:: sys_fill_exact, Error } ;
7
+ use core:: {
8
+ cmp,
9
+ ffi:: c_void,
10
+ mem:: { self , MaybeUninit } ,
11
+ ptr,
12
+ sync:: atomic:: { AtomicPtr , Ordering } ,
13
+ } ;
14
+
15
+ unsafe extern "C" fn polyfill_using_kern_arand (
16
+ buf : * mut c_void ,
17
+ buflen : libc:: size_t ,
18
+ flags : libc:: c_uint ,
19
+ ) -> libc:: ssize_t {
20
+ debug_assert_eq ! ( flags, 0 ) ;
4
21
5
- fn kern_arnd ( buf : & mut [ MaybeUninit < u8 > ] ) -> libc:: ssize_t {
6
22
static MIB : [ libc:: c_int ; 2 ] = [ libc:: CTL_KERN , libc:: KERN_ARND ] ;
7
- let mut len = buf. len ( ) ;
23
+
24
+ // NetBSD will only return up to 256 bytes at a time, and
25
+ // older NetBSD kernels will fail on longer buffers.
26
+ let mut len = cmp:: min ( buflen, 256 ) ;
27
+
8
28
let ret = unsafe {
9
29
libc:: sysctl (
10
30
MIB . as_ptr ( ) ,
11
31
MIB . len ( ) as libc:: c_uint ,
12
- buf. as_mut_ptr ( ) . cast :: < c_void > ( ) ,
32
+ buf,
13
33
& mut len,
14
34
ptr:: null ( ) ,
15
35
0 ,
@@ -22,30 +42,37 @@ fn kern_arnd(buf: &mut [MaybeUninit<u8>]) -> libc::ssize_t {
22
42
}
23
43
}
24
44
25
- type GetRandomFn = unsafe extern "C" fn ( * mut u8 , libc:: size_t , libc:: c_uint ) -> libc:: ssize_t ;
45
+ type GetRandomFn = unsafe extern "C" fn ( * mut c_void , libc:: size_t , libc:: c_uint ) -> libc:: ssize_t ;
26
46
27
- // getrandom(2) was introduced in NetBSD 10.0
28
- static GETRANDOM : LazyPtr = LazyPtr :: new ( ) ;
47
+ static GETRANDOM : AtomicPtr < c_void > = AtomicPtr :: new ( ptr:: null_mut ( ) ) ;
29
48
30
- fn dlsym_getrandom ( ) -> * mut c_void {
49
+ #[ cold]
50
+ fn init ( ) -> * mut c_void {
31
51
static NAME : & [ u8 ] = b"getrandom\0 " ;
32
52
let name_ptr = NAME . as_ptr ( ) . cast :: < libc:: c_char > ( ) ;
33
- unsafe { libc:: dlsym ( libc:: RTLD_DEFAULT , name_ptr) }
53
+ let mut ptr = unsafe { libc:: dlsym ( libc:: RTLD_DEFAULT , name_ptr) } ;
54
+ if ptr. is_null ( ) {
55
+ // Verify `polyfill_using_kern_arand` has the right signature.
56
+ const POLYFILL : GetRandomFn = polyfill_using_kern_arand;
57
+ ptr = POLYFILL as * mut c_void ;
58
+ }
59
+ GETRANDOM . store ( ptr, Ordering :: Release ) ;
60
+ ptr
34
61
}
35
62
36
63
pub fn getrandom_inner ( dest : & mut [ MaybeUninit < u8 > ] ) -> Result < ( ) , Error > {
37
- let fptr = GETRANDOM . unsync_init ( dlsym_getrandom) ;
38
- if !fptr. is_null ( ) {
39
- let func: GetRandomFn = unsafe { core:: mem:: transmute ( fptr) } ;
40
- return sys_fill_exact ( dest, |buf| unsafe {
41
- func ( buf. as_mut_ptr ( ) . cast :: < u8 > ( ) , buf. len ( ) , 0 )
42
- } ) ;
43
- }
44
-
45
- // NetBSD will only return up to 256 bytes at a time, and
46
- // older NetBSD kernels will fail on longer buffers.
47
- for chunk in dest. chunks_mut ( 256 ) {
48
- sys_fill_exact ( chunk, kern_arnd) ?
64
+ // Despite being only a single atomic variable, we still cannot always use
65
+ // Ordering::Relaxed, as we need to make sure a successful call to `init`
66
+ // is "ordered before" any data read through the returned pointer (which
67
+ // occurs when the function is called). Our implementation mirrors that of
68
+ // the one in libstd, meaning that the use of non-Relaxed operations is
69
+ // probably unnecessary.
70
+ let mut fptr = GETRANDOM . load ( Ordering :: Acquire ) ;
71
+ if fptr. is_null ( ) {
72
+ fptr = init ( ) ;
49
73
}
50
- Ok ( ( ) )
74
+ let fptr = unsafe { mem:: transmute :: < * mut c_void , GetRandomFn > ( fptr) } ;
75
+ sys_fill_exact ( dest, |buf| unsafe {
76
+ fptr ( buf. as_mut_ptr ( ) . cast :: < c_void > ( ) , buf. len ( ) , 0 )
77
+ } )
51
78
}
0 commit comments