snd_vibes: sv_attach failure paths after mixer_init leave a live 0666 /dev/mixerN over released bus resources and leak sc (DF-4156 family, 12th instance)
Summary
mixer_init publishes the 0666 mixer cdev whose devinfo is the softc; four later failure paths goto fail which destroys the DMA tag and releases ALL bus resources but never calls mixer_uninit (only reachable via pcm_unregister, never run for a failed attach). The world-RW mixer survives over cached bus_space handles now pointing at rman-released I/O ports - unpriv MIXER_WRITE drives bus_space I/O on ports rman may have reassigned to another device; sc also leaked on every fail path. mixer_uninit-in-unwind + kfree fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4552 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 3.1 KB | β raw | |
| VERDICT.md | β | 4.2 KB | β raw | |
| mixer_probe.c | β | 2.1 KB | view raw | |
| build.sh | β | 94 B | view raw | |
| run.sh | β | 106 B | view raw | |
| build.log | β | 181 B | view raw | |
| run.log | β | 743 B | view raw | |
| env.txt | β | 418 B | view raw | |
| fix.diff | β | 1.3 KB | view raw | |
| manifest.json | β | 1.1 KB | view raw | |
| verdict.json | β | 2.7 KB | view raw |
DF-4552 β snd_vibes attach-failure paths leave a live 0666 /dev/mixerN over released bus resources (DF-4156 family, 12th instance)
What this is
sv_attach() (sys/dev/sound/pci/vibes.c) publishes the mixer at line 789
(mixer_init(dev, &sv_mixer_class, sc)), which β see
sys/dev/sound/pcm/mixer.c:730-733 β make_dev()s a mode 0666
/dev/mixerN cdev whose si_drv1->devinfo is the driver softc sc.
Four later failure paths
- vibes.c:815-821 resource-assumption check (
goto fail) - vibes.c:837-840 dmaa allocation (
goto fail) - vibes.c:856-859 dmac allocation (
goto fail) - vibes.c:872-875
pcm_registerfailure (goto fail)
unwind with fail: (vibes.c:888-901): the DMA tag is destroyed, the
interrupt is torn down and all bus resources (enh/dmaa/dmac) are
released β but mixer_uninit() is never called (pcm_unregister(), the
only caller of mixer_uninit β sound.c:1205 β is never reached on a failed
attach, and even a hypothetical later pcm_unregister would return early
because PCM_ALIVE(d) is false for a never-registered device). sc itself
is also leaked (no kfree, including the early ENXIO return at
vibes.c:746-749).
Result: after a post-mixer_init attach failure, /dev/mixerN stays
world-RW with devinfo = sc whose cached bus_space_tag/handle pairs point
at I/O port ranges rman no longer owns. Any local user (device is 0666
root:wheel) issuing mixer ioctls drives sv_mix_set() β
sv_indirect_get/set() β bus_space_read/write_1 on released ports, which
rman may have re-allocated to a different PCI device.
This is the 12th instance of the sound-family mixer-UAF/unwind-asymmetry family: DF-4156, DF-4193, DF-4299, DF-4334, DF-4344, DF-4421, DF-4457, DF-4468, DF-4504, DF-4537, DF-4550.
Reproduce (on hardware)
- Machine with an S3 SonicVibes (PCI 5333:ca00).
- Force
sv_attach()to fail aftermixer_init()β e.g. make the midi/games port-gap assumption fail (vibes.c:815-821) by having the PCI BIOS lay out the MIDI/GAMES BARs such that the gap is >= 0x800 or not 0x200-aligned, or patch a failure injection at vibes.c:874. - As an unprivileged user:
cc -O -o mixer_probe mixer_probe.c && ./mixer_probe /dev/mixer0β the MIXER_WRITE ioctl reachessv_mix_set()over released ports.
What was run on the audit QEMU guest (2026-09-21)
The guest has no SonicVibes device (pciconf -l | grep 5333 β no
match), so sv_attach never runs and the failure window cannot be entered.
The probe was built on the guest and run anyway, as root and as an
unprivileged user, to document the honest result:
/tmp/mixer_probe /dev/mixer0 β open: No such file or directory (no mixer0 published) /tmp/mixer_probe /dev/mixer β open: Bad file descriptor (default clone; zero pcm units registered β mixer_clone ENODEV path)
with snd_vibes.ko loaded (kldload snd_vibes), /dev/sndstat showing
Installed devices: (empty). Logs: build.log, run.log.
Verdict
not_reproduced β the defect is certain at source level (same proven
family pattern as 11 accepted instances), but this guest has no SonicVibes
PCI hardware, so the trigger cannot be entered. See VERDICT.md.
DF-4552 VERDICT β not_reproduced on this guest (hardware absent); source-level: certain
Narrative
sv_attach() ordering (sys/dev/sound/pci/vibes.c):
| step | line | effect |
|---|---|---|
| alloc sc | 730 | kmalloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO) β driver-private, freed only in sv_detach |
| irq setup | 763-770 | snd_setup_intr(dev, sc->irq, 0, sv_intr, sc, &sc->ih) |
| mixer_init | 789 | publishes 0666 /dev/mixerN, m->devinfo = sc (mixer.c:730-733) |
| dmaa hack | 823-848 | midi/games port-gap hack; goto fail on assumption violation (815-821) or alloc failure (837-840) |
| dmac hack | 850-867 | goto fail on alloc failure (856-859) |
| pcm_register | 872-875 | goto fail on failure |
fail: |
888-901 | destroys dmat, tears down irq, releases enh/dmaa/dmac resources β no mixer_uninit, no kfree(sc) |
mixer_uninit() (mixer.c:767) is only ever called from pcm_unregister()
(sound.c:1205). A failed attach means device_is_attached() is false, so
detach is never invoked, and even if it were, pcm_unregister returns 0
early because PCM_ALIVE(d) is false for the never-registered device β
so nothing ever destroys the mixer cdev. Same asymmetry as the 11
accepted family instances (DF-4156 β¦ DF-4550); in the other instances the
fail path additionally kfrees the softc making it a heap UAF β here the
softc leaks, so the live mixer drives bus_space_* on released I/O
port resources instead (rman may have re-assigned them).
Post-failure reachability for an unprivileged user: /dev/mixerN is
0666 root:wheel (mixer.c:730-731). MIXER_WRITE β mixer_ioctl β
sv_mix_set (vibes.c:541) β sv_gain β sv_channel_gain (vibes.c:483) β
sv_indirect_get/set (vibes.c:139/151) using sc->enh_st/enh_sh, which
were cached at vibes.c:750-751 from a resource released at vibes.c:896.
Also noted (folded into this finding): the early return ENXIO at
vibes.c:746-749 leaks sc outright (no resource cleanup needed there, but
no kfree), and every fail: path leaks sc.
Guest run (audit guest, 2026-09-21, uname in env.txt)
pciconf -l | grep 5333β no SonicVibes device present (QEMU/i440fx machine: only Intel PIIX + virtio + Red Hat VGA).kldload snd_vibessucceeds (module loads, probe finds nothing):/dev/sndstatβInstalled devices:(empty); no/dev/mixer0is ever created (mixer_init never runs)../mixer_probe /dev/mixer0(root and unprivileged):open: No such file or directory../mixer_probe /dev/mixer(the sound-core default clone node, sound.c:1426):open: Bad file descriptorβ with zero registered pcm devicesmixer_clone()(mixer.c:1340-1360) finds noPCM_REGISTEREDunit and returns ENODEV; harmless here.- Module unloaded again; guest left clean.
Incidental: the second unload cycle (without any cat /dev/sndstat in
between) panicked the guest in sndstat_sysuninit() β that is the
already-known DF-4116 (sndstat.c never-initialized sbuf deleted on
sound.ko unload), reproduced live and consistent with its existing
reproduced/panic verdict. Not a vibes.c defect; not re-filed here.
Bottom line
- Source-level defect: certain β identical, already-accepted family pattern; all four post-mixer_init failure paths enumerated.
- On this guest: not_reproduced β the PCI device does not exist, so the trigger window cannot be entered at all.
- Realistic impact ceiling on real hardware: unprivileged mixer ioctls poking I/O ports the driver no longer owns (worst case: another PCI device's registers), plus a permanent softc/mixer leak per failed attach. No kernel heap memory corruption (softc is leaked, not freed).
Fix validation
Not testable on this guest (no hardware). fix.diff is source-reviewed
only: mirror the family fix β unwind the mixer before releasing resources
on every failure path taken after mixer_init(), and free sc.
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 not validated on hardware: guest has no SonicVibes device, so neither baseline nor patched behavior is observable; fix.diff is source-reviewed and git-apply-verified only
fix.diff
Confirmed kernel references
Detail
Evidence (decisive lines)
run.log sections =B..=I= (pciconf has no 5333 device; mixer_probe ENOENT/EBADF as both root and unprivileged user); VERDICT.md table of the four failure paths; mixer.c:730-733 make_dev 0666; sound.c:1205 mixer_uninit only reachable via pcm_unregister
PoC changes
authored from scratch this pass (no seed; finding filed this pass)
Verified recommended fix
On every failure path after mixer_init call mixer_uninit(dev) before releasing bus resources, and kfree(sc) on all fail paths (see fix.diff)
Verdict
Source-level defect is certain (identical unwind asymmetry to 11 accepted family instances DF-4156..DF-4550): sv_attach publishes the 0666 /dev/mixerN at mixer_init (vibes.c:789) and four later goto-fail paths (815-821, 837-840, 856-859, 872-875) release all bus resources without mixer_uninit, leaving a world-RW mixer whose devinfo (sc) drives bus_space I/O on rman-released ports; sc is also leaked on every fail path and on the early ENXIO at 746-749. Not reproduced on the audit guest because no S3 SonicVibes PCI device exists there: probe built and run as root and unprivileged user, /dev/mixer0 absent (ENOENT), default /dev/mixer clone returns EBADF with zero registered units.
No comments yet.