emu_pcm_attach late-failure path frees sc while the mixer remains registered: dangling mixer devinfo UAF on /dev/mixer ioctls and leaked AC97 submixer
Summary
After mixer_init succeeds, pcm_register can still fail (veto/unit exhaustion). The shared bad: label kfrees sc without mixer_uninit: the registered mixer keeps devinfo==freed sc, so the next /dev/mixerN ioctl runs emu_dspmixer_set on freed memory (route reads, sc->sm deref, indirect writes through a freed sc->card), and the mix_create'd AC97 submixer leaks. Distinct sink from DF-1506 (dangling IRQ consumer on the same path). Mixer-uninit-on-bad fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4299 Β· 5 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.3 KB | β raw | |
| VERDICT.md | β | 2.9 KB | β raw | |
| fix.diff | β | 2.0 KB | view raw | |
| manifest.json | β | 542 B | view raw | |
| verdict.json | β | 1.7 KB | view raw |
DF-4299 β emu10kx-pcm attach failure after successful mixer_init frees live mixer devinfo
Finding
emu_pcm_attach() (sys/dev/sound/pci/emu10kx-pcm.c) calls mixer_init(dev,
&emudspmixer_class, sc) (line 1399) β registering the mixer with sc as
its persistent devinfo β and only afterwards performs steps that can still
fail (emu_pcm_init() is a no-op that always returns 0, but
pcm_register() at line 1467 can fail with EINVAL/ENODEV). The shared
bad: label (lines 1488-1494) then frees sc (and, for RT_FRONT, the
AC97 submixer created inside emu_dspmixer_init() at line 214 via
mixer_create()) without calling mixer_uninit():
- every later
/dev/mixerNioctl dereferences the freedscthroughmix_getdevinfo(m)βemudspmixer_set()βsc->card,sc->sm,emumix_set_volume(sc->card, ...)β use-after-free read/write; - the
sc->smAC97 submixer object leaks.
Distinct from the known DF-1506 (which covers the dangling IRQ consumer
sc->ihandle registered at line 1453 on the same path).
Status
Not verified on the QEMU guest: snd_emu10kx_pcm requires SB Live!/
Audigy PCI hardware (CA0108/EMU10K2), which the guest does not emulate.
Analysis-only; root cause established by code path trace.
Fix
On the bad: path, run mixer_uninit(dev) (when mixer_init succeeded)
before freeing sc.
DF-4299 verdict
Reproduced?
No β not testable on this guest. snd_emu10kx_pcm binds to the
emu10kx bridge driver, which probes for Creative EMU10K1/EMU10K2 PCI
chipsets (emu10kx.c PCI IDs). The audit guest (QEMU virtio/ATI-less) has
no such device; the module has nothing to attach to, so the trigger
cannot be reached at runtime here (kldload snd_emu10kx would simply
find no hardware). Status: untested, analysis-only.
Root cause (path:line)
sys/dev/sound/pci/emu10kx-pcm.c:1399βmixer_init(dev, &emudspmixer_class, sc)registersscas the mixer's persistent devinfo (sound.cmixer_initstores it;mix_getdevinfo()returns it to every mixer ioctl).sys/dev/sound/pci/emu10kx-pcm.c:214β for RT_FRONT the mixer init callback alsomixer_create()s an AC97 submixer stored insc->sm.sys/dev/sound/pci/emu10kx-pcm.c:1455-1470β after mixer_init, onlyemu_pcm_init()(always returns 0, line 1285-1290) andpcm_register()(line 1467) can still fail;pcm_registerfails onpcm_veto_load(sound.c:1021-1027) ordevice_get_unit(dev) > PCMMAXUNIT(sound.c:1030-1035).sys/dev/sound/pci/emu10kx-pcm.c:1488-1494βbad:path:ac97_destroy(sc->codec)+snd_mtxfree(sc->lock)+kfree(sc)β nomixer_uninit(dev), nomixer_delete(sc->sm).
Consequence: /dev/mixerN remains registered (created by the sound core
inside mixer_init) with m->devinfo == freed sc; the next mixer ioctl
(e.g. MIXER_CMD/OSS mixer write) runs emu_dspmixer_set() on freed
memory: reads sc->route, dereferences sc->sm/sc->card, writes DSP
registers through emumix_set_volume(sc->card, ...) (sc->card freed
pointer load) β kernel UAF (read + effectively arbitrary indirect
register-write path), plus the submixer leak.
Threat
Requires the sound card present and an attach-time failure after
mixer_init β i.e. pcm_register() failing. Not attacker-schedulable in
general (unit exhaustion / prior pcm error), hence Low severity, but the
resulting UAF is on the standard mixer ioctl path.
Fix
--- a/sys/dev/sound/pci/emu10kx-pcm.c
+++ b/sys/dev/sound/pci/emu10kx-pcm.c
@@ -1485,6 +1485,9 @@ emu_pcm_attach(device_t dev)
return (0);
bad:
+ if (sc->ihandle >= 0)
+ (void)emu_intr_unregister(sc->card, sc->ihandle);
+ if (mixer_inited)
+ (void)mixer_uninit(dev);
if (sc->codec)
ac97_destroy(sc->codec);
if (sc->lock)
(with a local int mixer_inited = 0; set to 1 after each successful
mixer_init() call; the ihandle unregister line closes the DF-1506
attach-failure half.)
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 from the traced root cause; not built (no hardware)
VERDICT.md diff
Confirmed kernel references
Detail
Evidence (decisive lines)
VERDICT.md path trace: emu10kx-pcm.c:1399 (mixer_init), :1467 (pcm_register failure), :1488-1494 (bad: kfree without mixer_uninit); sound.c mixer_init devinfo registration.
PoC changes
n/a β no PoC (hardware prerequisite absent)
Verified recommended fix
Run mixer_uninit(dev) on the emu_pcm_attach bad: path when mixer_init succeeded before kfree(sc).
Verdict
Analysis-only: emu_pcm_attach() frees sc on the late-failure (pcm_register) path without mixer_uninit(), leaving /dev/mixerN's devinfo dangling (UAF on the next mixer ioctl) and leaking the AC97 submixer. Not verifiable on the audit guest: snd_emu10kx_pcm requires SB Live!/Audigy PCI hardware the guest does not emulate.
No comments yet.