11//! 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 ) ;
421
5- fn kern_arnd ( buf : & mut [ MaybeUninit < u8 > ] ) -> libc:: ssize_t {
622 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+
828 let ret = unsafe {
929 libc:: sysctl (
1030 MIB . as_ptr ( ) ,
1131 MIB . len ( ) as libc:: c_uint ,
12- buf. as_mut_ptr ( ) . cast :: < c_void > ( ) ,
32+ buf,
1333 & mut len,
1434 ptr:: null ( ) ,
1535 0 ,
@@ -22,30 +42,37 @@ fn kern_arnd(buf: &mut [MaybeUninit<u8>]) -> libc::ssize_t {
2242 }
2343}
2444
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 ;
2646
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 ( ) ) ;
2948
30- fn dlsym_getrandom ( ) -> * mut c_void {
49+ #[ cold]
50+ fn init ( ) -> * mut c_void {
3151 static NAME : & [ u8 ] = b"getrandom\0 " ;
3252 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
3461}
3562
3663pub 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 ( ) ;
4973 }
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+ } )
5178}
0 commit comments