# DF-0117 — PoC (UAF on kdmsg state in diskiodone)

Finding: `findings/DF-0117-diskiocom-uaf-kdmsg-state.md`
Claim: in `disk_blk_read/write/flush/freeblks` the kdmsg state pointer is
stored in `bio->bio_caller_info1.ptr` and dispatched to `dev_dstrategy`
async with **no** `kdmsg_state_hold` (commented out at
`sys/kern/subr_diskiocom.c:375/451/503/554`, drop at `:661`).  If the peer
sends DELETE or the connection drops while I/O is in flight, the kdmsg core
frees the state and `diskiodone` (`:577-662`) dereferences freed memory →
UAF.

**Verdict: NOT REPRODUCED** on master DEV.  See `VERDICT.md` for the full
line-by-line trace of why: the state's two topology references (rbtree +
parent `subq`) are only dropped inside `kdmsg_state_cleanuptx` *after* a
DELETE-bearing kernel reply is produced, and that reply is produced by
`diskiodone` itself — so the state cannot be freed until after every
`diskiodone` has finished using it.  The commented-out hold/drop is
redundant defensive code on this build, not the missing half of an active
UAF.  ~2800 `diskiodone` completions during aggressive teardown races (three
protocol modes, 256–1024 in-flight reads, immediate socket close) produced
**no panic, no fault, no KKASSERT**.

## Files

| file | purpose |
|------|---------|
| `trigger.c`   | self-contained trigger: DIOCRECLUSTER iocom attach + forged DMSG BLK_READ wire messages + connection-teardown race |
| `build.sh`    | `cc -O2 -o trigger trigger.c -lpthread` |
| `run.sh`      | kills hammer2 (frees the disk iocom), runs the trigger once for a given mode |
| `build.log`   | full untrimmed final build output |
| `run.log`     | decisive run (mode=1, 256 reads) — full untrimmed output |
| `run.mode2.log` / `run.hipress.log` | extra stress runs (mode 2; 1024-read high-pressure) |
| `env.txt`     | guest uname, cc version, post-run `blk_active` / thread state |
| `VERDICT.md`  | full narrative: mechanism trace, why-not, evidence |
| `manifest.json` | machine-readable catalog |

## Build & run (as root on the guest)

```
./build.sh
# each run consumes the disk iocom; reset the guest between modes
./run.sh <mode> <nreads> <preclose_us>
#   mode        0 = CREATE only (eof=0)
#               1 = CREATE|DELETE (eof=1)  -- exercises the DELETE-reply/free path
#               2 = CREATE then explicit DELETE msg on each state
#   nreads      default 256
#   preclose_us default 2000 (delay before socket close / teardown)
```

Expected on this kernel: trigger exits 0, guest stays up, `dmesg` clean,
`debug.blk_active` returns to 0, no panic markers in `dfbsd-qemu/boot.log`.
(A vulnerable kernel would panic in `diskiodone` / `kdmsg_state_free` /
page-fault on freed slab memory.)
