Skip to content

Commit 0498e88

Browse files
committed
Use AtomicPtr instead of AtomicUsize for Weak
This allows Strict Provenance to work properly, fixing #262. It also now matches what `libstd` does: https://github.com/rust-lang/rust/blob/9f7e997c8bc3cacd2ab4eb75e63cb5fa9279c7b0/library/std/src/sys/unix/weak.rs#L85-L141 Also, while reading the `libstd` code, I noticed that they use an `Acquire` fence and `Release` store as the returned pointer should have "consume" semantics. I changed our code to do something slightly stronger (Acquire load and Release store) for consistancy. Signed-off-by: Joe Richey <[email protected]>
1 parent 9e2c896 commit 0498e88

File tree

1 file changed

+36
-12
lines changed

1 file changed

+36
-12
lines changed

src/util_libc.rs

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,13 @@
66
// option. This file may not be copied, modified, or distributed
77
// except according to those terms.
88
#![allow(dead_code)]
9-
use crate::{util::LazyUsize, Error};
10-
use core::{num::NonZeroU32, ptr::NonNull};
9+
use crate::Error;
10+
use core::{
11+
num::NonZeroU32,
12+
ptr::NonNull,
13+
sync::atomic::{AtomicPtr, Ordering},
14+
};
15+
use libc::c_void;
1116

1217
cfg_if! {
1318
if #[cfg(any(target_os = "netbsd", target_os = "openbsd", target_os = "android"))] {
@@ -76,29 +81,48 @@ pub fn sys_fill_exact(
7681

7782
// A "weak" binding to a C function that may or may not be present at runtime.
7883
// Used for supporting newer OS features while still building on older systems.
79-
// F must be a function pointer of type `unsafe extern "C" fn`. Based off of the
80-
// weak! macro in libstd.
84+
// Based off of the DlsymWeak struct in libstd (src/sys/unix/weak.rs), except
85+
// that the caller must cast self.ptr() to a function pointer.
8186
pub struct Weak {
8287
name: &'static str,
83-
addr: LazyUsize,
88+
addr: AtomicPtr<c_void>,
8489
}
8590

8691
impl Weak {
92+
// A non-null pointer value which indicates we are uninitialized. This
93+
// constant should ideally not be a valid address of a function pointer.
94+
// However, if by chance libc::dlsym does return UNINIT, there will not
95+
// be undefined behavior. libc::dlsym will just be called each time ptr()
96+
// is called. This would be inefficient, but correct.
97+
// TODO: Replace with core::ptr::invalid_mut(1) when that is stable.
98+
const UNINIT: *mut c_void = 1 as *mut c_void;
99+
87100
// Construct a binding to a C function with a given name. This function is
88101
// unsafe because `name` _must_ be null terminated.
89102
pub const unsafe fn new(name: &'static str) -> Self {
90103
Self {
91104
name,
92-
addr: LazyUsize::new(),
105+
addr: AtomicPtr::new(Self::UNINIT),
93106
}
94107
}
95108

96-
// Return a function pointer if present at runtime. Otherwise, return null.
97-
pub fn ptr(&self) -> Option<NonNull<libc::c_void>> {
98-
let addr = self.addr.unsync_init(|| unsafe {
99-
libc::dlsym(libc::RTLD_DEFAULT, self.name.as_ptr() as *const _) as usize
100-
});
101-
NonNull::new(addr as *mut _)
109+
// Return the address of a function if present at runtime. Otherwise,
110+
// return null. Multiple callers can call ptr() concurrently. It will
111+
// always return _some_ value returned by libc::dlsym. However, the
112+
// dlsym function may be called multiple times.
113+
pub fn ptr(&self) -> Option<NonNull<c_void>> {
114+
// Despite having only a single atomic variable (self.addr), we still
115+
// need a "consume" ordering as we will generally be reading though
116+
// this value (by calling the function we dlsym'ed). Rust lacks this
117+
// ordering, so we have to go with the next strongest: Acquire/Release.
118+
// As noted in libstd, this might be unnecessary.
119+
let mut addr = self.addr.load(Ordering::Acquire);
120+
if addr == Self::UNINIT {
121+
let symbol = self.name.as_ptr() as *const _;
122+
addr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, symbol) };
123+
self.addr.store(addr, Ordering::Release);
124+
}
125+
NonNull::new(addr)
102126
}
103127
}
104128

0 commit comments

Comments
 (0)