# DF-4173 VERDICT — iir_shutdown ucmd use-after-free / double-queue / wrong free type

**Status: untested on this guest (hardware-gated: shutdown path of the iir driver,
no controller present). Bug existence: certain from code trace.**

## Reproduced?

No runtime reproduction: the guest has no iir controller (`gate_check.log`),
and the buggy path is the system-shutdown handler, driven by controller
completion latency. The gate is proven; the code trace below is line-accurate.

## Code trace

1. `iir_shutdown` (iir.c:1478-1520) allocates **one** `gdt_ucmd_t` with
   `kmalloc(..., M_GDTBUF, ...)` (iir.c:1493).
2. For each present host drive it fills that single ucmd, queues it
   (`TAILQ_INSERT_TAIL(&gdt->sc_ucmd_queue, ucmd, links)`, iir.c:1509),
   dispatches via `gdt_next()`, and waits at most 10 s
   (`tsleep(..., 10*hz)`, iir.c:1514).
3. `gdt_next()` *removes* the ucmd from the queue when dispatching it
   (iir.c:706) and the owning gccb keeps `gccb->gc_ucmd = ucmd`
   (iir.c:1006). Until the controller completes the command the ucmd is
   owned by that in-flight gccb.
4. **Defect A (double-queue):** if the 10 s sleep expires with
   `complete_flag == FALSE`, the next loop iteration overwrites the same
   ucmd's fields while the controller still owns it, and inserts it into
   `sc_ucmd_queue` again — one ucmd now referenced by an in-flight gccb
   *and* queued for a second gccb.
5. **Defect B (UAF):** after the loop, `kfree(ucmd, M_DEVBUF)` (iir.c:1518)
   frees the ucmd unconditionally — including while it is still in flight
   and/or still linked into `sc_ucmd_queue`. The later `GDT_GCF_IOCTL`
   completion path (iir.c:1599-1633) then does:
   - `ucmd->status = gdt->sc_status; ucmd->info = …; ucmd->complete_flag = TRUE;`
     (iir.c:1608-1610) — writes into freed memory;
   - `bcopy(gccb->gc_scratch, ucmd->data, cnt)` with `cnt` up to
     `GDT_SCRATCH_SZ` = 3072 (iir.c:1613-1626) — a 3 KB-class write into a
     freed ~3.1 KB heap chunk (heap corruption if reallocated);
   - `TAILQ_INSERT_HEAD(&gdt->sc_ucmd_queue, ucmd, links)` on the BSY path
     (iir.c:1604) — links freed memory into a live list;
   - `wakeup(ucmd)` (iir.c:1632).
6. **Defect C (type mismatch):** allocated with `M_GDTBUF`, freed with
   `M_DEVBUF` (iir.c:1493 vs 1518) — benign without `MALLOC_DEBUG`, but wrong.

## Impact ceiling

Shutdown-time UAF write / list corruption, requiring an iir controller that
stalls ≥10 s on a flush. Not software-triggerable. Severity Low; bucket:
memcorrupt.

## Fix validation

`fix.diff` (one ucmd per drive, free-only-when-complete, correct type) passes
`git apply --check`. No kernel-build/runtime validation possible without
hardware; fix_status = not_testable.

## Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest `make -j6 nativekernel KERNCONF=X86_64_GENERIC` completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
