1
1
//! Implementation for NetBSD
2
- use crate :: {
3
- util_libc:: { sys_fill_exact, Weak } ,
4
- Error ,
2
+ use crate :: { util_libc:: sys_fill_exact, Error } ;
3
+ use core:: {
4
+ ffi:: c_void,
5
+ mem:: MaybeUninit ,
6
+ ptr,
7
+ ptr:: NonNull ,
8
+ sync:: atomic:: { fence, AtomicPtr , Ordering } ,
5
9
} ;
6
- use core:: { mem:: MaybeUninit , ptr} ;
7
10
8
11
fn kern_arnd ( buf : & mut [ MaybeUninit < u8 > ] ) -> libc:: ssize_t {
9
12
static MIB : [ libc:: c_int ; 2 ] = [ libc:: CTL_KERN , libc:: KERN_ARND ] ;
@@ -29,7 +32,7 @@ type GetRandomFn = unsafe extern "C" fn(*mut u8, libc::size_t, libc::c_uint) ->
29
32
30
33
pub fn getrandom_inner ( dest : & mut [ MaybeUninit < u8 > ] ) -> Result < ( ) , Error > {
31
34
// getrandom(2) was introduced in NetBSD 10.0
32
- static GETRANDOM : Weak = unsafe { Weak :: new ( "getrandom \0 " ) } ;
35
+ static GETRANDOM : WeakGetrandom = WeakGetrandom :: new ( ) ;
33
36
if let Some ( fptr) = GETRANDOM . ptr ( ) {
34
37
let func: GetRandomFn = unsafe { core:: mem:: transmute ( fptr) } ;
35
38
return sys_fill_exact ( dest, |buf| unsafe {
@@ -44,3 +47,57 @@ pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
44
47
}
45
48
Ok ( ( ) )
46
49
}
50
+
51
+ // A "weak" binding to the `getrandom` function that may or may not be present at runtime.
52
+ // Used for supporting newer OS features while still building on older systems.
53
+ // Based off of the DlsymWeak struct in libstd:
54
+ // https://github.com/rust-lang/rust/blob/1.61.0/library/std/src/sys/unix/weak.rs#L84
55
+ // except that the caller must manually cast self.ptr() to a function pointer.
56
+ pub struct WeakGetrandom {
57
+ addr : AtomicPtr < c_void > ,
58
+ }
59
+
60
+ impl WeakGetrandom {
61
+ // A non-null pointer value which indicates we are uninitialized. This
62
+ // constant should ideally not be a valid address of a function pointer.
63
+ // However, if by chance libc::dlsym does return UNINIT, there will not
64
+ // be undefined behavior. libc::dlsym will just be called each time ptr()
65
+ // is called. This would be inefficient, but correct.
66
+ // TODO: Replace with core::ptr::invalid_mut(1) when that is stable.
67
+ const UNINIT : * mut c_void = 1 as * mut c_void ;
68
+
69
+ // Construct a weak binding to the `getrandom` function.
70
+ pub const fn new ( ) -> Self {
71
+ Self {
72
+ addr : AtomicPtr :: new ( Self :: UNINIT ) ,
73
+ }
74
+ }
75
+
76
+ // Return the address of a function if present at runtime. Otherwise,
77
+ // return None. Multiple callers can call ptr() concurrently. It will
78
+ // always return _some_ value returned by libc::dlsym. However, the
79
+ // dlsym function may be called multiple times.
80
+ pub fn ptr ( & self ) -> Option < NonNull < c_void > > {
81
+ // Despite having only a single atomic variable (self.addr), we still
82
+ // cannot always use Ordering::Relaxed, as we need to make sure a
83
+ // successful call to dlsym() is "ordered before" any data read through
84
+ // the returned pointer (which occurs when the function is called).
85
+ // Our implementation mirrors that of the one in libstd, meaning that
86
+ // the use of non-Relaxed operations is probably unnecessary.
87
+ match self . addr . load ( Ordering :: Relaxed ) {
88
+ Self :: UNINIT => {
89
+ let name = b"getrandom\0 " ;
90
+ assert_eq ! ( name[ name. len( ) - 1 ] , 0 ) ;
91
+ let addr = unsafe { libc:: dlsym ( libc:: RTLD_DEFAULT , name. as_ptr ( ) as * const _ ) } ;
92
+ // Synchronizes with the Acquire fence below
93
+ self . addr . store ( addr, Ordering :: Release ) ;
94
+ NonNull :: new ( addr)
95
+ }
96
+ addr => {
97
+ let func = NonNull :: new ( addr) ?;
98
+ fence ( Ordering :: Acquire ) ;
99
+ Some ( func)
100
+ }
101
+ }
102
+ }
103
+ }
0 commit comments