⬢ DragonFlyBSD Kernel Audit
DF-4322 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-4322",
  "title": "dsp_clone() hands clone cdevs through a shared `static struct cdev *dev` -- concurrent clone handlers (one per pcm unit) race; an open of /dev/dspN can be given another cards clone cdev (demonstrated unprivileged), and the stale static can hand devfs a destroyed cdev during pcm_unregister (traced UAF)",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "none",
  "confidence": "certain",
  "verdict": "Reproduced on DragonFly 6.5-DEVELOPMENT (X86_64_GENERIC) with two QEMU sound cards (pcm0=ES1370, pcm1=intel-hda). dsp_clone() stores the outgoing cdev in a function-static global (dsp.c:2381); devfs runs clone handlers with devfs_lock dropped (devfs_core.c:2329-2336) and each pcm unit has independent locks, so handlers for different units run concurrently while sharing that global. An unprivileged open loop (4+4 threads, ~118k opens) produced a /dev/dsp1 open whose fd carried card 0s clone cdev (minor unit byte 0, SNDCTL_AUDIOINFO devnode /dev/dsp0.vp0, channel pcm0:virtual:dsp0.vp0). Three further ~36-56k-open runs were hit-free, characterizing the race as rare (~1e-5 per open) but real. Demonstrated impact is cross-card channel confusion; both dsp nodes are 0666 by default so no permission boundary is crossed on a default box, hence impact=none in the enum (logic defect). Traced but not demonstrated: snd_clone_alloc()s early NULL returns (clone.c:627-629) do not set *dev, so a dsp_clone() racing pcm_unregister()s snd_clone_disable() (sound.c:1200) can return the stale static -- potentially a cdev already destroyed by snd_clone_destroy/GC (sound.c:1226, clone.c:431/457) -- handing devfs a freed cdev (UAF/panic ceiling, requires root-side detach concurrency).",
  "exploit_chain": "Unprivileged: open(\"/dev/dsp0\") and open(\"/dev/dsp1\") in parallel loops -> devfs drops devfs_lock and invokes both dsp_clone() handlers concurrently -> shared static `dev` last-writer-wins -> wrong cards clone cdev returned to devfs -> open(\"/dev/dsp1\") yields a pcm0 channel (proved via fstat minor + SNDCTL_AUDIOINFO devnode).",
  "evidence": "run.log (decisive run: RACE HIT: opened /dev/dsp1, fd is clone of card 0 (devnode /dev/dsp0.vp0, name pcm0:virtual:dsp0.vp0), 117879 cycles, 1 mismatch); run.2/3/4.log (hit-free characterization); VERDICT.md (full trace with dsp.c/clone.c/sound.c/devfs_core.c line refs); fix.diff (one-line fix: make `dev` local, matching FreeBSD)",
  "kernel_refs": [
    "sys/dev/sound/pcm/dsp.c:2381",
    "sys/dev/sound/pcm/dsp.c:2495",
    "sys/dev/sound/pcm/dsp.c:2506",
    "sys/dev/sound/pcm/dsp.c:2520",
    "sys/dev/sound/clone.c:627",
    "sys/dev/sound/clone.c:794",
    "sys/vfs/devfs/devfs_core.c:2329",
    "sys/dev/sound/pcm/sound.c:1138",
    "sys/dev/sound/pcm/sound.c:1200",
    "sys/dev/sound/pcm/sound.c:1226"
  ],
  "poc_changes": "none -- PoC authored fresh for this finding (fstat minor-byte check per open for speed; SNDCTL_AUDIOINFO confirmation on mismatch)",
  "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": 540,
  "guest_dirty": 1,
  "build_cmd": "cc -O2 -pthread -o /tmp/dsp_race /tmp/dsp_race.c",
  "run_cmd": "/tmp/dsp_race   (as unprivileged user, two pcm units registered)",
  "code_hash": "28250295ae4cbb00a1ffa0c2d019a9b9cf965f57df072db9631ee1797f673d3f",
  "notes": "Guest launched with dfbsd-qemu/vm_snd.sh (ES1370 + intel-hda + hda-duplex); modules loaded as root (kldload snd_es137x snd_hda); the PoC itself runs unprivileged (uid 1001). Fix validation skipped as impractical: the race fires ~1e-5 per open, so proving the patched negative in one kernel build cycle is not statistically meaningful; the fix is structural (removes the shared state entirely, identical to FreeBSDs code).",
  "recommended_fix": "Make the outgoing cdev a local variable in dsp_clone(): `struct cdev *dev = NULL;` instead of `static struct cdev *dev = NULL;` (dsp.c:2381), matching FreeBSD.",
  "fix_status": "not_testable",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "Structural one-line fix (localize `dev`) eliminates the shared state; kernel-build fix validation was not performed because the baseline race probability (~1 hit per 1e5 opens) makes a single patched run statistically uninformative.",
  "fix_evidence": "fix.diff; VERDICT.md root-cause section"
}