# DF-1083 — Verdict

**REPRODUCED** (code-level off-by-one confirmed via verbatim-source harness;
live kernel trigger requires FireWire hardware absent from the QEMU guest).

**Severity:** High (kernel stack OOB write from an external device's data).

---

## The bug

`crom_next()` in `sys/bus/firewire/fwcrom.c:105` walks an IEEE-1212
Configuration ROM.  When it encounters a Directory-type entry (`CSRTYPE_D`)
it descends one level:

```c
if ((reg->key & CSRTYPE_MASK) == CSRTYPE_D) {
    if (cc->depth >= CROM_MAX_DEPTH) {   /* line 115 — BUG */
        kprintf("crom_next: too deep\n");
        goto again;
    }
    cc->depth ++;                        /* line 119 */
    ptr = &cc->stack[cc->depth];         /* line 121 — OOB */
    ptr->dir = (struct csrdirectory *)(reg + reg->val);  /* line 122 */
    ptr->index = 0;                      /* line 123 */
    goto check;
}
```

`CROM_MAX_DEPTH` is **10** (`iec13213.h:200`) and `stack` is declared
`struct crom_ptr stack[CROM_MAX_DEPTH]` (`iec13213.h:208`) — valid indices
**0..9**.  At `cc->depth == 9` the guard `9 >= 10` evaluates **false**, so
`depth` is incremented to **10** and `&cc->stack[10]` is written.  That
slot is past the end of the struct, i.e. **past the end of the kernel
stack frame** that contains `cc`.

The write is a full `struct crom_ptr` — **16 bytes** (an 8-byte pointer
+ 4-byte int + 4 padding):
- `ptr->dir` = `(struct csrdirectory *)(reg + reg->val)` — a pointer the
  attacker controls via the `val` field (24-bit offset into the ROM);
- `ptr->index` = `0` (fixed).

## In-kernel reachability & impact ceiling

The vulnerable code is **statically linked into the default GENERIC
kernel** (`sys/config/X86_64_GENERIC`: `device firewire`, `device sbp`;
`nm /boot/kernel/kernel` shows `crom_next` at `0xffffffff804bdc90`).

All three callers use `struct crom_context cc` as a **local (stack)
variable**:
- `sbp_alloc_lun()`   — `sys/dev/disk/sbp/sbp.c:405`
- `sbp_alloc_target()`— `sys/dev/disk/sbp/sbp.c:549`
- `sbp_alloc_dev()`   — `sys/dev/disk/sbp/sbp.c:595` (via `crom_has_specver`,
  which also has a stack-local `struct crom_context`)

These run during SBP-2 (SCSI-over-FireWire) target enumeration.  The
`cc` struct (168 bytes) sits on the kernel stack; `&cc.stack[10]` at
offset 168 writes 16 bytes into the adjacent stack region — typically
saved registers / saved frame pointer / **return address**.

**Realistic threat model:** a malicious external FireWire device presents
a Configuration ROM nested 10 directories deep.  When the host kernel
attaches it, `crom_next` overwrites the return address of the SBP attach
function on the kernel stack.  On a kernel without SMEP/SMAP (or with an
appropriate gadget) this is kernel code execution → local privilege
escalation or remote (physical FireWire access) RCE.

**Exploitation note:** This guest has no FireWire controller, so the
in-kernel path cannot be triggered live.  The primitive is proven at the
harness level (verbatim kernel code, crafted attacker ROM).  This is the
same situation as DF-0594/0616/0281 (latent bug reachable only with
absent hardware).  The OOB write is **16 bytes, partially attacker-
controlled** (the `dir` pointer is set to an attacker-chosen offset into
the ROM; `index` is fixed at 0).  Converting this to `uid=0` requires a
FireWire controller — not available here — so no live escalation is
demonstrated; the bug is confirmed as a real stack-corruption primitive.

## Proof (harness)

`harness.c` compiles the **verbatim** `crom_init_context`/`crom_get`/
`crom_next` (fwcrom.c:62-143) and the exact structures (iec13213.h),
then feeds a crafted 10-deep-nested Configuration ROM (25 words).
A `struct crom_ptr overflow_slot` is placed immediately after
`cc.stack[9]` — exactly where the buggy code writes `&cc.stack[10]` —
filled with sentinels and checked after the walk.

**Before fix (unpatched logic):**
```
overflow_slot (== &stack[10]) AFTER walk:
  .dir   = 0x00007fffffdfd814  (sentinel was 0xDEADBEEFDEADBEEF)
  .index = 0x00000000     (sentinel was 0x12345678)
>>> BUG CONFIRMED: crom_next wrote &stack[10] OUT OF BOUNDS.
```
Deterministic across 3 runs.

**After fix (`>= CROM_MAX_DEPTH - 1`):**
```
crom_next: too deep
overflow_slot (== &stack[10]) AFTER walk:
  .dir   = 0xdeadbeefdeadbeef  (sentinel was 0xDEADBEEFDEADBEEF)
  .index = 0x12345678     (sentinel was 0x12345678)
>>> canary intact: no OOB write (guard fired correctly).
```

## Fix

`fix.diff` — one-line change at `fwcrom.c:115`:
```diff
-		if (cc->depth >= CROM_MAX_DEPTH) {
+		if (cc->depth >= CROM_MAX_DEPTH - 1) {
```
At depth 9 the test `9 >= 9` is true → the "too deep" guard fires → no
descent, no write to `stack[10]`.  The fix is minimal and targeted at the
root cause (off-by-one in the boundary check).

## Fix validation (Phase 8)

- **Baseline (#0, unpatched):** harness shows BUG CONFIRMED (canary
  corrupted, exit 1).
- **Fix applied** to `/usr/src/sys/bus/firewire/fwcrom.c`, kernel built
  (`make -j6 nativekernel KERNCONF=X86_64_GENERIC`, rc=0), installed,
  booted (`6.5-DEVELOPMENT #1`, healthy, ssh responsive).
- **Fixed harness logic:** canary intact, "too deep" guard fires, exit 0.
- **fix_status: fixed** — the one-line change closes the OOB write and
  the patched kernel compiles and boots cleanly.

Because the live kernel path needs FireWire hardware (absent in QEMU),
fix_status is `fixed` on the basis of (a) the harness before/after and
(b) the patched kernel building + booting + containing the corrected
line — consistent with the `not_testable`-augmented standard for
hardware-gated latent bugs.
