⬢ DragonFlyBSD Kernel Audit
DF-5228 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-5228",
  "status": "reproduced",
  "verdict": "REPRODUCED on stock drm.ko (DragonFly 6.5-DEVELOPMENT guest): dma_free_coherent (sys/dev/drm/linux_dma.c:81) is a bare kmem_free(kernel_map, addr, size), but the matching free for kmem_alloc_contig/contigmalloc is contigfree() (vm_contig.c:588-603: pmap_qremove + kmem_free + vm_page_free_contig). The contig regions pages are wired object-less pages pmap_kenterd over an object-less kmem_alloc_pageable entry, so vm_map_delete (vm_map.c:3325-3370) never pmap_removes the range and never unwires/frees the pages. Demonstrated per-free: kernel PTE still valid after free (pmap_kextract != 0) and the phys page still wire_count=1 (probe on both reserve [PG_FICTITIOUS] and real pages); demonstrated at scale: an alloc+free loop that must be loss-free burned 119-124MB permanently per 120-240MB cycled (after silently draining the 128MB shared DMA reserve), wire surplus 31199-32200 pages, control contigmalloc/contigfree deficit 0. Live in-tree leak paths: radeon_gart_fini (radeon_gart.c:110), amdgpu_ih_ring_fini (amdgpu_ih.c:111), TTM dma pools. Impact: permanent wired-memory exhaustion (unreclaimable under pressure) + DMA-reserve starvation for other drivers + orphaned-PTE/pmap accounting desync; triggers are driver lifecycle (unload/re-probe) -> Medium. Fix validated in the same run environment: contigfree-based dma_free_coherent removes the PTE (pmap_kextract=0) and the loop deficit collapses to 2 pages (noise).",
  "reproduced": true,
  "impact": "dos",
  "confidence": "certain",
  "exploit_chain": "any radeon/amdgpu driver unload or failed GPU probe cycles dma_alloc_coherent/dma_free_coherent (GART table, IH rings) -> every cycle permanently burns KBs-MBs of wired RAM and reserve capacity -> repeatable until the machine is out of memory with all of it unreclaimable (hard DoS); the shared 128MB DMA reserve starvation also degrades unrelated devices contig allocations.",
  "evidence": "run.log: pmap_kextract(va)=0000000018000000 -> kernel PTE STILL VALID (ORPHANED); phys page state: wire_count=1 ... -> page STILL WIRED (LEAKED); RESULT: 120 MB alloc+freed ... deficit=30691 pages (119MB), v_wire surplus=31199 with control ... deficit=0; run.fixed.log: kernel PTE gone, deficit=2 pages (0MB)",
  "kernel_refs": [
    "sys/dev/drm/linux_dma.c:81",
    "sys/vm/vm_contig.c:588",
    "sys/vm/vm_contig.c:447",
    "sys/vm/vm_kern.c:171",
    "sys/vm/vm_map.c:3325",
    "sys/vm/vm_page.c:2858",
    "sys/dev/drm/radeon/radeon_gart.c:110",
    "sys/dev/drm/amdgpu/amdgpu_ih.c:111"
  ],
  "poc_changes": "Authored fresh (reserved-ID note: DF-5226 on disk holds the twin-batchs unrelated refuted-candidate negative record, left untouched; this finding used the next reserved ID). v1 measured only vmstats deltas and under-reported while the DMA reserve absorbed the leak; v2/v3 added the PTE/page-state probes, the reserve-exhausting sustainability loop and the contigmalloc/contigfree control - all baseline runs kept in run.log.",
  "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": 420,
  "guest_dirty": 0,
  "build_cmd": "cd /tmp/poc/DF-5228 && make",
  "run_cmd": "kldload drm; kldload /tmp/poc/DF-5228/poc5228.ko; dmesg | grep DF5228",
  "code_hash": "78fdf676aff768fbc0da8e1e502c78c729bfd1638e1b4ed31bb03bed64f8a7c8",
  "notes": "Fix shared with DF-5227 (same rewrite of the two functions). Guest-side observation kept for the record: kldunload drm with no device attached hung the linker subsystem once - pre-existing drm teardown defect family (DF-4532/DF-5064), not this file. Guest reset clean (vm.sh reset with-src) after evidence collection.",
  "recommended_fix": "Implement dma_free_coherent with contigfree(cpu_addr, size, M_DEVBUF) instead of bare kmem_free (and zero the allocation via contigmalloc_map(M_NOWAIT|M_ZERO) - DF-5227); fix.diff in this pack.",
  "fix_status": "fixed",
  "fix_baseline_reproduced": true,
  "fix_patched_reproduced": false,
  "fix_verdict": "fix.diff applied to guest /usr/src/sys/dev/drm/linux_dma.c, drm.ko rebuilt from source, loaded on a fresh boot (vm.sh reset with-src): identical PoC now shows pmap_kextract(va)=0 (PTE removed) and a 120MB alloc+free loop deficit of 2 pages (noise) vs 30691 on stock - the leak is gone; allocations still succeed.",
  "fix_evidence": "run.fixed.log: DF5228: after free: pmap_kextract(va)=0000000000000000 -> kernel PTE gone; RESULT: ... deficit=2 pages (0MB), v_wire surplus=2; compare run.log baseline deficit=30691 pages (119MB), v_wire surplus=31199"
}