Skip to content

Commit 6194bec

Browse files
committed
Fix segfault in env access in statically linked FreeBSD binaries
std resolves environ through dlsym on FreeBSD so that shared libraries can link, but a statically linked executable has no dynamic symbol table: the lookup returns null and the env iteration dereferences it. Take a weak reference instead: it binds at link time in any executable, and in a shared library the runtime linker resolves it against the environ the executable exports. Treat a null environ as an empty environment instead of dereferencing it.
1 parent 375b143 commit 6194bec

1 file changed

Lines changed: 22 additions & 19 deletions

File tree

library/std/src/sys/env/unix.rs

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -38,21 +38,19 @@ pub unsafe fn environ() -> *mut *const *const c_char {
3838
unsafe { libc::_NSGetEnviron() as *mut *const *const c_char }
3939
}
4040

41-
// On FreeBSD, environ comes from CRT rather than libc
41+
// On FreeBSD, environ lives in crt1.o, not libc.so, so a shared library
42+
// cannot take a strong link-time reference to it (#153451), and dlsym cannot
43+
// find it in a statically linked executable, which has no dynamic symbol
44+
// table to search (#158939). A weak reference covers both: it binds at link
45+
// time in any executable, and in a shared library the runtime linker
46+
// resolves it against the environ the executable exports.
4247
#[cfg(target_os = "freebsd")]
4348
pub unsafe fn environ() -> *mut *const *const c_char {
44-
use crate::sync::LazyLock;
45-
46-
struct Environ(*mut *const *const c_char);
47-
unsafe impl Send for Environ {}
48-
unsafe impl Sync for Environ {}
49-
50-
static ENVIRON: LazyLock<Environ> = LazyLock::new(|| {
51-
Environ(unsafe {
52-
libc::dlsym(libc::RTLD_DEFAULT, c"environ".as_ptr()) as *mut *const *const c_char
53-
})
54-
});
55-
ENVIRON.0
49+
unsafe extern "C" {
50+
#[linkage = "extern_weak"]
51+
static environ: *mut *const *const c_char;
52+
}
53+
unsafe { environ }
5654
}
5755

5856
// Use the `environ` static which is part of POSIX.
@@ -75,14 +73,19 @@ pub fn env_read_lock() -> impl Drop {
7573
pub fn env() -> Env {
7674
unsafe {
7775
let _guard = env_read_lock();
78-
let mut environ = *environ();
7976
let mut result = Vec::new();
80-
if !environ.is_null() {
81-
while !(*environ).is_null() {
82-
if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) {
83-
result.push(key_value);
77+
// A null return means the platform could not locate the symbol;
78+
// treat it like an empty environment.
79+
let environ_ptr = environ();
80+
if !environ_ptr.is_null() {
81+
let mut environ = *environ_ptr;
82+
if !environ.is_null() {
83+
while !(*environ).is_null() {
84+
if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) {
85+
result.push(key_value);
86+
}
87+
environ = environ.add(1);
8488
}
85-
environ = environ.add(1);
8689
}
8790
}
8891
return Env::new(result);

0 commit comments

Comments
 (0)