DF-5348 / verdict.json
{ "finding_id": "DF-5348", "status": "reproduced", "reproduced": true, "impact": "dos", "confidence": "certain", "verdict": "bus_dmamap_unload() is a silent no-op for a map queued at EINPROGRESS: _bus_dmamap_unload only frees active bounce pages and never dequeues the map from bounce_map_waitinglist nor resets pagesneeded/pagesreserved nor releases its partial bounce-page reservation. Reproduced on a clean-boot DragonFly 6.5 guest: after unloading the queued map, releasing any bounce page in the zone completed its reservation and busdma_swi executed a full deferred bus_dmamap_load on the UNLOADED map, invoking its callback with stale buf/buflen state (DEFERRED CALLBACK FIRED FOR UNLOADED MAP). For drivers that free/reuse their buffer after aborting (the normal pattern) this is a write-after-free via the subsequent sync bounce copies; the stuck reservations also drain the shared bounce zone (DoS). impact recorded as dos (the driver-independent harm); the driver-dependent write-after-free ceiling is documented in VERDICT.md.", "exploit_chain": "bouncing tag -> load returns EINPROGRESS (map queued holding partial reservation) -> driver aborts: bus_dmamap_unload (no-op) + buffer freed/reused -> any later bounce-page release in the zone -> get_map_waiting completes reservation + dequeues the unloaded map -> busdma_swi runs deferred load on stale buf -> driver callback fires for dead transaction / bounce copies touch the freed buffer", "evidence": "run.log.baseline: bus_dmamap_unload(mapY) returned -- map is STILL on the waiting list then *** DEFERRED CALLBACK FIRED FOR UNLOADED MAP *** and RESULT=PHANTOM CONFIRMED; patched kernel (dmesg in DF-5347/dmesg_fixrun.txt): RESULT=FAILED + cleanup done", "kernel_refs": [ "sys/sys/bus_dma.h:313", "sys/platform/pc64/x86_64/busdma_machdep.c:1022", "sys/platform/pc64/x86_64/busdma_machdep.c:645", "sys/platform/pc64/x86_64/busdma_machdep.c:1408", "sys/platform/pc64/x86_64/busdma_machdep.c:1445" ], "poc_changes": "authored fresh (no seed); POC_TEST=2 variant of the same KLD harness as DF-5347", "attempts": 3, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #0 (baseline) / #1 Oct 3 06:29 (fix validation) x86_64", "runtime_sec": 900, "guest_dirty": 0, "build_cmd": "make -m /usr/share/mk SYSDIR=/usr/src/sys MAKEOBJDIRPREFIX=/root/obj with COPTS+=-DPOC_TEST=2", "run_cmd": "kldload ./busdmauaf_t2.ko; sleep 2; dmesg | grep PHANTOM", "code_hash": "bab05c50e33011b81dbdc0092940682cf9cebd45759eca53a301eacf188b3167", "notes": "shares fix.diff with DF-5347 (the unload-dequeue hunk is this findings fix); fix validated on rebuilt kernel: phantom callback gone, reservations released, guest stable", "recommended_fix": "_bus_dmamap_unload: if pagesneeded||pagesreserved, STAILQ_REMOVE the map from the zone waiting list and call return_bounce_pages; busdma_swi: skip maps whose counters were reset (see fix.diff)", "fix_status": "fixed", "fix_baseline_reproduced": true, "fix_patched_reproduced": false, "fix_verdict": "fix.diff (DF-5347 pack) applied to guest /usr/src, kernel rebuilt. Baseline: phantom callback fired for unloaded map. Patched: RESULT=FAILED -- no callback after unload, reservation released, cleanup completed, guest stable.", "fix_evidence": "dmesg_fixrun.txt (DF-5347 pack): BUSDMA-PHANTOM lines on patched kernel show RESULT=FAILED + cleanup done" } |