# DF-5370 VERDICT — REPRODUCED (vkernel kernel write-after-free + controlled-pointer dereference via freed bus_dma map)

## Root cause (path:line into sys/platform/vkernel64/platform/busdma_machdep.c)

1. `_bus_dmamap_load_buffer()` :580-593: a blocking load that cannot fully
   reserve bounce pages queues the map on
   `dmat->bounce_zone->bounce_map_waitinglist` (:587-589), stashes
   `map->dmat/buf/buflen` (:583-585) and returns EINPROGRESS. Queued state:
   `map->bpages` empty, `map->pagesneeded > 0`, `map->pagesreserved` = the
   partial reservation (both observed: 7 / 2 in the run).
2. `bus_dmamap_destroy()` :383-392: only guard is
   `STAILQ_FIRST(&map->bpages) != NULL` → a queued map is `kfree`d while
   still linked. Verified: destroy returned **0**.
3. `bus_dmamap_unload()` (macro sys/bus_dma.h:313) calls only
   `_bus_dmamap_unload()` :891-900, which walks the empty `bpages` list —
   the queued map is never dequeued (DF-5348 twin), so both orderings of
   conventional driver error cleanup leave the dangling link armed.
4. Wake path: `free_bounce_page()` :1156-1181 → `get_map_waiting()` :1184-1200
   pops the freed chunk → `reserve_bounce_pages(map->dmat, map, 1)` :1071-1092
   follows `map->dmat` read from freed memory (`bz = dmat->bounce_zone`,
   `BZ_LOCK(&bz->spin)` on the pointed-to address, counter writes at
   bz+52/+56) and writes `map->pagesreserved += pages` into freed memory.

## Reproduction (DragonFly 6.5-DEVELOPMENT guest, VKERNEL64 vkernel booted with -m 512m)

The dfbusdma driver (compiled into the vkernel, SYSINIT at SI_SUB_CONFIGURE)
plays a bus_dma driver: private lowaddr=16MB bounce zone seeded with
1 page (+1 per extra map_create ⇒ 3 total), mapA loads 1 page (consumed),
mapB loads a 7-page buffer above 16MB ⇒ pagesneeded=7, partial reserve=2 ⇒
queued, EINPROGRESS (DFly EINPROGRESS=36).

- `bus_dmamap_destroy(tag, mapB)` → **0** (freed while queued — UAF armed).
- Immediate `kmalloc(72)` spray reclaims the exact chunk (SAME CHUNK,
  deterministic; note `sizeof(struct bus_dmamap)` == 72 ⇒ 128-byte size
  class — a 64-byte spray misses, which cost one iteration to find).
- Planted body: `pagesneeded=0x10000, pagesreserved=0, dmat=<static planted
  fake tag whose bounce_zone points at the real zone>`.
- Wake via `bus_dmamap_unload(tag, mapA)`:
  kernel followed the **planted dmat** (0x1120e20) and wrote
  `pagesreserved 0→1` into the freed chunk (write-after-free through an
  attacker-chosen pointer), read `pagesneeded` as 65536 through it.
  The unsatisfiable pagesneeded keeps `get_map_waiting` returning NULL, so
  the `#ifdef notyet` `add_map_callback` panic stub (DF-1044) is NOT
  reached — the proof isolates *this* bug and leaves the vkernel ALIVE.
- Victim `mapC` load → EINPROGRESS forever: the ghost reservation
  permanently wedges the zone (one bounce page sits on the free list while
  `free_bpages==0`).
- A separate baseline run without reclaim observed the kernel writing
  `pagesreserved` 2→3 through the stale link — the UAF does not depend on
  reclaim luck.

Escalation ceiling: identical code to pc64 DF-5347 (High, arbitrary ring-0
call via the freed map's callback). On the vkernel the deferred callback is
the notyet panic, so the chain stops at controlled write + controlled
pointer dereference + zone wedge; reaching the stub instead yields the
DF-1044 panic. No stock VKERNEL64 driver uses bus_dma, so guest-of-guest
reachability requires a bus_dma driver kldloaded into the vkernel — hence
Medium severity (memcorrupt bucket) rather than the pc64 twin's High.

## Fix validation

fix.diff (destroy EBUSY guard on pagesneeded/pagesreserved; unload dequeues
queued maps and returns their reservation):

- baseline run.log: destroy=0, planted-dmat UAF write (0→1), wedge PROVEN.
- run.fixed.log: `bus_dmamap_destroy(queued mapB) = 16 (EBUSY)`,
  unload(mapA)+unload(mapB) drain the queue, `mapC load err=0 nsegs=1 —
  zone healthy`. No UAF, no wedge. Vkernel ALIVE.

## Negative/notes

- No panic in the decisive runs (by design; deterministic).
- EINPROGRESS is 36 on DragonFly (not 115).
- vkernel pmap interplay: `_bus_dma_extract` (:503-510) feeds
  `pmap_extract/pmap_kextract`, whose empty-PTE garbage return is DF-5290;
  not re-verified here.

## 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.
