Description
Problem
Hi! I have a project where I define a dylib
to use as LD_PRELOAD
by linking an extern "C"
function to .init_array
. I was using Rust 1.80.1 and unit tests were working without issues.
However, since bumping to 1.81.0, our CI (Linux runners) started failing though no significant changes had been added to the code. The thing is, if you built the project with either version and passed the resulting .so
as LD_PRELOAD
it seemed to work without issues.
I have set up a minimal reproducer that you can inspect here. It is just a "hello world" lib.rs
with a simple unit and integration test, though these tests can be removed and the output will be the same when running the steps below. The reproducer's README.md
includes sample commands ran through docker
, as I wrote it from a different OS.
Steps
- Clone the repository and check the code at
src/lib.rs
. - Run
cargo test
with Rust 1.80.1. Should work. - Run
cargo test
with Rust 1.81.0. Should fail withfatal runtime error: thread::set_current should only be called once per thread
. - Run
cargo build
and call some command passing the resulting.so
asLD_PRELOAD
, such asLD_PRELOAD=target/debug/libpreload_tests.so ls -lah
. Should work for Rust 1.80.1. - Run
cargo build
and call some command passing the resulting.so
asLD_PRELOAD
, such asLD_PRELOAD=target/debug/libpreload_tests.so ls -lah
. Should work for Rust 1.81.0.
Possible Solution(s)
I have noticed that if I add #[cfg(not(test))]
to the static
that stores the extern "C"
function and is linked to .init_array
the tests run without issues in both versions, but I'm not sure if this is a proper solution and the way to work with testing LD_PRELOAD
dylib
s or if I'm masking some other problem.
Notes
I have opened the issue here because I think the failure might lie in how the "test runner executable" is built and ran. If you read the outputs I pasted on the reproducer's README.md
for the cargo test
runs, you'll see that the behavior linked to .init_array
is present in the test runner as the log line "HOLA FROM LD_PRELOAD!"
is shown at the beginning, in both Rust toolchain versions.
Please let me know if this is not the proper place and point me to the appropriate one to open the report.
While I am not very knowledgeable about the internals that might have caused this, I'd like to figure this out, particularly to make sure that this cannot happen on an eventual "production" usage with the compiled project and not only on tests, given the actual problem I'm working with would require performing more than just a println!
and any crash during the preload prevents the actual process from running (placing it in /etc/ld.so.preload
could seemingly brick a host!).
I get that this kind of lower level, run-before-main behaviors can be pretty hard to do well, so I really want to be sure I'm doing things right.
Version
cargo 1.81.0 (2dbb1af80 2024-08-20)
release: 1.81.0
commit-hash: 2dbb1af80a2914475ba76827a312e29cedfa6b2f
commit-date: 2024-08-20
host: aarch64-unknown-linux-gnu
libgit2: 1.8.1 (sys:0.19.0 vendored)
libcurl: 8.8.0-DEV (sys:0.4.73+curl-8.8.0 vendored ssl:OpenSSL/1.1.1w)
ssl: OpenSSL 1.1.1w 11 Sep 2023
os: Debian 12.0.0 [64-bit]