DF-5520 / verdict.json
{ "finding_id": "DF-5520", "status": "reproduced", "reproduced": true, "impact": "panic", "confidence": "certain", "verdict": "isa_dmadone() (isa_dma.c:351-357) bcopy()s the caller-supplied nbytes out of the channels bounce buffer without any clamp against dma_bouncebufsize[chan] -- the mirror of the strict start-side check at isa_dma.c:241-244. KLD proof on the stock kernel with a 64-byte bounce on channel 1: nbytes=0x800 copied 0x800 bytes from beyond the chunk into the modules buffer (silent in-page OOB read; guest alive); nbytes=0x11000 and 0x20000 walked past the mapped low-arena window -> Fatal trap 12 supervisor READ in memmove called from isa_dmadone at 0xffffffff82604000, deterministic across independent boots (2/2). isa_dma_release() also deliberately leaves the stale dma_bounced bit set (XXX comment isa_dma.c:170-178), which arms the same unclamped copy for a later transfers dmadone after a release/re-acquire without dmadone. In-tree callers (fd.c, ppc.c) pass paired sizes today, so this is a latent API-contract defect: any driver size mismatch or lifecycle slip becomes a kernel panic or an info leak of low-memory DMA-arena contents into a driver buffer. Low severity, certain confidence.", "exploit_chain": "isa_dma_init(1,64) -> isa_dmastart(READ,64,1) arms dma_bounced -> isa_dmadone(READ,out,N,1) with N>>64 performs the unclamped bcopy: within the mapped page it silently discloses neighboring low-memory DMA-arena contents into the driver buffer; across the mapped window edge it faults the kernel (captured). Gated on a driver contract slip (size mismatch or missing dmadone before release); no unprivileged in-tree trigger.", "evidence": "run.log (0x800 silent run with leak dump + primed 0xE7 residue); panic.txt (0x11000: Fatal trap 12, supervisor read, VA 0xffffffff82604000, Stopped at memmove+0x10a from isa_dmadone); run.2.log (0x20000: identical fault address, second boot); leak_sample.txt; fix_run.log (isa_dmadone: count 69632 exceeds bounce buffer size 64, clamping -- no panic, guest alive).", "kernel_refs": [ "sys/bus/isa/x86_64/isa_dma.c:170-178", "sys/bus/isa/x86_64/isa_dma.c:241-244", "sys/bus/isa/x86_64/isa_dma.c:333-359", "sys/dev/disk/fd/fd.c:1844-1864", "sys/dev/misc/ppc/ppc.c:1594-1608", "sys/dev/misc/ppc/ppc.c:1697-1727" ], "poc_changes": "Seeded concept only; wrote the harness, discovered the bounce chunk sits at the mapped low-arena edge (0x11000/0x20000 both fault at 0xffffffff82604000) so the fatal size doubles as determinism evidence, added an in-page 0x800 silent variant with a freed-chunk residue primer, added sys/bus.h include + generated isa_if/bus_if/device_if headers.", "attempts": 4, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "runtime_sec": 3300, "guest_dirty": 0, "build_cmd": "cd /root/poc && make -f Makefile.20 && make -f Makefile.20f", "run_cmd": "kldload /root/poc/df5520.ko; kldunload df5520; kldload /root/poc/df5520f.ko", "code_hash": "df5520.c sha256=70e61b2a31e57a7fa9c7ef949846ef0f6cae459a5e2672a8d77dcf9a68a5c72d", "notes": "Impact recorded as panic (deterministic demonstration); the same primitive is an info leak whenever the overread stays mapped (contents on this guest were fresh-page zeros; any prior occupant of that low-memory page -- other drivers DMA buffers, bus_dma low-address pools -- would be returned). One harness artifact during fix validation: re-initializing the same channel in one boot trips the fixs intentional FreeBSD-style double-init panic (isa_dma_init: impossible request) -- documented in fix_run.log Boot A.", "recommended_fix": "Clamp isa_dmadones bounce readback to dma_bouncebufsize[chan] (warn on clamp) and clear the stale dma_bounced bit in isa_dma_release().", "fix_status": "fixed", "fix_baseline_reproduced": true, "fix_patched_reproduced": false, "fix_verdict": "Patched nativekernel (fix.diff applied in guest /usr/src, stacked with DF-5519s validation in the same file): the 0x800 run printed isa_dmadone: count 2048 exceeds bounce buffer size 64, clamping with nothing copied past the bounce buffer, and the previously-fatal 0x11000 run printed the clamp (69632 -> 64) with NO panic and the guest stayed up.", "fix_evidence": "findings/poc/DF-5520/fix_run.log (boot A + boot B sections); kernel #1 Tue Oct 6 20:57:57 UTC 2026" } |