⬢ DragonFlyBSD Kernel Audit
DF-3866 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-3866",
  "status": "inconclusive",
  "reproduced": 0,
  "impact": "none",
  "confidence": "certain",
  "verdict": "Runtime reproduction is hardware-gated: the QEMU guest has no Creative Labs (0x1102) PCI device, snd_emu10kx.ko loads but emu_pci_probe() matches nothing, so no softc, /dev/emu10kx or pcm device ever exists and the /dev/dsp trigger stops at open(ENOENT) (run.log). The defect itself is source-level certain and its memory-corruption geometry is PROVEN on the guest kernel ABI: emu_intr_register() returns -1 on slot exhaustion (emu10kx.c:945), emu10kx-pcm.c stores it unchecked (emu10kx-pcm.c:983,1137) and passes it to emu_intr_unregister() (emu10kx-pcm.c:992,1162), which indexes sc->ihandler[hnumber] with no bounds check (emu10kx.c:956,964-967). The kernel-compiled layout proof shows ihandler[-1] spans exactly {sc->reg, sc->irq, sc->ih}; the -1 path reads the nonzero high dword of the irq pointer (bypassing the inte_mask==0 guard), NULLs reg/irq/ih, and smears pointer bits into the EMU_INTE register write; detach then NULL-derefs in bus_release_resource/bus_teardown_intr (emu10kx.c:3524-3527). Pool exhaustion is reachable because the card-level handler registered at emu10kx-pcm.c:1453 is never unregistered (emu_pcm_uninit is a no-op at emu10kx-pcm.c:1292-1296), leaking up to 6 slots per root kldunload/kldload cycle. Both gates (hardware + root for exhaustion) cap real-world impact at a root-triggered panic/DoS: severity Low, memcorrupt bucket.",
  "exploit_chain": "root: kldunload/kldload snd_emu10kx xN (each cycle leaks 1-6 ihandler slots via never-unregistered card-level handle) -> emu_intr_register() returns -1 -> rec-channel PCMTRIG_START stores ch->ihandle=-1 -> PCMTRIG_STOP calls emu_intr_unregister(sc,-1) -> ihandler[-1] OOB read (hi32(sc->irq)!=0 passes guard) -> four NULL OOB writes NULL sc->reg/sc->irq/sc->ih -> next detach panics in bus_teardown_intr/bus_release_resource. No unprivileged path to exhaustion exists (concurrent use needs <=12 of 32 slots); no uid0 chain.",
  "evidence": "run.log lines DF3866: PROVEN: ihandler[-1] overlays {reg, irq, ih} and GATE: target device absent; layout_proof.c (kernel module, verbatim softc prefix); VERDICT.md full trace; fix.diff (bounds check + caller guards + leak fix)",
  "kernel_refs": [
    "sys/dev/sound/pci/emu10kx.c:919",
    "sys/dev/sound/pci/emu10kx.c:945",
    "sys/dev/sound/pci/emu10kx.c:948",
    "sys/dev/sound/pci/emu10kx.c:956",
    "sys/dev/sound/pci/emu10kx.c:964",
    "sys/dev/sound/pci/emu10kx.c:3524",
    "sys/dev/sound/pci/emu10kx-pcm.c:983",
    "sys/dev/sound/pci/emu10kx-pcm.c:992",
    "sys/dev/sound/pci/emu10kx-pcm.c:1137",
    "sys/dev/sound/pci/emu10kx-pcm.c:1162",
    "sys/dev/sound/pci/emu10kx-pcm.c:1453",
    "sys/dev/sound/pci/emu10kx-pcm.c:1292"
  ],
  "poc_changes": "Fixed ioctl name SNDCTL_DSP_SETTRIG -> SNDCTL_DSP_SETTRIGGER (DragonFly soundcard.h); kmod Makefile needed SRCS += bus_if.h device_if.h to generate interface headers; fixed an arithmetic slip in the layout-proof assertion (oh-16 not oh-sz+16)",
  "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": 95,
  "guest_dirty": 0,
  "build_cmd": "cd findings/poc/DF-3866 && ./build.sh   (cc -O2 -o rec_cycle rec_cycle.c; kmod via /usr/src/sys/conf/kmod.mk)",
  "run_cmd": "cd findings/poc/DF-3866 && ./run.sh   (gate check; kldload kmod/lay.ko; dmesg | grep DF3866; ./rec_cycle /dev/dsp0 5)",
  "code_hash": "ab3f600b5497f7213d90c8dd150d69bba571df6b51037e6f52f3e1af2cbc2c5d",
  "notes": "Hardware gate proven live: kldload snd_emu10kx succeeded on guest, zero PCI 0x1102 devices, no /dev/emu10kx created; rec trigger stops at open(ENOENT). Post-session cleanup kldunload snd_emu10kx PANICKED the guest via an unrelated cascade: linker_file_unload of the sound module -> sndstat_sysuninit() -> sbuf_delete called with uninitialized or corrupt sbuf (sys/dev/sound/pcm/sndstat.c, outside this files scope; serial-console panic trace in the orchestrator task_log). Guest reset from with-src snapshot and verified clean (no emu module, no Creative hw, /tmp clean). snd_emu10kx is NOT in X86_64_GENERIC; module-only.",
  "recommended_fix": "Bounds-check hnumber in emu_intr_unregister() (return -1 if <0 or >=EMU_MAX_IRQ_CONSUMERS), guard emu_intr_register() failure at emu10kx-pcm.c:983/1137, and unregister the card-level handle in emu_pcm_uninit()",
  "fix_status": "not_testable",
  "fix_baseline_reproduced": 0,
  "fix_patched_reproduced": 0,
  "fix_verdict": "Fix validation impossible on this guest: the bug never executes without Creative Labs hardware (baseline already gated). fix.diff is authored against the read-only sys/ tree, never applied; it adds the bounds check, both caller guards, and the emu_pcm_uninit() unregister that stops the leak feeding the exhaustion.",
  "fix_evidence": "findings/poc/DF-3866/fix.diff; VERDICT.md Fix validation section; run.log GATE lines"
}