# DF-5413 — `FW_SSTBUF` → 32-bit `nchunk*npacket` wrap → 0-segment DMA array → `fwdma_v_addr()` OOB → controlled kernel write via `fw_write`

**File:** `sys/bus/firewire/fwdev.c` · **Severity:** High · **Bucket:** memcorrupt · **Confidence:** certain
**Status: reproduced on the QEMU guest** (KLD harness with the *production*
`firewire.c`+`fwdma.c`+`fwdev.c` compiled in; DF-5302/DF-5321 evidence class —
no FireWire controller exists in QEMU, so the `/dev/fwN.N` entry point is
hardware-gated, but the vulnerable code itself was executed end-to-end).

## Root cause (production path:line)

1. `FW_SSTBUF` (`fwdev.c:504-506`) copies the user's `struct fw_isobufreq`
   (`nchunk`, `npacket`, `psize` — all `unsigned int`, no validation at all)
   into `d->bufreq`.
2. `FW_STSTREAM`/`FW_SRSTREAM` call `fwdev_allocbuf()` (`fwdev.c:458/486`)
   which passes `b->nchunk * b->npacket` to `fwdma_malloc_multiseg()`
   (`fwdev.c:108-109`). The multiply is **32-bit**: `0x10000 * 0x10000`
   wraps to **0**, so `fwdma_malloc_multiseg()` (`fwdma.c:160`, `nseg =
   howmany(0, …) = 0`) returns a **0-segment** `struct fwdma_alloc_multi`
   — a bare 32-byte `kmalloc` (`fwdma.c:162-163`) with **no `seg[]` at all**.
3. `q->bnchunk`/`q->bnpacket` are nevertheless set to the attacker's huge
   values (`fwdev.c:116-117`) and the bulkxfer/stfree machinery is built.
4. `fw_write()` (`fwdev.c:388-392`) computes the packet address with
   `fwdma_v_addr(it->buf, stproc->poffset + queued)` — `fwdma.h:60-66`
   indexes `am->seg[0]` **16 bytes past the 32-byte allocation** and uses
   the out-of-bounds `seg[0].v_addr` as the destination pointer for two
   `uiomove()`s of fully user-controlled bytes (4-byte stream header +
   `stream.len` payload).
5. `fw_read()` (`fwdev.c:321-330`) is the read-side twin: the same OOB
   `seg[]` lookup becomes an arbitrary-read/leak primitive once the DMA
   engine fills buffers.

## Demonstration (guest, module `df5413.ko`, test 1)

* production `fw_ioctl(FW_SSTBUF {nchunk=0x10000, npacket=0x10000, psize=2048})`
* production `fw_ioctl(FW_STSTREAM)` — allocates the 0-segment buffer
* the 32-byte heap slot following `am` is groomed to contain the address of a
  module-global marker (`kmalloc` adjacency pair trick, `df5413.c` bank code)
* production `fw_write()` — result:

```
df5413:   &am->seg[0]=0xfffff8004f1026e0 (allocation is 32 bytes) -- seg[0] is OUT OF BOUNDS
df5413:   OOB-read seg[0].v_addr = 0xffffffff82606180 (want 0xffffffff82606180 == GROOMED)
df5413: fw_write err=0 resid=0 marker after = 343546440020abcd 52544e4f432d3331 454b2d44454c4c4f
df5413:   payload="13-CONTROLLED-KERNEL-WRITE!!..."
```

i.e. **a chosen kernel address received chosen attacker bytes through
100% production code**: header `0x0020ABCD` at `V+0`, then the 32-byte
payload at `V+4` (`fp->mode.stream.payload`), exactly as `fw_write` would
for a real transmit. On the standard snapshot (no SMAP/SMEP/KASLR) this is
a direct path to `uid=0` for anyone who can open `/dev/fwN.N` (mode 0660,
group operator): overwrite e.g. `curthread->td_proc->p_ucred` fields or any
function pointer reached from the same write.

## What to run

```
./build.sh          # compiles production firewire/fwdma/fwdev into df5413.ko
sh run.sh           # kern.df5413_test=1 -> marker before/after proof
```

## Fix

`fix.diff` (shared with DF-5414/5415/5416 packs) validates the bufreq in
`fwdev_allocbuf()`. Validated: with `fix.diff` applied to the guest
`/usr/src` and the module rebuilt, the same trigger returns `EINVAL`
(`STSTREAM err=22`) and no write occurs (`fix_run.log`).
