Repository navigation
shadow-core: read AT_EXECFN through rustix, drop the getauxval unsafe - #402
pierre-warnier wants to merge 2 commits into
Conversation
Either feature makes rustix look getauxval(3) up by name at run time. In the static musl binary nothing links that symbol in, so AT_EXECFN reads as empty and the argv[0] check refuses every setuid invocation (#302, which uucore 0.11 brought in by enabling use-libc-auxv). Banning both makes cargo-deny fail if any crate in the graph turns them back on.
With use-libc-auxv off, rustix's linux_raw backend reads the auxiliary vector itself (PR_GET_AUXV, falling back to /proc/self/auxv), so rustix::param::linux_execfn is correct in the static musl binary too and the unsafe getauxval call is no longer needed. An empty value still fails closed, and rustix panics, an abort in release builds, when it can read the auxiliary vector neither way. Suggested by oech3.
|
You cannot use raw-backend because |
|
Yes. But we cannot drop it yet. I opened bytecodealliance/rustix#1735 for the future. I'm not sure if people want to use |
| std::ffi::CStr::from_ptr(ptr) | ||
| }; | ||
| let execfn = rustix::param::linux_execfn(); | ||
| if execfn.is_empty() { |
There was a problem hiding this comment.
I'm not sure if .is_empty() is possible. Maybe, debug_assert!?
| if execfn.is_empty() { | ||
| return false; | ||
| } | ||
| let execfn = execfn.to_string_lossy(); |
There was a problem hiding this comment.
This is not coming from this PR. But can we avoid lossy conversion e.g. as_encoded_bytes?
We need last utf-8 part only (since all util names are utf-8).
|
@oech3 you're right, thanks for checking. Without Once bytecodealliance/rustix#1735 is released, we can switch to |
The argv[0] check in the multicall binary read
AT_EXECFNwith a directunsafecall tolibc::getauxval. That call went in for #302: the static musl archive refused every setuid invocation because rustix foundgetauxvalby name at run time, and nothing in a static link pulls that symbol in.The by-name lookup was never rustix's default. It comes from rustix's
use-libc-auxvfeature, which uucore 0.11 turned on; since uucore 0.12 nothing in the graph enables it. Without it, rustix's linux_raw backend reads the auxiliary vector itself (PR_GET_AUXV, falling back to/proc/self/auxv). See bytecodealliance/rustix#1734. Thanks to @oech3 for suggesting this.This PR:
rustix::param::linux_execfn()and removes theunsafeblock. An empty value still fails closed. If rustix cannot read the auxiliary vector at all (noPR_GET_AUXVand no/proc), it panics, which is an abort in release builds, so that case fails closed too.use-libc-auxvanduse-libcin deny.toml.use-libcswitches rustix to its libc backend, which does the same weak lookup. If a dependency ever turns either one back on,cargo deny check bansfails instead of the regression shipping silently.shadow-core::processkeeps#[allow(unsafe_code)]. It still wraps setuid, getpwuid_r, sigprocmask and others.Evidence:
cargo tree -e featuresshowsuse-libc-auxvoff everywhere.AT_EXECFNwhen installed setuid root and run by an unprivileged user, both normally and withPR_GET_AUXVblocked by seccomp (EINVAL, which forces the/procfallback). Withuse-libc-auxvon, the same probe returned an empty string, which is the shadow-core: read AT_EXECFN through getauxval, not a run-time lookup #302 failure.make dist-musland ran it in Alpine, installed 4755 root, reached through/usr/local/bin/passwd -> /usr/local/sbin/shadow-rs, as an unprivileged user. I ran it under the default seccomp profile and again withPR_GET_AUXVblocked. Both timespasswd -Sprintedprobe L 2026-11-08 0 99999 7 -1and exited 0, andexec -a chsh /usr/local/bin/passwd -Swas refused withargv[0] does not match executed binary.feature-bannedandbans FAILED.cargo fmt --all --check, clippy-D warnings(with and withoutpam),cargo test -p uu_shadow_coreandcargo deny checkall pass.Regression coverage: the existing
Static musl archiveCI job already installs the archive 4755 root behind apasswdsymlink and runspasswd -Sasnobody. That is exactly the case #302 broke, so I did not add a second copy of it.tests/verify-release.shalso checks that a spoofed argv[0] is refused.Reviews welcome. Without objection, this will be merged after 7 days.