Race Windows VM mount sources concurrently

This commit is contained in:
Erik Melton
2026-08-29 15:15:51 +01:00
committed by Afonso Oliveira
parent bf10b75150
commit 11fa6b9809
+59
View File
@@ -213,6 +213,65 @@ mv -T -- "$raced_shared" "$HOME/Windows"
unset -f dc
pass "a post-validation path swap cannot redirect Docker away from the pinned shared inode"
# Run the same attack as a genuinely concurrent process. A successful bring-up
# deliberately waits inside the Docker boundary until the attacker has replaced
# the familiar path with /, then verifies that the real bind anchor still names
# the caller-owned directory that was pinned before the race.
reset_case
prepare_user_mount_sources
touch "$HOME/Windows/safe-marker"
write 4G 2 64G concurrent pw UTC
resolve_caller
concurrent_shared_id=$(stat -Lc '%d:%i' "$HOME/Windows")
host_root_id=$(stat -Lc '%d:%i' /)
race_source="$HOME/Windows.race-source"
race_stop="$TMPDIR/stop-concurrent-race"
race_swaps="$TMPDIR/concurrent-race-swaps"
(
set +e
while [[ ! -e $race_stop ]]; do
if [[ -d $HOME/Windows && ! -L $HOME/Windows ]] && mv -T -- "$HOME/Windows" "$race_source" 2>/dev/null; then
ln -s / "$HOME/Windows" 2>/dev/null || true
printf x >>"$race_swaps"
sleep 0.002
fi
if [[ -L $HOME/Windows ]]; then
rm -f -- "$HOME/Windows"
mv -T -- "$race_source" "$HOME/Windows" 2>/dev/null || true
sleep 0.005
fi
done
) &
racer_pid=$!
concurrent_dc_calls=0
dc() {
local attempt
[[ $1 == up && ${2:-} == -d ]] || return 1
for ((attempt = 0; attempt < 20000; attempt++)); do
if [[ -L $HOME/Windows && $(readlink "$HOME/Windows" 2>/dev/null) == / ]]; then
break
fi
done
[[ -L $HOME/Windows && $(readlink "$HOME/Windows" 2>/dev/null) == / ]] || return 1
((concurrent_dc_calls++))
[[ $(get_mount_source /shared) == "$EXPECTED_SHARED" ]] || return 1
[[ $(stat -Lc '%d:%i' "$EXPECTED_SHARED") == "$concurrent_shared_id" ]] || return 1
[[ $(stat -Lc '%d:%i' "$EXPECTED_SHARED") != "$host_root_id" ]] || return 1
[[ -f $EXPECTED_SHARED/safe-marker ]]
}
for ((attempt = 0; attempt < 200; attempt++)); do
if __priv_up 2>/dev/null; then break; fi
done
touch "$race_stop"
wait "$racer_pid"
unset -f dc
if [[ -L $HOME/Windows ]]; then rm -f -- "$HOME/Windows"; fi
if [[ ! -e $HOME/Windows && -d $race_source ]]; then mv -T -- "$race_source" "$HOME/Windows"; fi
[[ -s $race_swaps ]] || fail "concurrent attacker never swapped the shared path"
((concurrent_dc_calls > 0)) || fail "concurrent race never reached Docker while the familiar path named host root"
[[ $(stat -Lc '%d:%i' "$EXPECTED_SHARED") == "$concurrent_shared_id" ]] || fail "concurrent race changed the protected shared inode"
pass "a concurrent home-path swap cannot redirect Docker away from the pinned shared inode"
# Same-inode sources fail before mounting and close both descriptors.
reset_case
same="$TMPDIR/same-source"