stdbuf: fix tmpdir leak - #14059
Conversation
Two security/correctness issues reported in uutils#13939: 1. Leaked temporary directories: using exec() replaced the stdbuf process before TempDir's destructor could run, leaving one .tmp* directory per invocation in $TMPDIR forever. Fix: use spawn() + wait() so the parent process survives to drop the TempDir after the child exits. 2. World-readable temporary directory: the tmpdir was created with default permissions, making libstdbuf.so writable by any user on a multi-user system (privilege-escalation risk). Fix: call set_permissions(0o700) immediately after creation, bypassing the umask. Both fixes apply only when feat_external_libstdbuf is not set (i.e. the embedded .so path).
Use tempfile::Builder::permissions so the directory is never world-accessible between tempdir() and chmod, closing a TOCTOU window on permissive umasks. Co-authored-by: Cursor <cursoragent@cursor.com>
Open the injected shared library with an explicit mode so umask 0 cannot leave a world-writable .so in the private temp directory. Co-authored-by: Cursor <cursoragent@cursor.com>
Point at rust-lang/cargo#8317 as the still-open request for cargo install to support installing shared libraries, since that's the root cause of the /tmp fallback described in the comment above. Co-authored-by: Cursor <cursoragent@cursor.com>
PathBuf lost its import when the exec() use was removed, and the 0700 tempdir helpers were gated on the feature alone rather than on Unix. The external-libstdbuf path no longer creates a temp directory it immediately throws away, and waiting on the child now maps a signal death to 128+signal so the caller still sees what exec() would have shown it.
The Windows build never creates a temporary directory: build.rs enables feat_external_libstdbuf off Unix and the DLL comes from the Cygwin package. Saying spawn() is there for the TempDir destructor only described the Unix half of a shared path.
|
GNU testsuite comparison: |
Merging this PR will degrade performance by 4.22%
|
| Mode | Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|---|
| ❌ | Simulation | du_summarize_balanced_tree[(5, 4, 10)] |
16.2 ms | 16.9 ms | -4.22% |
Tip
Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.
Comparing Ecordonnier:stdbuf-fix-tmpdir-leak (e00ac36) with main (df30282)
Footnotes
-
50 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩
|
Can we reuse a directory named |
| #[cfg(unix)] | ||
| let e = command.exec(); | ||
| #[cfg(windows)] | ||
| // Spawn a child so the TempDir destructor fires in the parent, cleaning up |
There was a problem hiding this comment.
I think this is mistake because WIndows native stdbuf depends on Cygwin's libstdbuf.dll binary given by different package.
Two security/correctness issues reported in #13939:
Leaked temporary directories: using exec() replaced the stdbuf process before TempDir's destructor could run, leaving one .tmp* directory per invocation in $TMPDIR forever.
Fix: use spawn() + wait() so the parent process survives to drop the TempDir after the child exits.
World-readable temporary directory: the tmpdir was created with default permissions, making libstdbuf.so writable by any user on a multi-user system (privilege-escalation risk).
Fix: call set_permissions(0o700) immediately after creation, bypassing the umask.
Both fixes apply only when feat_external_libstdbuf is not set (i.e. the embedded .so path).