fm801_pci_attach failure paths after mixer_init() leave a live 0666 /dev/mixerN over freed codec+softc β mixer-UAF family, 14th instance (DF-4156/4193/4299/4334/4344/4421/4457/4468/4504/4537/4550/4610)
Summary
The world-writable mixer cdev is published before three fallible operations remain (IRQ setup, dma tag, pcm_register); the oops: unwind destroys the codec and frees the softc while the cdev still dispatches open/ioctl into them (kobj indirect dispatch UAF). Secondary: oops: releases the MMIO region before teardown_intr - live ISR over an unmapped window. Family precedent severity; FM801 hardware absent from guest. Reorder + fix-unwind-order fix, compile-validated on guest (-Werror).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4651 Β· 10 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.4 KB | β raw | |
| VERDICT.md | β | 5.5 KB | β raw | |
| fm801_probe.c | β | 2.5 KB | view raw | |
| build.sh | β | 134 B | view raw | |
| run.sh | β | 90 B | view raw | |
| run.log | β | 2.2 KB | view raw | |
| env.txt | β | 210 B | view raw | |
| fix.diff | β | 2.1 KB | view raw | |
| fix_validate.sh | β | 830 B | view raw | |
| fix_build.log | β | 8.3 KB | view raw |
DF-4651 β fm801 attach-failure mixer-UAF (DF-4156 family, 14th instance)
fm801_pci_attach() publishes the 0666 /dev/mixerN at mixer_init()
(fm801.c:620) and keeps three failure paths after it β IRQ setup
(:625-627), dma tag (:636-638), pcm_register (:646). oops: then
ac97_destroy()s the codec (:657) and kfrees the softc (:662) while the
mixer cdev still dispatches open/ioctl into them: use-after-free with
kobj indirect dispatch. Secondary: oops: releases the register window
(:658) before bus_teardown_intr (:659) β live handler over unmapped
MMIO.
Build (guest, root)
cd /root/df801 && sh build.sh
# cc -O -Wall -o /tmp/fm801_probe fm801_probe.c ; runs the probe +
# captures pciconf/sndstat evidence
Run
sh run.sh # probe + full pciconf -l
Expected output
The probe prints the trigger preconditions and this guest's hardware absence:
NO 1319 PCI function on this guest no sndstat
The UAF itself needs a real FM801 (1319:0801) card whose attach passes mixer_init() then fails late β impossible under QEMU 7.2.22 (no FM801 device model). The source chain is walked line-by-line in VERDICT.md.
Fix validation
sh fix_validate.sh
# patches the guest /usr/src copy with fix.diff, builds snd_fm801.ko
# with the full kernel warning set (incl. -Werror) -- compiles clean;
# restores the pristine source afterwards (md5-verified).
DF-4651 β fm801: attach failure paths after mixer_init() leave a live 0666 /dev/mixerN over freed codec+softc β mixer-UAF family, 14th instance
Verdict: source chain fully walked (certain); runtime NOT testable on the audit guest (no FM801 hardware model in QEMU 7.2.22). Family precedent: DF-4156/4193/4299/4334/4344/4421/4457/4468/4504/4537/4550/4610.
Root cause β full source chain
sys/dev/sound/pci/fm801.c, fm801_pci_attach():
- The mixer is published early. After the register space is mapped (fm801.c:587-611) and the codec brought up:
- fm801.c:617 β
codec = AC97_CREATE(dev, fm801, fm801_ac97); - fm801.c:620 β
if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) goto oops;
mixer_init() creates the world-writable mixer cdev and binds the
codec to it: make_dev(&mixer_ops, PCMMKMINOR(unit,0,0), UID_ROOT,
GID_WHEEL, 0666, "mixer%d", unit); pdev->si_drv1 = m;
(sys/dev/sound/pcm/mixer.c:729-733), with m->devinfo = codec
(the AC97_CREATE product). From this point any user can open/ioctl
/dev/mixerN and dispatch into fm801 + codec.
- Three failure paths remain after it:
- fm801.c:625-627 β
bus_alloc_resource_any(SYS_RES_IRQ)orsnd_setup_intr()fails β "unable to map interrupt" βgoto oops - fm801.c:636-638 β
bus_dma_tag_create()fails β "unable to create dma tag" βgoto oops - fm801.c:646 β
pcm_register()fails βgoto oops(reachable: EINVAL whenpcm_veto_loadis set, sound.c:1021; ENODEV when PCMMAXUNIT is reached, sound.c:1027)
- oops: frees everything the live cdev points at.
- fm801.c:657 β
ac97_destroy(codec)βkobj_delete(codec->methods)kfree(codec)(sys/dev/sound/pcm/ac97.c:851-858)
- fm801.c:662 β
kfree(fm801, M_DEVBUF)
No pcm_unregister()/mixer_uninit() runs on this path (that is the
only thing that would retire the mixer cdev), so the 0666
/dev/mixerN stays registered with si_drv1 β freed snd_mixer,
m->devinfo β freed ac97_info whose kobj method table is deleted,
and the channel ops still reference the freed fm801_info. Any
later open/ioctl is a use-after-free with kobj indirect dispatch
(attacker-influenced heap reuse β memory corruption; family impact).
- Secondary defect on the same unwind: fm801.c:658 releases
fm801->reg(the bus space the interrupt handler immediately reads,fm801_intrβfm801_rd(FM_INTSTATUS)at fm801.c:292) beforebus_teardown_intrat fm801.c:659 β a live-handler window over an unmapped MMIO/IO window.
Trigger preconditions (why untested here)
Requires a ForteMedia FM801 (PCI 1319:0801) audio function whose attach
passes mixer_init() (AC97 codec answers the FM_CODEC_CMD protocol,
fm801.c:210-238) and then fails at IRQ allocation / dma-tag creation /
pcm_register. The audit guest runs QEMU 7.2.22 which has no FM801
device model (only AC97/ES1370/intel-hda/sb16): run.log shows the
full pciconf -l with no 1319 vendor function and no sndstat. A
hardware-bearing host (or PCI-passthrough of a flaky card, or IRQ-table
exhaustion during hotplug) can realize the path.
Fix
fix.diff (validated: compiles clean with the full kernel warning set
incl. -Werror as snd_fm801.ko on the guest /usr/src, see
fix_build.log):
- Move the IRQ setup (625-628) and dma-tag creation (630-639) and
pcm_register()(646) beforeAC97_CREATE/mixer_init, so no failure path exists after the mixer cdev is published;mixer_init()failure destroys the codec inline and theoops:path can never free an object a live/dev/mixerNpoints at. - Fix the unwind order:
bus_teardown_intrbefore releasingfm801->reg.
Same shape as the accepted DF-4610 (neomagic) family fix; FreeBSD upstream eliminated the class by making pcm_register void + reordering.
What was checked and is NOT broken (negative results)
fm801ch_setspeed(fm801.c:400): loop boundi < 10over the 11-entry rate table βfm801_rates[10]is the sentinel; no OOB.fm801ch_setblocksize/fm801ch_triggerDMALEN/DMABUF2: the pcm core clampshblksz <= sndbuf_getmaxsize>>1before calling the driver (sys/dev/sound/pcm/channel.c:1839-1841), bufsz β€ 65536 (fm801.c:613), soplay_nextblk = start + blksizestays inside the buffer and DMALEN fits the 16-bit register. No OOB DMA.fm801ch_setformatrec path usesFM_PLAY_16BIT(0x4000) instead ofFM_REC_16BIT(fm801.c:367) β same numeric value, benign.- AC97 access (fm801.c:210-274): regno bounded by the ac97 core; the 50Γ10ms busy-polls are a latency wart, not a security defect.
fm801_intrNULL-channel window betweensnd_setup_intr(625) andpcm_addchan(647) requires a spurious PLAY/REC status latch from well-behaved hardware β speculative only, not filed.- The
radiochild (fm801.c:651): no "radio" driver exists in this tree, the child never attaches;fm801_alloc_resourceonly hands outfm801->regβ dead path here. - Suspend/resume use
bus_generic_*only β functional gap (device not re-initialized after resume), no memory-safety impact. ar2316GetChannelMaxMinPower-style interpolation in the ath file was checked separately (see the ar2316 findings' packs).
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 reorders attach (IRQ, dma tag, pcm_register, then codec/mixer with mixer_init() checked and codec destroyed inline on its failure) and corrects the oops: release order (teardown_intr before reg release); compiles and links clean as snd_fm801.ko with the full kernel warning set incl. -Werror on the guest /usr/src (fix_build.log), pristine source restored md5-verified. Baseline vs patched behaviour is not observable on the audit guest: no 1319:0801 function exists there (QEMU 7.2 has no FM801 device model), so neither the buggy nor the fixed path can attach.
findings/poc/DF-4651/fix.diff; fix_build.log (patched module compile+link with -Werror, artifact snd_fm801.ko + fm801.o listed, restore md5s match); canonical unwind at sound.c pcm_unregister (mixer_uninit before codec teardown)
Confirmed kernel references
Detail
Evidence (decisive lines)
findings/poc/DF-4651/VERDICT.md (source chain + unwind walk + negative results); fm801_probe.c/run.log/env.txt (trigger preconditions + guest hardware absence); fix.diff + fix_build.log (patched snd_fm801.ko builds clean with -Werror on guest /usr/src; artifact list + pristine-restore md5 proof appended)
PoC changes
authored fresh probe (no seed); probe documents the absent trigger environment rather than the UAF itself; fix_validate.sh builds the patched module on the guest and md5-verifies the pristine restore
Verified recommended fix
Move IRQ setup, dma-tag creation and pcm_register() before AC97_CREATE/mixer_init, destroy the codec inline when mixer_init() fails, and tear the handler down before unmapping MMIO in oops: (see fix.diff)
Verdict
fm801_pci_attach() publishes the 0666 /dev/mixerN at mixer_init() (fm801.c:620; make_dev+si_drv1=m at mixer.c:729-733, m->devinfo=codec) and keeps three failure paths after it: IRQ allocation/snd_setup_intr failure (fm801.c:625-627), bus_dma_tag_create failure (fm801.c:636-638) and pcm_register() failure (fm801.c:646; EINVAL/ENODEV per sound.c:1021/1027). oops: (fm801.c:656-663) then ac97_destroy()s the codec (kobj_delete + kfree, ac97.c:851-858) and kfrees fm801 while the mixer cdev still dispatches open/ioctl into them -- use-after-free with kobj indirect dispatch, 14th instance of the DF-4156/4193/4299/4334/4344/4421/4457/4468/4504/4537/4550/4610 family. Secondary defect folded into the fix: oops: released fm801->reg (fm801.c:658) before bus_teardown_intr (fm801.c:659), leaving a live fm801_intr window over unmapped MMIO (fm801_intr reads FM_INTSTATUS at fm801.c:292 immediately). Untested rather than demonstrated: the trigger needs a real ForteMedia 1319:0801 function whose attach reaches mixer_init() and then fails; the audit guest runs QEMU 7.2.22 which has no FM801 device model (run.log: full pciconf -l shows no 1319 vendor function; no sndstat). Source chain fully walked in VERDICT.md. Fix compile-validated on guest /usr/src: patched snd_fm801.ko builds with the full kernel warning set incl. -Werror (fix_build.log); pristine source restored (md5-verified).
No comments yet.