⬢ DragonFlyBSD Kernel Audit
DF-4026 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-4026",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "leak",
  "confidence": "certain",
  "verdict": "Unprivileged local user (0666 /dev/dsp) obtains a persistent RW page mapping over freed M_DEVBUF kernel heap: dsp_mmap_single() can only acquire the channel lock while chn_resizebuf() is inside one of its three lock-drop windows, and exactly then it maps the old bufsoft buffer that the resize kfree()s microseconds later. Reproduced live: shrink-ioctl-returns-0 + 128K mapping held + live buffer 256B in every session (round 0-73); controlled write into the live kernel buffer that reclaimed the freed block demonstrated via the alias proof (0xDF marker), and post-free kernel rewriting of 31/32 stale pages demonstrated via the mutation proof. No panic is needed for the corruption (silent). uid=0 chain not completed this run - the remaining step is privileged-target grooming within the 128K kmem-slab class.",
  "exploit_chain": "open /dev/dsp (fresh vchan clone, 0666) -> SETFRAGMENT 2x64K (bufsoft=128K) -> race mmap(128K) vs shrinking SETFRAGMENT 2x16B: the shrink validates CHN_F_MMAP at chn_resizebuf entry, drops the lock (SETBLOCKSIZE + remalloc kmalloc windows), the mapper takes the lock mid-window and maps the OLD buffer, the resize completes and kfrees that buffer -> stale RW mapping over freed heap -> spray fresh dsp channels grown to 128K to reclaim the block -> read/write the reclaiming victims live kernel buffer through the stale alias (proven). Escalation to uid=0 would require reclaiming a privileged 128K-class kernel object (not completed).",
  "evidence": "run.3.log (alias proof: HIT round 0, READ-after-free alias, WRITE-after-free 0xDF in victim live buffer, CONFIRMED, EXIT=0); run.log (stock baseline round 2 HIT + mutation proof 31/32 pages); run.fix.log (patched sound.ko: no window in 3000 rounds + legit mmap preserved); VERDICT.md for the full matrix and line refs",
  "kernel_refs": [
    "sys/dev/sound/pcm/channel.c:1762",
    "sys/dev/sound/pcm/channel.c:1815-1819",
    "sys/dev/sound/pcm/channel.c:1858-1864",
    "sys/dev/sound/pcm/channel.c:1910",
    "sys/dev/sound/pcm/buffer.c:224-228",
    "sys/dev/sound/pcm/buffer.c:229-237",
    "sys/dev/sound/pcm/dsp.c:2297-2364",
    "sys/dev/sound/pcm/dsp.c:210-213"
  ],
  "poc_changes": "Seed had no runnable PoC. Learned/adjusted during bring-up: fd must be O_RDWR for PROT_READ|PROT_WRITE mmap (EACCES otherwise); no write()/SETTRIGGER before the race (CHN_F_RUNNING poisons CHN_F_MMAP_INVALID -> mmap EINVAL); shrink target must be < allocsize>>5 (256 works from a 128K buffer; the vchan parent limit floors deep shrinks at 256B; 8K working sets never free); reclaim via fresh-channel 128K grow (not shrink/regrow - pool channels with allocsize==128K take the NOCHANGE branch); pre-fault the stale mapping before spraying.",
  "attempts": 9,
  "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": 2400,
  "guest_dirty": 0,
  "build_cmd": "cc -O2 -Wall -pthread -o /tmp/mmap_resize_uaf /tmp/mmap_resize_uaf.c",
  "run_cmd": "/tmp/mmap_resize_uaf 3000",
  "code_hash": "c41acc08ec6c3a02a98bbeea5ec39c7b4d5b04254c5b80cce176427aa418ef6c",
  "fix_status": "fixed",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "fix.diff (channel.c recheck of CHN_F_MMAP before sndbuf_remalloc + buffer.c post-relock abort inside sndbuf_remalloc) built into sound.ko: identical PoC caught the window within 73 rounds on stock and never in 3000 rounds on the patched module; legit dsp mmap and fragment resizing preserved",
  "fix_evidence": "run.fix.log (patched: RESULT no window caught in 3000 attempts, EXIT=1; feature sanity OK); patched module md5 a517bbe17032d9ba88046ab759e578ca vs stock e869fcdebe0137ee26a9de85429c0baf; fix.diff in this pack",
  "notes": "impact recorded as leak per taxonomy (post-free kernel heap is readable), but a controlled write into a live kernel buffer was also proven (alias proof) - this is a write-capable UAF. Side observation recorded separately: kldunload sound panics in sndstat_sysuninit (root-only; panic.sndstat-unload.txt).",
  "recommended_fix": "Re-check CHN_F_MMAP after every relock in chn_resizebuf (before sndbuf_remalloc) and abort sndbuf_remallocs swap if the channel got mapped during its kmalloc window."
}