Skip to content

Commit 0332a66

Browse files
committed
std: improve safety of dlsym!
1 parent dd28c1e commit 0332a66

File tree

2 files changed

+33
-15
lines changed

2 files changed

+33
-15
lines changed

library/std/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,7 @@
350350
#![feature(float_gamma)]
351351
#![feature(float_minimum_maximum)]
352352
#![feature(fmt_internals)]
353+
#![feature(fn_ptr_trait)]
353354
#![feature(generic_atomic)]
354355
#![feature(hasher_prefixfree_extras)]
355356
#![feature(hashmap_internals)]

library/std/src/sys/pal/unix/weak.rs

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
#![forbid(unsafe_op_in_unsafe_fn)]
2424

2525
use crate::ffi::{CStr, c_char, c_void};
26-
use crate::marker::PhantomData;
26+
use crate::marker::{FnPtr, PhantomData};
2727
use crate::sync::atomic::{Atomic, AtomicPtr, Ordering};
2828
use crate::{mem, ptr};
2929

@@ -76,8 +76,18 @@ pub(crate) macro dlsym {
7676
#[link_name = $sym:expr]
7777
fn $name:ident($($param:ident : $t:ty),* $(,)?) -> $ret:ty;
7878
) => (
79-
static DLSYM: DlsymWeak<unsafe extern "C" fn($($t),*) -> $ret> =
80-
DlsymWeak::new(concat!($sym, '\0'));
79+
static DLSYM: DlsymWeak<unsafe extern "C" fn($($t),*) -> $ret> = {
80+
let Ok(name) = CStr::from_bytes_with_nul(concat!($sym, '\0').as_bytes()) else {
81+
panic!("symbol name may not contain NUL")
82+
};
83+
84+
// SAFETY: Whoever calls the function pointer returned by `get()`
85+
// is responsible for ensuring that the signature is correct. Just
86+
// like with extern blocks, this is syntactically enforced by making
87+
// the function pointer be unsafe.
88+
unsafe { DlsymWeak::new(name) }
89+
};
90+
8191
let $name = &DLSYM;
8292
)
8393
}
@@ -90,12 +100,13 @@ pub(crate) struct DlsymWeak<F> {
90100
_marker: PhantomData<F>,
91101
}
92102

93-
impl<F> DlsymWeak<F> {
94-
pub(crate) const fn new(name: &'static str) -> Self {
95-
let Ok(name) = CStr::from_bytes_with_nul(name.as_bytes()) else {
96-
panic!("not a nul-terminated string")
97-
};
98-
103+
impl<F: FnPtr> DlsymWeak<F> {
104+
/// # Safety
105+
///
106+
/// If the signature of `F` does not match the signature of the symbol (if
107+
/// it exists), calling the function pointer returned by `get()` is
108+
/// undefined behaviour.
109+
pub(crate) const unsafe fn new(name: &'static CStr) -> Self {
99110
DlsymWeak {
100111
name: name.as_ptr(),
101112
func: AtomicPtr::new(ptr::without_provenance_mut(1)),
@@ -125,26 +136,32 @@ impl<F> DlsymWeak<F> {
125136
match self.func.load(Ordering::Acquire) {
126137
func if func.addr() == 1 => self.initialize(),
127138
func if func.is_null() => None,
139+
// SAFETY:
140+
// `func` is not null and `F` implements `FnPtr`, thus this
141+
// transmutation is well-defined. It is the responsibility of the
142+
// creator of this `DlsymWeak` to ensure that calling the resulting
143+
// function pointer does not result in undefined behaviour (though
144+
// the `dlsym!` macro delegates this responsibility to the caller
145+
// of the function by using `unsafe` function pointers).
146+
// FIXME: use `transmute` once it stops complaining about generics.
128147
func => Some(unsafe { mem::transmute_copy::<*mut c_void, F>(&func) }),
129148
}
130149
}
131150

132151
// Cold because it should only happen during first-time initialization.
133152
#[cold]
134153
fn initialize(&self) -> Option<F> {
135-
const {
136-
if size_of::<F>() != size_of::<*mut libc::c_void>() {
137-
panic!("not a function pointer")
138-
}
139-
}
140-
154+
// SAFETY: `self.name` was created from a `&'static CStr` and is
155+
// therefore a valid C string pointer.
141156
let val = unsafe { libc::dlsym(libc::RTLD_DEFAULT, self.name) };
142157
// This synchronizes with the acquire load in `get`.
143158
self.func.store(val, Ordering::Release);
144159

145160
if val.is_null() {
146161
None
147162
} else {
163+
// SAFETY: see the comment in `get`.
164+
// FIXME: use `transmute` once it stops complaining about generics.
148165
Some(unsafe { mem::transmute_copy::<*mut libc::c_void, F>(&val) })
149166
}
150167
}

0 commit comments

Comments
 (0)