Skip to content

Commit 267639e

Browse files
authored
netbsd: Simplify weak lookup. (#484)
Remove LazyPtr. Avoid constructing an invalid pointer as a sentinel to indicate that the pointer is uninitialized. Now, a null pointer means it is uninitialized, and a non-null pointer means it is initialized. This is less questionable from a safety perspective, and should also be more efficient. Reduce duplication between the "getrandom is available" and the fallback case.
1 parent c740f03 commit 267639e

File tree

2 files changed

+51
-82
lines changed

2 files changed

+51
-82
lines changed

src/lazy.rs

Lines changed: 1 addition & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
//! Helpers built around pointer-sized atomics.
22
#![cfg(target_has_atomic = "ptr")]
33
#![allow(dead_code)]
4-
use core::{
5-
ffi::c_void,
6-
sync::atomic::{AtomicPtr, AtomicUsize, Ordering},
7-
};
4+
use core::sync::atomic::{AtomicUsize, Ordering};
85

96
// This structure represents a lazily initialized static usize value. Useful
107
// when it is preferable to just rerun initialization instead of locking.
@@ -67,58 +64,3 @@ impl LazyBool {
6764
self.0.unsync_init(|| usize::from(init())) != 0
6865
}
6966
}
70-
71-
// This structure represents a lazily initialized static pointer value.
72-
///
73-
/// It's intended to be used for weak linking of a C function that may
74-
/// or may not be present at runtime.
75-
///
76-
/// Based off of the DlsymWeak struct in libstd:
77-
/// https://github.com/rust-lang/rust/blob/1.61.0/library/std/src/sys/unix/weak.rs#L84
78-
/// except that the caller must manually cast self.ptr() to a function pointer.
79-
pub struct LazyPtr {
80-
addr: AtomicPtr<c_void>,
81-
}
82-
83-
impl LazyPtr {
84-
/// A non-null pointer value which indicates we are uninitialized.
85-
///
86-
/// This constant should ideally not be a valid pointer. However,
87-
/// if by chance initialization function passed to the `unsync_init`
88-
/// method does return UNINIT, there will not be undefined behavior.
89-
/// The initialization function will just be called each time `get()`
90-
/// is called. This would be inefficient, but correct.
91-
const UNINIT: *mut c_void = !0usize as *mut c_void;
92-
93-
/// Construct new `LazyPtr` in uninitialized state.
94-
pub const fn new() -> Self {
95-
Self {
96-
addr: AtomicPtr::new(Self::UNINIT),
97-
}
98-
}
99-
100-
// Runs the init() function at most once, returning the value of some run of
101-
// init(). Multiple callers can run their init() functions in parallel.
102-
// init() should always return the same value, if it succeeds.
103-
pub fn unsync_init(&self, init: impl Fn() -> *mut c_void) -> *mut c_void {
104-
#[cold]
105-
fn do_init(this: &LazyPtr, init: impl Fn() -> *mut c_void) -> *mut c_void {
106-
let addr = init();
107-
this.addr.store(addr, Ordering::Release);
108-
addr
109-
}
110-
111-
// Despite having only a single atomic variable (self.addr), we still
112-
// cannot always use Ordering::Relaxed, as we need to make sure a
113-
// successful call to `init` is "ordered before" any data read through
114-
// the returned pointer (which occurs when the function is called).
115-
// Our implementation mirrors that of the one in libstd, meaning that
116-
// the use of non-Relaxed operations is probably unnecessary.
117-
let val = self.addr.load(Ordering::Acquire);
118-
if val != Self::UNINIT {
119-
val
120-
} else {
121-
do_init(self, init)
122-
}
123-
}
124-
}

src/netbsd.rs

Lines changed: 50 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,35 @@
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

3663
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();
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

Comments
 (0)