# DF-5613 — Unsynchronized FireWire iso-xferq claim + teardown → fwdma multiseg UAF write / bulkxfer heap overflow

**File under audit:** `sys/bus/firewire/fwdma.c` (pass 2) — finding rooted in the
unsynchronized lifecycle of the buffers this file allocates/frees, driven by
`sys/bus/firewire/fwdev.c` (claim + teardown) and the `fwdma.h` addressing
inlines used by `fw_read`/`fw_write`.

## What the bug is

1. **Claim race (root cause):** `FW_STSTREAM`/`FW_SRSTREAM`
   (fwdev.c:447-467 / 475-496) scan `fc->it[]`/`fc->ir[]` for a queue with
   `FWXFERQ_OPEN` clear, then call the *sleeping* `fwdev_allocbuf()`
   (kmalloc + `fwdma_malloc_multiseg()` — milliseconds for real buffers),
   and only set `FWXFERQ_OPEN` **after** the allocation returns
   (fwdev.c:461/489). `fwdev_allocbuf`'s own `FWXFERQ_EXTBUF` re-check
   (fwdev.c:100-101) is equally check-then-act. Two opens of different
   `/dev/fw0.x` minors that race these ioctls **both claim the same
   `struct fw_xferq`**.

2. **Heap overflow:** both racers run `fwdev_allocbuf()` on the shared
   queue. `q->bulkxfer` (fwdev.c:103) and `q->buf` (fwdev.c:108) are
   overwritten without freeing the loser's allocations, and the winner's
   chunk loop (fwdev.c:126-130) writes `b->nchunk` × 32-byte
   `fw_bulkxfer` entries through whichever `q->bulkxfer` is current —
   i.e. past the **other** caller's smaller kmalloc. Values are
   attacker-chosen: `poffset = i * npacket` (both from the unvalidated
   `FW_SSTBUF` bufspec), `mbuf = NULL`, plus STAILQ link pointers.

3. **Teardown vs in-flight xfer (the fwdma lifetime hole):** once two
   `fw_drv1` own one queue, one fd's `fw_close()` → `fwdev_freebuf()`
   (fwdev.c:139-153) calls **`fwdma_free_multiseg()`** (fwdma.c:207-218,
   freeing every segment page, destroying the tag, kfree'ing `am`) and
   `kfree(q->bulkxfer)` while the other fd is inside `fw_write()`
   (fwdev.c:366-405) / `fw_read()` (fwdev.c:280-345) walking those very
   buffers via `fwdma_v_addr()` (fwdma.h:59-66). Nothing synchronizes
   the two paths — no lock, no flag the I/O loops re-check, and
   `fwdev_freebuf` doesn't even clear `q->stproc` or the chunk lists.
   The in-flight `uiomove()`s then write/read **freed kernel memory**:
   attacker-controlled iso payload in (fwdev.c:390-392), kernel data out
   (fwdev.c:329-330).

Preconditions: open of `/dev/fwU.S` (0660 root:operator — operator-group
member or root) on a machine with a FireWire OHCI controller; both bugs are
pure software races after that.

## How to build / run (no FireWire hardware needed — KLD harness)

The harness compiles the **unmodified production** `firewire.c + fwdma.c +
fwdev.c` into `df5613.ko` (globals -D-renamed, DF-5413 pattern) and drives
their real `fw_ioctl`/`fw_close`/`fwdma_v_addr`/`uiomove` paths with the
synthetic `fc`/cdev state that fwohci+devfs would create.

```sh
# guest, root (fresh `vm.sh reset with-src` build):
# NOTE: boot with 2GB RAM:  QEMU_EXTRA="-m 2048" dfbsd-qemu/vm.sh up
cd /root/df5613 && sh build.sh     # -> df5613.ko   (build.log)
sh run.sh                          # tests 2 and 1  (run.log)
```

### Expected (baseline, reproduced):
* test 2 — `kern.df5613_status`:
  `DF-5613 REPRODUCED: fd2's in-flight fw_write uiomove()'d attacker iso
  payload (2048 bytes) into the fwdma segment page freed by fd1's
  fw_close and reclaimed by the spray (block 15, offset 11) -- UAF write
  with controlled content`
* test 1 — `DF-5613 REPRODUCED: two fds claimed one xferq ...; fd1's
  chunk loop overwrote 62 x 32 bytes past fd2's 2-entry bulkxfer kmalloc
  (poffset=i*npacket controlled)`
* On a >=4GB-RAM guest the same race taken mid-flight faults inside
  production `fw_write` instead: `Fatal trap 12 ... supervisor write ...
  memmove+0xbc` writing the stale freed segment pointer (panic.txt).

### Expected (with fix.diff applied to /usr/src, module rebuilt):
* test 1 — `shared=no overflow=0`: fd2 now claims a *different* queue
  (`OPEN=1` visible while fd1 is still inside `fwdma_malloc_multiseg`),
  no bulkxfer overwrite (fix_run.log).

## Files
df5613.c (harness), Makefile + shim/ (production includes), build.sh,
run.sh, build.log, run.log, panic.txt (4GB fatal-write run), fix.diff,
fix_build.log, fix_run.log, env.txt, VERDICT.md, manifest.json,
verdict.json
