# DF-5228 VERDICT — dma_free_coherent() leaks every physical page

**Status: REPRODUCED** (impact: dos — permanent wired-memory exhaustion;
also an orphaned-PTE hazard) on the stock `/boot/kernel/drm.ko` of the
DragonFly 6.5-DEVELOPMENT guest.

## Evidence chain (all from run.log, stock drm.ko)

1. **Orphaned PTE**: immediately after `dma_free_coherent(p)`:
   `pmap_kextract(p) = 0x03900000 / 0x03a00000 / 0x18000000` across runs —
   the kernel PTE stays valid after the KVA was handed back to kernel_map.
   Root cause: the contig region is mapped by `pmap_kenter_noinval`
   (vm_contig.c:531) over an object-less kmem_alloc_pageable entry
   (vm_kern.c:171-175); `kmem_free` → `vm_map_delete` only calls
   `pmap_remove` for object-backed entries (vm_map.c:3325-3370) — never for
   this one. vm_kern.c:311-313 warns about exactly this misuse.
2. **Pages never released**: `PHYS_TO_VM_PAGE(pa)->wire_count == 1`,
   `queue == 0` — page STILL WIRED after the free (probe across both the
   reserve path, PG_FICTITIOUS=1, and the real-page path, PG_FICTITIOUS=0).
   `vm_page_free_contig` (vm_page.c:2858) is never called; the correct free is
   `contigfree()` (vm_contig.c:588-603).
3. **Sustainability test**: 60–120 cycles of
   `dma_alloc_coherent(2MB)` + `dma_free_coherent(2MB)` — with a correct free
   this must be loss-free forever. Observed on stock drm.ko:
   - v2 run: plateau for the first ~60 cycles (the 128MB DMA reserve being
     silently drained — fictitious reserve pages are invisible to
     `vmstats.v_free_count`), then linear drain once the reserve is
     exhausted; 240MB cycled → **124MB permanent deficit, wire surplus 32200**;
   - v3 run (reserve already exhausted): linear from cycle 1; 120MB cycled →
     **119MB permanent deficit, wire surplus 31199**, with **zero live
     allocations** at the end.
4. **Control**: identical loop with `contigmalloc/contigfree` → deficit 0–77
   pages (sampling noise; 400:1 signal-to-noise).

## Impact

Every `dma_free_coherent()` call permanently burns its pages (wired → never
reclaimed, even under full memory pressure) plus reserve capacity shared with
all other contig consumers (network rings, sound, USB). Live triggers:
`radeon_gart_fini` (per driver unload / failed probe — the GART table is
64KB–MBs), `amdgpu_ih_ring_fini` ×1–3 rings per amdgpu teardown (kldunload /
re-probe), TTM DMA pools. Root/hardware lifecycle triggered → Medium.

The orphaned PTE (KVA returned to kernel_map while a valid PTE to a wired
page persists) additionally leaves the map/pmap out of sync — vm_kern.c:311
warns resident_count is mistabulated in this situation.

No uid=0 chain (resource-exhaustion class). Panic was never needed; the
primitive is directly observable.

## Fix validation (same run environment)

`fix.diff` (shared with DF-5227): `dma_free_coherent` = `contigfree(cpu_addr,
size, M_DEVBUF)`. Guest reset (`vm.sh reset with-src`), fix applied,
drm.ko rebuilt and loaded on the fresh boot; identical PoC re-run
(run.fixed.log):

- `pmap_kextract(va) = 0` → kernel PTE properly removed;
- 120MB of alloc+free cycles → **deficit 2 pages (noise)**, wire surplus 2
  (vs 30691/31199 on stock);
- allocations still succeed → no functional regression.

## Notes

- v1 of the PoC mis-measured (vmstats deltas are invisible while the reserve
  absorbs the leak) — v2/v3 instruments (page-state probe + sustainability
  loop + control) were written after observing the reserve behavior; all
  three baseline runs are kept in run.log.
- `kldunload drm` (stock, no device attached) hung the guest's linker
  subsystem once; pre-existing drm teardown defect family (DF-4532/DF-5064),
  unrelated to this finding (linux_dma.c code paths are not involved in
  module teardown). The fix-validation run avoided unload (fresh boot).
- Guest reset clean after evidence collection.

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