# DF-1020 — fw_bus_explore_callback unconditional OOB csrrom write — 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 controller, no
`/dev/fw*` on this guest).

## Mechanism (source-level trace)

1. **Code location:** `fw_bus_explore_callback()` in
   `sys/bus/firewire/firewire.c:1494-1561` (the `else` branch for
   Config ROM exploration responses).

2. **Unconditional write before bounds check:**
   - Line 1500 (WRITE):
     `fc->ongodev->csrrom[(fc->ongoaddr - CSRROMOFF)/4] = ntohl(rfp->mode.rresq.data);`
   - Line 1558 (CHECK — too late):
     `if((fc->ongoaddr - CSRROMOFF) > CSRROMSIZE) goto nextnode;`

3. **ongoaddr wrapping:** At line 1513-1520:
   ```c
   csrreg = (struct csrreg *)&fc->ongodev->csrrom[(fc->ongoaddr - CSRROMOFF)/4];
   if (csrreg->key == 0x81 || csrreg->key == 0xd1) {
       csrd->ongoaddr = fc->ongoaddr;
       fc->ongoaddr += csrreg->val * 4;   // val is 24-bit device-controlled
   ```
   - `csrreg->val` (iec13213.h:129): `u_int32_t val:24` — device-controlled.
   - `fc->ongoaddr` (firewirereg.h:107-109): `u_int32_t ... ongoaddr:16` —
     a **16-bit bitfield** that truncates on overflow.
   - `val * 4` can be up to `0xFFFFFF * 4 = 0x3FFFFFC`, wrapping the 16-bit
     `ongoaddr` to any value.

4. **OOB write:** After wrapping, the next call to `fw_bus_explore` sends a
   new read request. The response hits line 1500, writing to
   `csrrom[(wrapped_ongoaddr - CSRROMOFF)/4]`. If `ongoaddr < CSRROMOFF`,
   the unsigned subtraction produces a massive index → write far past
   `csrrom[256]`.

5. **Target struct layout** (`struct fw_device`, firewirereg.h:44-62):
   ```
   csrrom[256] (1024 bytes) | rcnt (4) | *fc (8, KERNEL POINTER) |
   status (4) | STAILQ link (8) | adjacent heap
   ```
   Overwriting `*fc` (kernel pointer to `firewire_comm`) gives RIP control
   when the corrupted pointer is later dereferenced.

## Exploit chain

This is a **device-attacker** exploit: a malicious FireWire device on the
bus responds to exploration reads with crafted Config ROM. The OOB write
corrupts the `fw_device` struct (kernel pointer, linked list pointers,
status). Combined with FW_GCROM info leak (KASLR defeat), this yields
arbitrary kernel write → potential RIP control.

**Not a local user exploit** — requires physical/bus access to a FireWire
controller with a malicious device.

## Why runtime reproduction is blocked

- No FireWire controller on this QEMU guest.
- `/dev/fw*` nodes don't exist.
- The `firewire` driver is compiled into GENERIC but not active.
- The bus exploration callback only fires when a FireWire controller
  initiates node discovery — impossible without HW.

## PoC changes

Created `harness.c` — demonstrates ongoaddr 16-bit bitfield wrapping
arithmetic and the order-of-operations bug (write before bounds check).
Built and verified on the guest.

## Fix validation

The fix (`fix.diff`) moves the bounds check BEFORE the write:
```c
if ((fc->ongoaddr - CSRROMOFF) >= CSRROMSIZE)
    goto nextnode;
fc->ongodev->csrrom[...] = ntohl(rfp->mode.rresq.data);
```
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).

## Recommended fix

Move the bounds check `(ongoaddr - CSRROMOFF) >= CSRROMSIZE` to BEFORE
the write at line 1500 (currently the check is at line 1558, after the
write). This matches the finding proposal. Additionally, consider capping
`csrreg->val` at a sane maximum to prevent 16-bit wrapping of `ongoaddr`.
