# DF-5520 — isa_dmadone() copies `nbytes` out of the bounce buffer with no clamp → unbounded OOB kernel read (panic / info leak into driver buffers)

**File:** `sys/bus/isa/x86_64/isa_dma.c` (pass 2)
**Verdict:** reproduced (see `verdict.json`, `VERDICT.md`)

`isa_dmastart()` validates the transfer size against the channel's bounce
buffer before copying **into** it (isa_dma.c:241-244), but `isa_dmadone()`
(isa_dma.c:351-357) `bcopy`s the caller-supplied `nbytes` **out of** the
bounce buffer with no check at all. A driver that passes a done-size larger
than the started size — or that reaches dmadone with the stale `dma_bounced`
bit that `isa_dma_release()` deliberately does not clear (the XXX comment at
isa_dma.c:170-178) — gets an out-of-bounds read of the low-memory DMA arena
delivered into its buffer.

## Demonstrated on the stock kernel

- **Silent in-page read** (`df5520.ko`, nbytes=0x800 out of a 64-byte
  bounce): 0x800 bytes copied from beyond the chunk into the module's buffer
  (`run.log`, `leak_sample.txt`); guest survives. On this boot the
  neighboring page was fresh zeros — with any prior use of that low-memory
  page the same call returns those contents.
- **Fatal walk-off** (`DONE_NBYTES=0x11000` and `0x20000`): the same bcopy
  runs past the mapped low-arena window — deterministic `Fatal trap 12,
  supervisor read data` in `memmove` called from `isa_dmadone`, fault VA
  `0xffffffff82604000`, 2/2 boots (`panic.txt`, `run.2.log`).

In-tree callers (fd.c, ppc.c) currently pass matching sizes, so this is an
API-contract/hardening defect (any future or third-party driver mistake
turns it into a panic or leak), not a directly unprivileged-reachable bug.

## Fix

`fix.diff` clamps the readback to `dma_bouncebufsize[chan]` (with a console
warning) and clears the stale `dma_bounced` bit in `isa_dma_release()`.
Patched-kernel rerun: clamp message printed, no data beyond 64 bytes copied,
no panic (`fix_run.log`).
