⬢ DragonFlyBSD Kernel Audit
DF-5227 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-5227",
  "status": "reproduced",
  "verdict": "REPRODUCED on stock drm.ko (DragonFly 6.5-DEVELOPMENT guest): dma_alloc_coherent() (sys/dev/drm/linux_dma.c:47) implements the Linux coherent-allocation API over kmem_alloc_contig() without M_ZERO (vm_contig.c:612 passes M_NOWAIT only; zeroing requires M_ZERO at vm_contig.c:533-534; reserve path vm_contig.c:288 never zeroes), so the returned buffer contains the previous owners kernel memory verbatim. PoC: contigmalloc 2MB -> marker fill -> contigfree -> dma_alloc_coherent 2MB lands on the SAME physical block (0x3000000) with 512/512 stale pages and the marker readable in the allocation. Contract violation certain; live in-tree callers (amdgpu_ih.c:66, drm_pci.c:130, radeon_gart.c:85) all carry manual memset() workarounds, so no unprivileged leak on stock desktop today -> severity Medium (latent kernel info leak for any caller that trusts the API, e.g. future upstream syncs). Fix validated in the same run: contigmalloc_map with M_NOWAIT|M_ZERO returns a fully zeroed buffer (marker_hits=0/512 stale 0/512) with identical reuse determinism.",
  "reproduced": true,
  "impact": "leak",
  "confidence": "certain",
  "exploit_chain": "spray/stale kernel data in contiguous physical pages (any prior contigmalloc/kmem_alloc_contig user, incl. network driver rings or a previous coherent allocation) -> driver calls dma_alloc_coherent() trusting the zeroing contract -> GPU/engine reads the buffer -> userspace reads GPU-accessible memory: stale kernel memory disclosure. The in-kernel primitive (stale bytes verbatim in the coherent allocation) is demonstrated; the userspace-read leg requires GPU hardware (absent on the audit guest).",
  "evidence": "run.log: SAME PHYSICAL BLOCK, marker_hits=512 stale_nonzero_pages=512/512, hexdump lines DF5227 LEAK 0000: 44 46 35 32 32 37 ... |DF5227-STALE-Ker|; run.fixed.log: marker_hits=0 stale_nonzero_pages=0/512",
  "kernel_refs": [
    "sys/dev/drm/linux_dma.c:47",
    "sys/dev/drm/linux_dma.c:64",
    "sys/vm/vm_contig.c:612",
    "sys/vm/vm_contig.c:533",
    "sys/vm/vm_contig.c:288",
    "sys/dev/drm/amdgpu/amdgpu_ih.c:66",
    "sys/dev/drm/drm_pci.c:130",
    "sys/dev/drm/radeon/radeon_gart.c:85"
  ],
  "poc_changes": "Authored fresh (no usable seed for this finding; the reserved DF-5226 directory holds the twin-batchs unrelated refuted-candidate negative record and was left untouched, so this finding used the next reserved ID). KLD module calls the real drm.ko-exported dma_alloc_coherent; determinism engineered via the DMA-reserve first-fit alist reuse (contigmalloc/memset/contigfree immediately before the coherent alloc).",
  "attempts": 2,
  "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": 240,
  "guest_dirty": 0,
  "build_cmd": "cd /tmp/poc/DF-5227 && make",
  "run_cmd": "kldload drm; kldload /tmp/poc/DF-5227/poc5227.ko; dmesg | grep DF5227",
  "code_hash": "40fbf2c18620bd11c034f7e1fccdb31daba8e1feba2784698fe3aaeba3a91203",
  "notes": "Fix shared with DF-5228 (same two functions rewritten to contigmalloc_map(M_ZERO)/contigfree). Observation kept for the record: kldunload drm (no device attached) hung the guest linker subsystem once - pre-existing drm teardown behavior (DF-4532/DF-5064 family), unrelated to this patch. Guest reset clean (vm.sh reset with-src) after evidence collection.",
  "recommended_fix": "Implement dma_alloc_coherent with contigmalloc_map(..., M_NOWAIT | M_ZERO, ...) instead of kmem_alloc_contig (and dma_free_coherent with contigfree - see DF-5228); 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 and loaded on a fresh boot (vm.sh reset with-src): the identical PoC returns marker_hits=0 stale_nonzero_pages=0/512 (run.fixed.log) - the stale-memory primitive is gone; allocation still succeeds on the same physical block, so no functional regression in the PoC path.",
  "fix_evidence": "run.fixed.log: DF5227 RESULT: marker_hits=0 stale_nonzero_pages=0/512; compare run.log baseline marker_hits=512 stale_nonzero_pages=512/512"
}