Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

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)

AltStyle によって変換されたページ (->オリジナル) /