# DF-1058 — fw_write trusts user-supplied stream packet header len — VERDICT

## Verdict: INCONCLUSIVE (runtime) / CONFIRMED (source-level)

The bug is **confirmed real** by line-by-line source tracing. Runtime
reproduction is blocked by missing FireWire hardware (no `/dev/fw0`).

## Mechanism (source-level trace)

1. **Code location:** `fw_write()` in `sys/bus/firewire/fwdev.c:388-405`.

2. **The bug** at `fwdev.c:390-392`:
   ```c
   err = uiomove((caddr_t)fp, sizeof(struct fw_isohdr), uio);
   /* NO validation of fp->mode.stream.len against it->psize */
   err = uiomove((caddr_t)fp->mode.stream.payload,
           fp->mode.stream.len, uio);
   ```
   - Line 390: First `uiomove` copies 4 bytes (`sizeof(struct fw_isohdr)` =
     `sizeof(u_int32_t[1])` = 4) from user data into `fp`, setting
     `fp->mode.stream.len` — a **16-bit field fully attacker-controlled**
     (firewire.h:141, `COMMON_HDR(len, chtag, tcode, sy)` → `len:16`).
   - Line 391-392: Second `uiomove` copies `fp->mode.stream.len` bytes
     into `fp->mode.stream.payload` (= `(char*)fp + 4`, right after the
     4-byte header).
   - **No check** that `len <= psize - sizeof(struct fw_isohdr)`.

3. **Slot allocation:** Per-packet slots are allocated for exactly `psize`
   bytes via `fwdma_malloc_multiseg` (fwdev.c:108-109). `psize` comes from
   `FW_SSTBUF` ioctl (fwdev.c:505) with no upper bound.

4. **Overflow:** If the user sets `len` > `psize - 4` (up to 65535), the
   second `uiomove` writes past the per-packet slot into adjacent slots
   and ultimately past `it->buf` into adjacent kernel heap. With `psize=8`
   and `len=0xffff`, the overflow is ~65531 bytes per packet.

5. **Loop amplification:** The loop at fwdev.c:401:
   `if (uio->uio_resid >= sizeof(struct fw_isohdr)) goto isoloop;`
   keeps processing packets as long as the user supplies more 4-byte headers.
   Each overflowing packet corrupts successive slots.

6. **DMA leak:** The OHCI TX path (`fwohci.c:2500` `add_tx_buf`) DMAs the
   corrupted slot onto the FireWire bus → kernel-memory-to-bus disclosure
   if a peer node receives the packets.

## Exploit chain

**Local privilege escalation possible** — but requires `/dev/fw0` access
AND a FireWire controller. The heap overflow (up to ~64KB per packet) into
adjacent kernel heap can corrupt slab metadata or victim objects. With
no SMAP/SMEP/KASLR, grooming + function pointer corruption → root shell.

**Blocked** by the missing FireWire controller: `/dev/fw0` does not exist.
This is a valid hard blocker (Phase 6): the code path is unreachable
without FireWire hardware.

## Why runtime reproduction is blocked

- No FireWire controller on this QEMU guest.
- `/dev/fw0` does not exist — `open()` returns ENOENT.
- The PoC detects this and reports the source-level analysis.

## PoC changes

Created `df-fw-write-overflow.c` (from finding markdown) with graceful
fallback for missing `/dev/fw0`. Built and verified: compiles with
`-I/usr/src/sys`, runs, correctly reports no FW device and confirms the bug.

## Fix validation

The fix (`fix.diff`) clamps `fp->mode.stream.len` to `it->psize - sizeof(struct fw_isohdr)`:
```c
if (fp->mode.stream.len > it->psize - sizeof(struct fw_isohdr))
    fp->mode.stream.len = it->psize - sizeof(struct fw_isohdr);
```
Applied + compiled in combined-fix kernel (`#1`, rc=0, boots cleanly).
Runtime before/after not possible (no FireWire HW).

**fix_status: not_testable** (diff applies + compiles; runtime blocked by
missing HW — `/dev/fw0` does not exist).

## Recommended fix

Clamp `fp->mode.stream.len` to the slot's usable size after the first
`uiomove` sets it from user data. Matches the finding proposal. Also
recommend validating `psize >= sizeof(struct fw_isohdr)` in `FW_SSTBUF`
and adding upper bounds for `psize`/`nchunk`/`npacket`.
