# DF-2301 — print_db off-by-one reads one descriptor past the DMA block

## What this is
Verification pack for finding DF-2301 (`sys/bus/firewire/fwohci.c:2347`).
Low / OOB read + console info-leak. Claim: `print_db()` loops
`for (i = 0; i <= max; i++)` with `max = dbch->ndesc` (dump_db,
fwohci.c:2311) over a descriptor block that has exactly `ndesc` entries,
so row `i == ndesc` reads `db[ndesc]` — one `struct fwohcidb` (16 bytes)
past the block; the ST2 case additionally reads `db[i+1]` (fwohci.c:2379)
and double-increments (fwohci.c:2400).

## Verdict (short)
REPRODUCED on the verbatim-interposition harness, with byte-exact
attribution, triggered by an **operator-group user** via DUMPDMA:

* ch=0 (atrq, ndesc=8, ndb=64, real fwohci_db_init allocation):
  `print_db` printed **9 rows** (i=0..8). Row i=8 read `db[8]` — the
  9th descriptor of an 8-descriptor block:
  - **in-allocation case (element 5):** row 8 printed EXACTLY the
    canary the rig planted in element 6 db[0]
    `{cafe0001,11111111,22222222,33333333}` (KVA 0xfffff80117c14300,
    confirmed by the rig KVA peek printing the same 4 words). Because
    the canary cmd word happens to carry key bits ST2, print_db then
    ALSO took the fwohci.c:2377 branch and printed `db[9]` — the
    compounding `db[i+1]` OOB read, live.
  - **past-the-allocation case (element 63 = last element of the last
    4096-byte fwdma segment):** row 8 read the 16 bytes at
    `seg1.v_addr + 4096` — OUTSIDE every fwdma segment — and printed
    them (`OUTM ST0 ... 00000000`), matching the rig KVA peek of the
    same address.

Rows go to the console/dmesg (kprintf, fwohci.c:2352), which is
world-readable via dmesg on default installs -> kernel/DMA memory
disclosure; on a differently-shaped allocation the same read can fault
(latent panic).

## Rig
Same fwohci.c TU harness as DF-2300; root prepares atrq via the REAL
fwohci_db_init (ndesc=8, ndb=64 -> esize=128, 2 fwdma segments of
4096B, 32 elements each), fabricates the descriptors of elements 5 and
63 (INPUT_MORE | KEY_ST3 | INTR_TRUE, no early-exit conditions so the
loop runs to completion) plus the canary in element 6, sets the ATQ
command-pointer register in the emulated MMIO, and then **opuser**
issues DUMPDMA(0): real fwohci_ioctl (fwohci.c:371) -> dump_dma ->
dump_db (fwohci.c:2305) -> print_db (fwohci.c:2322).

## Fix
fix.diff: loop bound `i < max` and the ST2 immediate print guarded by
`i + 1 < max`. A/B validated: patched kernel prints exactly ndesc rows,
no canary row, no row past the allocation.

## Reachability honesty
DUMPDMA requires only the operator group (same 0660 node); the leak is
console/dmesg-visible. The OOB is a READ only — Low severity as filed.
