emu_pci_attach bad: path never unwinds emu_init β leaks ptb_pages/silent_page DMA and mpu401/midi state on every late attach failure
Summary
After emu_init succeeds (allocating the 2KB PTB DMA page and 4KB silent page and wiring mpu401), any later attach failure (AC97_CREATE, mixer_init, IRQ alloc/snd_setup_intr) jumps to bad: which releases reg/irq/dmat/lock and kfrees sc but never calls emu_uninit - orphaning ~6KB of coherent DMA plus mpu401/midi structures per failed attach, repeated on bus rescans. Contrast emu_init's own correct internal unwind. Hardware-gated (no 1102 PCI device on guest, kldload fails). inited-flag unwind fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4094 Β· 8 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.4 KB | β raw | |
| VERDICT.md | β | 2.6 KB | β raw | |
| probe.sh | β | 585 B | view raw | |
| run.log | β | 282 B | view raw | |
| env.txt | β | 184 B | view raw | |
| fix.diff | β | 771 B | view raw | |
| manifest.json | β | 1006 B | view raw | |
| verdict.json | β | 2.4 KB | view raw |
DF-4094 β emu10k1 attach-failure path leaks emu_init DMA allocations + mpu401 state
File: sys/dev/sound/pci/emu10k1.c (pass 2, GLM 5.3)
What
emu_pci_attach() calls emu_init(sc) at sys/dev/sound/pci/emu10k1.c:2114.
On success emu_init() has allocated and programmed:
sc->mem.ptb_pagesβ EMUMAXPAGES*4 = 2048 bytes of bus_dmamem (emu10k1.c:1891-1892)sc->mem.silent_pageβ 4096 bytes of bus_dmamem (emu10k1.c:1896-1897)
After that, three more steps can still fail and jump to bad::
AC97_CREATE()== NULL (emu10k1.c:2119-2120)mixer_init()== -1 (emu10k1.c:2122)- IRQ alloc /
snd_setup_intr()(emu10k1.c:2127-2133) - additionally
emu_midiattach()has already run (emu10k1.c:2124), sosc->mpu(kmalloc'd mpu401 + midi unit) is live too.
The bad: path (emu10k1.c:2149-2157) destroys codec/reg/irq/dmat/lock and
frees sc, but never calls emu_uninit(), so the two DMA pages
(~6 KB of coherent memory per failed attach) and the mpu401/midi structures
leak. emu_init()'s own internal failure paths free their allocations
correctly (emu10k1.c:1893-1901) β the leak is exclusively the late-failure
window.
Threat model
Requires an emu10k1/emu10k2/emu10k3 PCI card whose attach fails late (e.g. shared-IRQ resource exhaustion, AC97 codec probe failure). Privileged/hardware-gated; impact is a kernel-memory leak per attach attempt (attach retries on bus rescans repeat the leak). No memory corruption, no info leak to userspace.
Source trace (certain, source-level)
- emu10k1.c:2114
if (emu_init(sc) == -1) goto bad;β success continues - emu10k1.c:1891-1901 emu_init allocates ptb_pages + silent_page (kept on success)
- emu10k1.c:2119-2133 any of AC97_CREATE / mixer_init / irq alloc fails β
goto bad - emu10k1.c:2149-2157
bad:frees reg/irq/dmat/lock/sc β ptb_pages, silent_page, sc->mpu never released.
Fix
Track that emu_init succeeded and unwind via emu_uninit() in bad:
(see fix.diff). emu_uninit() is safe to call in this window: it writes
quiescent values to the card (already resources-mapped), frees both DMA
pages, and only calls mpu401_uninit if sc->mpu != NULL (emu10k1.c:2038).
Reproduce
Hardware-gated: QEMU does not emulate emu10k1 (probe evidence in
env.txt / run.log: no 1102:0002/0004/0008 PCI device, kldload snd_emu10k1
fails). Source-level certainty only.
DF-4094 VERDICT
Status: inconclusive on-guest (hardware-gated) / certain at source level. Impact: kernel heap/DMA leak (~6 KB + mpu401 struct per failed attach).
Was it reproduced?
No. The DragonFly QEMU guest has no Creative emu10k1 device β verified live:
pciconf -l shows no vendor 0x1102 function, kldload snd_emu10k1 fails
(run.log). The attach-failure path cannot be entered without the PCI device,
so runtime reproduction is impossible on this guest. The defect itself is
proven by source trace:
emu_pci_attach()sys/dev/sound/pci/emu10k1.c:2114 βemu_init(sc)succeeds and allocatessc->mem.ptb_pages(emu10k1.c:1891) andsc->mem.silent_page(emu10k1.c:1896).- Late failures at emu10k1.c:2119 (AC97_CREATE), :2122 (mixer_init),
:2127-2133 (IRQ alloc/setup) all
goto bad. bad:at emu10k1.c:2149-2157 releases reg/irq/dmat/lock and kfreesscbut never callsemu_uninit()(which is the only code that frees ptb_pages/silent_page β emu10k1.c:2035-2036 β and mpu401_uninit at :2038-2039, whileemu_midiattach()ran at :2124).- emu_init's own failure paths (emu10k1.c:1893-1901) free properly, so the leak is exactly the post-emu_init failure window.
Why not exploitable further
Pure allocation leak; no use-after-free (the leaked blocks are simply orphaned), no info leak, no attacker-controlled size. Trigger needs the physical card AND a late attach failure (resource exhaustion). Severity Low.
Fix validation
Not performed on-guest (would require emu10k1 hardware to build a kernel and fail an attach deliberately). fix.diff is a straightforward unwind fix:
int inited = 0;
...
if (emu_init(sc) == -1) goto bad;
inited = 1;
...
bad:
if (inited)
emu_uninit(sc); /* frees ptb_pages, silent_page, mpu */
emu_uninit() is safe here: card registers are still mapped until the
bus_release_resource that follows, EMU_INTE is zeroed first, and
mpu401_uninit is guarded by if (sc->mpu).
kernel_refs
- sys/dev/sound/pci/emu10k1.c:2114 (emu_init success)
- sys/dev/sound/pci/emu10k1.c:1891-1901 (allocations + internal unwind)
- sys/dev/sound/pci/emu10k1.c:2119-2133 (late failure jumps)
- sys/dev/sound/pci/emu10k1.c:2149-2157 (bad: without emu_uninit)
- sys/dev/sound/pci/emu10k1.c:2035-2039 (what emu_uninit frees)
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
Fix verification
not_testablefix.diff authored and git-apply-verified against the read-only sys/ tree; cannot be built+validated on the guest because no emu10k1 device exists to attach (and thus fail late).
fix.diff (unified, applies clean: 'git apply --check' OK)
Confirmed kernel references
Detail
Evidence (decisive lines)
VERDICT.md (4-step source trace with emu10k1.c line refs); run.log (guest probe: pciconf grep 1102 empty, kldload fails); fix.diff (git apply --check clean, adds inited flag + emu_uninit() at bad:)
PoC changes
No PoC seed existed; wrote probe.sh documenting the guest-side hardware-absence check and ran it.
Verified recommended fix
Track emu_init success and call emu_uninit(sc) in the bad: path before releasing resources.
Verdict
Source-level certain, runtime-gated: emu_pci_attach() succeeds in emu_init() (which allocates ptb_pages + silent_page DMA at emu10k1.c:1891/1896 and wires mpu401 at :2124), and every later failure (AC97_CREATE :2119, mixer_init :2122, IRQ alloc :2127-2133) jumps to bad: (:2149-2157) which never calls emu_uninit() β leaking ~6KB coherent DMA + struct mpu401 per failed attach. The QEMU guest has no emu10k1 device (probe: no 0x1102 PCI function, kldload snd_emu10k1 fails), so the path cannot be entered at runtime; classified inconclusive-on-guest rather than not_reproduced.
No comments yet.