shadow-rs: replace println!/eprintln! with non-panicking writes (#141)#142
Conversation
println!() and eprintln!() panic when stdout/stderr is full or closed (e.g., `command >/dev/full`). Replace all production-code occurrences with non-panicking alternatives: - show_error!/show_warning! macros rewritten to use `let _ = writeln!(stderr())` instead of eprintln! - Normal output uses `let _ = writeln!(stdout)` with locked handles - PAM display_message uses `let _ = writeln!(stderr())` Also fixes a pre-existing clippy::map_unwrap_or lint in atomic.rs. Test code is intentionally left unchanged — panicking on write failure in tests is acceptable and desirable for fast failure. Fixes #141.
There was a problem hiding this comment.
Pull request overview
This PR hardens shadow-rs production binaries against panics caused by println!/eprintln! when stdout/stderr is closed or full (e.g., redirected to /dev/full), which is particularly important for setuid-root utilities.
Changes:
- Replaces
println!/eprintln!in multiple utilities and binaries with best-effortwrite!/writeln!(typically using locked stdout/stderr handles and ignoring write errors). - Updates PAM message display and shared error/warning macros to use non-panicking writes.
- Fixes a pre-existing clippy lint in
atomic.rsby replacingmap(...).unwrap_or(...)withmap_or(...).
Reviewed changes
Copilot reviewed 8 out of 10 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| src/uu/useradd/src/useradd.rs | Uses locked stdout + writeln! for useradd -D defaults output without panicking on write failure. |
| src/uu/pwck/src/pwck.rs | Replaces stderr output paths with non-panicking alternatives (including switching one message to uucore::show_error!). |
| src/uu/passwd/src/passwd.rs | Uses locked stdout for status output; replaces critical stderr log in Drop with best-effort writeln!. |
| src/uu/chsh/src/chsh.rs | Uses locked stdout + writeln! for --list-shells output. |
| src/uu/chage/src/chage.rs | Uses locked stdout + writeln! for chage -l aging info output. |
| src/shadow-core/src/pam.rs | Makes PAM display_message best-effort via writeln!(stderr, ...) to avoid panic on write failure. |
| src/shadow-core/src/error.rs | Rewrites show_error! / show_warning! macros to lock stderr and perform non-panicking writes. |
| src/shadow-core/src/atomic.rs | Refactors metadata permission selection to map_or(...) (clippy lint fix). |
| src/bin/shadow-rs.rs | Converts dispatcher usage/help/error output to best-effort writeln! (stdout/stderr). |
| src/bin/completions.rs | Converts stderr reporting to best-effort writeln! to avoid panics on write failure. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 10 changed files in this pull request and generated 6 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- show_error!/show_warning!: combine prefix + message into a single writeln! call (was two writes: write! then writeln!) - pam display_message: lock stderr before writing - passwd PrivDrop::Drop: lock stderr before writing - pwck check_passwd_entries: hoist stderr lock above the loop so per-entry diagnostics reuse one locked handle instead of reconstructing it on each iteration
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 10 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
I guess we want to update https://github.com/shadow-utils-rs/shadow-rs/blob/main/CONTRIBUTING.md#dont-panic too |
Summary
Replace all
println!/eprintln!in production code with non-panicking write alternatives. These macros panic when stdout/stderr is full or closed (e.g.,command >/dev/full), which is unacceptable for setuid-root tools that must fail gracefully.Fixes #141. Raised by @oech3.
Changes (10 files)
show_error!/show_warning!macros rewritten to uselet _ = writeln!(stderr())instead ofeprintln!let _ = writeln!(stdout)with locked handlesdisplay_messageuseslet _ = writeln!(stderr())clippy::map_unwrap_orlint in atomic.rsTest code intentionally left unchanged — panicking on write failure in tests is acceptable.
Test plan
cargo fmt --all --check— cleancargo clippy --workspace --all-targets -- -D warnings— zero warnings