# DF-2295 VERDICT — REPRODUCED (kernel heap over-read to userspace, leak)

## Chain (line-accurate, stock fwdev.o + fwdma.o)
```c
fw_read (fwdev.c:260):
321:	fp = (struct fw_pkt *)fwdma_v_addr(ir->buf,
322:			ir->stproc->poffset + ir->queued);   /* slot base */
325:	if(fp->mode.stream.len == 0){ err = EIO; ... }       /* len from
                                                             RECEIVED
                                                             packet hdr */
329:	err = uiomove((caddr_t)fp,
330:		fp->mode.stream.len + sizeof(u_int32_t), uio);  /* UNBOUNDED
                                                             vs psize */
```
`fp->mode.stream.len` is the 16-bit length field of the stream packet
header that the FireWire peer transmits (OHCI DMA writes header+payload
into the psize-sized slot). No check against `ir->psize` exists
anywhere on the path. `uiomove` copies `len+4` bytes from the slot to
userspace.

## Observed (baseline, stock binaries)
Ring: 4 chunks x 8 packets x 512-byte slots (32 slots), allocated by
the real `fwdev_allocbuf` → `fwdma_malloc_multiseg`. All 32 slots
pre-filled (by the harness emulating the peer) with 16-byte canary
records: {0xF00DF00D, tag=0x50000000|slot, byte_offset, 0xDEADC0DE^tag}.
Packet header injected at slot 0: `05dc01a0` (len=1500, tcode=0xa).

```
DF-2295: read() returned 1504 (errno=0)
DF-2295: returned packet header = 05dc01a0 (len=1500 tcode=a sy/chtag=a0)
DF-2295: copied 1504 bytes; slot capacity 512 -> 992 bytes beyond the packet slot
DF-2295: user[512..] == DMA ring slot 1 (tag=0x50000001) offset 0 -- beyond slot 0
DF-2295: user[528..] == DMA ring slot 1 (tag=0x50000001) offset 16 -- beyond slot 0
...
```
Host-side verification of leak_sample.txt (this pack): all 62 sixteen-
byte windows in [512,1504) decode to the exact expected
(slot, offset): window at user offset X carries the record of ring
offset X-512, i.e. slot (X-512)/512+1 at offset (X-512)%512.
**Every one of the 992 beyond-slot bytes is attributed to known
adjacent ring-slot memory — a complete, byte-exact kernel memory
disclosure to userspace.** (The in-guest PoC summary line
"attributed 248/4 bytes" under-counts by reporting matched 4-byte
words; the host verifier and leak_sample.txt are authoritative.)

Note: read() returns SUCCESS (rc=1504) — the over-read is not even
flagged as an error.

## Threat model / reachability (honest)
* Preconditions: operator-group user with an open /dev/fwU.S (0660
  root:operator, stock clone perms) that has set up an iso receive
  stream, AND a FireWire peer transmitting a crafted packet with
  stream.len > psize-4. The peer is the source of the length field;
  the local user is the recipient of the over-read bytes.
* Disclosure range: with psize=512 and len up to 65535, a single
  uiomove can read up to 65539 bytes starting at a 512-byte slot —
  i.e. beyond the entire fwdma multiseg allocation into adjacent
  kernel virtual memory. The 992-byte attributed over-read (adjacent
  slots 1-3) is proven; the beyond-ring variant uses the identical
  primitive and was not separately run (the guest kernel had already
  been rebuilt with the fix for A/B validation).
* Privesc assessment: READ-ONLY disclosure (adjacent DMA ring content
  and, for extreme len, adjacent kernel KVA). No write primitive, no
  direct escalation. On FireWire-equipped hosts this is a kernel
  info-leak that can assist exploit chains; requires peer cooperation.
  Closed as: leak, local impact contingent on a malicious peer.

## Fix validation (A/B, fix.diff clamp hunk)
Fixed `fw_read`:
```c
	ulen = fp->mode.stream.len + sizeof(u_int32_t);
	if (ulen > ir->psize)
		ulen = ir->psize;
	err = uiomove((caddr_t)fp, ulen, uio);
```
* Malicious packet (len=1500, psize=512): every copy is clamped to the
  slot; read returns per-slot chunks (512 bytes each) — no single copy
  ever crosses a slot boundary (run_2295_fixed_malicious.log: the
  beyond-slot bytes now come from *separate, clamped* per-slot copies
  of subsequent slots — bounded by construction).
* Legit packet (len=508 = psize-4): 512 bytes returned, hexdump
  **bit-identical** to the baseline kernel output
  (leak_legit_508.txt vs leak_fixed_508.txt — `diff` empty,
  BIT_IDENTICAL_508).

## Artifacts
leak_sample.txt, leak_attributed.txt (host verifier output),
run_2295_baseline.log, run_2295_fixed_*.log, leak_*_508.txt,
poc_2295.c, fwhctl.c, fwharness.c (rig), env.txt.

## Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest `make -j6 nativekernel KERNCONF=X86_64_GENERIC` completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
