Concurrent proxy startup can delete the active self-updater and remove rustup
Problem
Rustup stages every self-update at the shared path $CARGO_HOME/bin/rustup-init.
Normal rustup and proxy invocations such as rustc run updater cleanup on startup. If a proxy starts while self-update is replacing rustup, it can delete the running updater's path.
On Unix, replacement then:
- removes the installed
rustup; - tries to copy the updater from its now-deleted path;
- fails with
No such file or directory; - leaves
rustupmissing.
This seems to be related to #1864, #4648, and #4777.
Reproduction
The reproduction uses isolated test directories and does not affect the user's real rustup installation. I have included a branch with the accompanying changes to demonstrate the bug.
git clone https://github.com/cachebag/rustup.git
cd rustup
git switch demo/self-update-race-before
cargo test \
--test test_bonanza \
--no-default-features \
--features reqwest-rustls-tls,test \
suite::cli_self_upd::self_update_replacement_survives_proxy_cleanup \
-- --exact --nocaptureThe test deterministically pauses self-update immediately before replacement, starts a real rustc proxy invocation, then resumes replacement.
The vulnerable branch fails with:
error: could not read metadata for file: '.../bin/rustup-init':
No such file or directory
concurrent proxy removed the installed rustup during self-updateFor comparison, the same test passes on my branch (which is the PR linked to this issue):
git switch demo/self-update-race-after
cargo test \
--test test_bonanza \
--no-default-features \
--features reqwest-rustls-tls,test \
suite::cli_self_upd::self_update_replacement_survives_proxy_cleanup \
-- --exact --nocaptureExpected behavior
Concurrent rustup or proxy invocations must not delete another process's self-update artifact. A failed replacement must leave the previously installed rustup runnable.
Source: rust-lang/rustup