Race Windows VM mount sources concurrently
This commit is contained in:
committed by
Afonso Oliveira
parent
bf10b75150
commit
11fa6b9809
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user