Summary
install --backup=simple is supposed to refuse an operation when creating the destination's backup would overwrite the source file. install performs no such check at all: it destroys the source file and exits 0 with no diagnostic. Unlike the mv issue I reported earlier, no spelling mismatch is required — the plain form install --backup=simple a~ a is unprotected. GNU coreutils refuses every such case with a non-zero exit and an error.
Per SECURITY.md this is both a bypass of a documented safety guard and an unintended destructive action — a divergence from GNU where the check "fails open" instead of erroring.
Details
src/uu/install/src/install.rs imports uucore::backup_control and computes a backup path at install.rs:918 via backup_control::get_backup_path, but never performs any source-is-backup check before taking the backup:
$ grep -n source_is_target_backup src/uu/install/src/install.rs
$ # no output
Because the check is absent rather than incorrect, every spelling of the destination fails — a, ./a, $PWD/a alike.
Suggested fix. cp already does this correctly — src/uu/cp/src/cp.rs:2121:
let backup_path = backup_control::get_backup_path(options.backup, dest, &options.backup_suffix);
if let Some(backup_path) = backup_path {
if paths_refer_to_same_file(source, &backup_path, true) {
return Err(translate!("cp-error-backing-up-destroy-source", ...).into());
}
...
}
install already computes the backup path via get_backup_path; the missing piece is the uucore::fs::paths_refer_to_same_file guard before the backup is taken.
PoC
Verified on:
- 0.9.0, release tag
840c36d (2026-05-29) — the latest release
main tip f66e155d80df3d82798324726e460675a02c5cd5 (2026-07-27)
Ubuntu 24.04.2 LTS, Linux 6.8.0 x86_64, ext4. Console output captured with LANG=C.
No special configuration. Default build, no environment variables, any filesystem — the defect is filesystem- and platform-independent. The only requirement is --backup=simple (or -b with the default suffix) and a source named <destination><suffix>.
$ cd "$(mktemp -d)"
$ : > a # destination, empty
$ echo payload > a~ # source, holds the only copy of the data
$ install --backup=simple a~ a ; echo "exit=$?"
exit=0
$ cat a ; cat a~
payload no longer exists anywhere.
Unlike mv, no spelling mismatch is needed — the same happens with ./a as the destination:
$ install --backup=simple a~ ./a ; echo "exit=$?"
exit=0
Both GNU coreutils 9.7 and 9.11 refuse both forms:
$ install --backup=simple a~ a ; echo "exit=$?"
install: backing up 'a' might destroy source; 'a~' not copied
exit=1
Impact
For install --backup=simple a~ a:
- The destination's backup is created:
a is renamed to a~ — this overwrites the source.
- The copy proceeds from
a~, which no longer holds the original data.
The destination ends up unchanged, the source is gone, and the exit status is 0. The operation reports success while doing the opposite of what was requested.
Because the exit code is 0, defensive idioms provide no protection:
install --backup=simple "$src" "$dst" || rollback # rollback never runs
set -e; install --backup=simple "$src" "$dst" # never aborts
Concrete case:
install --backup=simple /usr/local/bin/app~ /usr/local/bin/app
# exit 0, reported as success
# app unchanged; the retained previous version destroyed
Rolling back to the retained previous version leaves the current binary in place and destroys the copy that was being rolled back to.
Security impact.
Recovery fails at the moment it is exercised. Restoring from a backup is what an operator reaches for after a file is tampered with or misconfigured. Here it leaves the suspect file live and consumes the known-good copy, so there is no second attempt.
Remediation automation cannot detect it. Exit 0 satisfies || rollback, set -e, and configuration-management error handling alike, so a script records a restore that never happened and carries on — discarding a staging copy, or restarting a service to pick up a binary that was never put in place.
The pre-change artifact is destroyed. Backup files are often the only on-disk record of a file's prior contents, and the baseline an investigator would diff against after an incident.
The protection is absent, not bypassed. No spelling mismatch, special permissions, timing, or crafted input is needed — the plain form fails (CWE-693, a protection mechanism that is missing rather than defeated). install is used heavily in make install, packaging scripts, and configuration management, frequently as root, where --backup exists specifically to retain the previous version of a file being replaced.
My Acknowledgement Information
Hongkai Chen of SEFCOM Lab at Arizona State University
Summary
install --backup=simpleis supposed to refuse an operation when creating the destination's backup would overwrite the source file.installperforms no such check at all: it destroys the source file and exits 0 with no diagnostic. Unlike themvissue I reported earlier, no spelling mismatch is required — the plain forminstall --backup=simple a~ ais unprotected. GNU coreutils refuses every such case with a non-zero exit and an error.Per
SECURITY.mdthis is both a bypass of a documented safety guard and an unintended destructive action — a divergence from GNU where the check "fails open" instead of erroring.Details
src/uu/install/src/install.rsimportsuucore::backup_controland computes a backup path atinstall.rs:918viabackup_control::get_backup_path, but never performs any source-is-backup check before taking the backup:Because the check is absent rather than incorrect, every spelling of the destination fails —
a,./a,$PWD/aalike.Suggested fix.
cpalready does this correctly —src/uu/cp/src/cp.rs:2121:installalready computes the backup path viaget_backup_path; the missing piece is theuucore::fs::paths_refer_to_same_fileguard before the backup is taken.PoC
Verified on:
840c36d(2026-05-29) — the latest releasemaintipf66e155d80df3d82798324726e460675a02c5cd5(2026-07-27)Ubuntu 24.04.2 LTS, Linux 6.8.0 x86_64, ext4. Console output captured with
LANG=C.No special configuration. Default build, no environment variables, any filesystem — the defect is filesystem- and platform-independent. The only requirement is
--backup=simple(or-bwith the default suffix) and a source named<destination><suffix>.payloadno longer exists anywhere.Unlike
mv, no spelling mismatch is needed — the same happens with./aas the destination:Both GNU coreutils 9.7 and 9.11 refuse both forms:
Impact
For
install --backup=simple a~ a:ais renamed toa~— this overwrites the source.a~, which no longer holds the original data.The destination ends up unchanged, the source is gone, and the exit status is 0. The operation reports success while doing the opposite of what was requested.
Because the exit code is 0, defensive idioms provide no protection:
Concrete case:
Rolling back to the retained previous version leaves the current binary in place and destroys the copy that was being rolled back to.
Security impact.
Recovery fails at the moment it is exercised. Restoring from a backup is what an operator reaches for after a file is tampered with or misconfigured. Here it leaves the suspect file live and consumes the known-good copy, so there is no second attempt.
Remediation automation cannot detect it. Exit 0 satisfies
|| rollback,set -e, and configuration-management error handling alike, so a script records a restore that never happened and carries on — discarding a staging copy, or restarting a service to pick up a binary that was never put in place.The pre-change artifact is destroyed. Backup files are often the only on-disk record of a file's prior contents, and the baseline an investigator would diff against after an incident.
The protection is absent, not bypassed. No spelling mismatch, special permissions, timing, or crafted input is needed — the plain form fails (CWE-693, a protection mechanism that is missing rather than defeated).
installis used heavily inmake install, packaging scripts, and configuration management, frequently as root, where--backupexists specifically to retain the previous version of a file being replaced.My Acknowledgement Information
Hongkai Chen of SEFCOM Lab at Arizona State University