Fix segfault in env access in statically linked FreeBSD binaries#159112
Conversation
|
Thanks for the pull request, and welcome! The Rust Project is excited to review your changes, and you should hear from @clarfonthey (or someone else) some time within the next two weeks. Please see the contribution instructions for more information. Namely, in order to ensure the minimum review times lag, PR authors and assigned reviewers should ensure that the review label (
Why was this reviewer chosen?The reviewer was selected based on:
|
This comment has been minimized.
This comment has been minimized.
422f653 to
d0615ff
Compare
|
@rustbot label +regression-from-stable-to-stable |
|
Note: #159097 is an earlier draft PR for the same issue, using the same |
d0615ff to
31ae9ac
Compare
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.
31ae9ac to
6194bec
Compare
|
All comments were addressed, all discussions resolved. |
|
LGTM, will wait for @bjorn3 to give it a look and then can merge. |
|
LGTM |
|
@bors r+ rollup |
… r=clarfonthey Fix segfault in env access in statically linked FreeBSD binaries On FreeBSD `environ` is provided by the startup object linked into every executable (crt1.o) rather than by libc.so, so rust-lang#153718 switched std to a dlsym lookup to keep shared libraries linkable with `-Wl,--no-undefined` (rust-lang#153451). But dlsym needs a dynamic symbol table, which statically linked executables do not have: it returns null, and both `env::vars_os` and the `Command` spawn paths dereference it (rust-lang#158939). Resolve `environ` through a weak reference first: crt1.o defines it in every executable, statically linked ones included, and a weak undefined symbol does not break shared library links. Fall back to dlsym only when the weak reference is null, i.e. inside a shared library, where the dynamic lookup is available. Also stop dereferencing a null table pointer in `env()`, so an unresolvable `environ` degrades to an empty environment instead of a crash. The `extern_weak` reference is the same mechanism std already uses for `__dso_handle` and `__cxa_thread_atexit_impl` in `sys/thread_local/destructors/linux_like.rs`. ## Testing CI cannot execute FreeBSD tests (the target is cross-compiled only), so: - `./x check library/std --target x86_64-unknown-freebsd` and a host check pass. - The reproduction from rust-lang#158939, built with `-C target-feature=+crt-static`, was executed on FreeBSD 14.4-RELEASE (amd64): built with stock 1.96.0 it segfaults on `vars_os()` (SIGSEGV, exit 139); built against std from this branch it iterates the environment, spawns a child with an inherited environment, and exits cleanly. - Downstream, the crash was originally isolated and worked around in a production daemon whose CI runs the exact affected configuration (static, LTO) on a real FreeBSD kernel per commit; that harness stands ready to validate a nightly containing this fix. This is a regression from stable to stable (1.95 -> 1.96) and may be worth a beta backport. Fixes rust-lang#158939
…uwer Rollup of 7 pull requests Successful merges: - #158732 (Apply MCP 1003 and move diagnostics.rs into its own module) - #159010 (Simplify the unwind crate) - #159131 (bootstrap: Allow path-based skipping of the coverage test suite) - #159134 (std: fix panic in Windows Stdin::read_vectored with pending surrogate) - #159178 (Print duration of BOLT instrumentation and optimization steps) - #159112 (Fix segfault in env access in statically linked FreeBSD binaries) - #159124 (compiletest: use VecDeque::pop_front_if)
…uwer Rollup of 6 pull requests Successful merges: - #158732 (Apply MCP 1003 and move diagnostics.rs into its own module) - #159131 (bootstrap: Allow path-based skipping of the coverage test suite) - #159134 (std: fix panic in Windows Stdin::read_vectored with pending surrogate) - #159178 (Print duration of BOLT instrumentation and optimization steps) - #159112 (Fix segfault in env access in statically linked FreeBSD binaries) - #159124 (compiletest: use VecDeque::pop_front_if)
Rollup merge of #159112 - sbogomolov:freebsd-static-environ, r=clarfonthey Fix segfault in env access in statically linked FreeBSD binaries On FreeBSD `environ` is provided by the startup object linked into every executable (crt1.o) rather than by libc.so, so #153718 switched std to a dlsym lookup to keep shared libraries linkable with `-Wl,--no-undefined` (#153451). But dlsym needs a dynamic symbol table, which statically linked executables do not have: it returns null, and both `env::vars_os` and the `Command` spawn paths dereference it (#158939). Resolve `environ` through a weak reference first: crt1.o defines it in every executable, statically linked ones included, and a weak undefined symbol does not break shared library links. Fall back to dlsym only when the weak reference is null, i.e. inside a shared library, where the dynamic lookup is available. Also stop dereferencing a null table pointer in `env()`, so an unresolvable `environ` degrades to an empty environment instead of a crash. The `extern_weak` reference is the same mechanism std already uses for `__dso_handle` and `__cxa_thread_atexit_impl` in `sys/thread_local/destructors/linux_like.rs`. ## Testing CI cannot execute FreeBSD tests (the target is cross-compiled only), so: - `./x check library/std --target x86_64-unknown-freebsd` and a host check pass. - The reproduction from #158939, built with `-C target-feature=+crt-static`, was executed on FreeBSD 14.4-RELEASE (amd64): built with stock 1.96.0 it segfaults on `vars_os()` (SIGSEGV, exit 139); built against std from this branch it iterates the environment, spawns a child with an inherited environment, and exits cleanly. - Downstream, the crash was originally isolated and worked around in a production daemon whose CI runs the exact affected configuration (static, LTO) on a real FreeBSD kernel per commit; that harness stands ready to validate a nightly containing this fix. This is a regression from stable to stable (1.95 -> 1.96) and may be worth a beta backport. Fixes #158939
View all comments
On FreeBSD
environis provided by the startup object linked into every executable (crt1.o) rather than by libc.so, so #153718 switched std to a dlsym lookup to keep shared libraries linkable with-Wl,--no-undefined(#153451). But dlsym needs a dynamic symbol table, which statically linked executables do not have: it returns null, and bothenv::vars_osand theCommandspawn paths dereference it (#158939).Resolve
environthrough a weak reference first: crt1.o defines it in every executable, statically linked ones included, and a weak undefined symbol does not break shared library links. Fall back to dlsym only when the weak reference is null, i.e. inside a shared library, where the dynamic lookup is available. Also stop dereferencing a null table pointer inenv(), so an unresolvableenvirondegrades to an empty environment instead of a crash.The
extern_weakreference is the same mechanism std already uses for__dso_handleand__cxa_thread_atexit_implinsys/thread_local/destructors/linux_like.rs.Testing
CI cannot execute FreeBSD tests (the target is cross-compiled only), so:
./x check library/std --target x86_64-unknown-freebsdand a host check pass.-C target-feature=+crt-static, was executed on FreeBSD 14.4-RELEASE (amd64): built with stock 1.96.0 it segfaults onvars_os()(SIGSEGV, exit 139); built against std from this branch it iterates the environment, spawns a child with an inherited environment, and exits cleanly.This is a regression from stable to stable (1.95 -> 1.96) and may be worth a beta backport.
Fixes #158939