From 70811cd35ba7c7f23782e4d1701f010ca38e5c77 Mon Sep 17 00:00:00 2001 From: Valentyn Kit Date: Fri, 10 Jul 2026 14:55:39 +0300 Subject: [PATCH] std: fix FreeBSD environ() null deref under crt-static On FreeBSD, environ() looks up the environ symbol with dlsym(RTLD_DEFAULT, "environ"), which needs a dynamic symbol table. A statically linked executable (-C target-feature=+crt-static) has none, so dlsym returns null and every caller that walks the environment array (std::env::vars, Command::spawn) derefs it unchecked and segfaults. Fall back to a weakly linked reference to environ when dlsym fails. Reading an extern_weak static yields the symbol's address, or null when nothing defines it, so the fallback resolves in a static executable (crt1.o defines environ) while a cdylib linked with -Wl,--no-undefined still links, the same constraint the dlsym lookup was introduced to satisfy. --- library/std/src/sys/env/unix.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/library/std/src/sys/env/unix.rs b/library/std/src/sys/env/unix.rs index 7a19f97b3ad50..744a4c526e0b0 100644 --- a/library/std/src/sys/env/unix.rs +++ b/library/std/src/sys/env/unix.rs @@ -49,7 +49,19 @@ pub unsafe fn environ() -> *mut *const *const c_char { static ENVIRON: LazyLock = LazyLock::new(|| { Environ(unsafe { - libc::dlsym(libc::RTLD_DEFAULT, c"environ".as_ptr()) as *mut *const *const c_char + let dynamic = + libc::dlsym(libc::RTLD_DEFAULT, c"environ".as_ptr()) as *mut *const *const c_char; + if !dynamic.is_null() { + dynamic + } else { + // Reading an `extern_weak` static yields the symbol's address + // (null if unresolved), not its contents. + unsafe extern "C" { + #[linkage = "extern_weak"] + static environ: *mut *const *const c_char; + } + environ + } }) }); ENVIRON.0