via_attach() pcm_register()-failure path destroys the AC97 codec while the 0666 /dev/mixerN mixer_init() created survives β dangling mixer devinfo UAF on open/ioctl
Summary
via_attach ignores mixer_init's return, which make_dev's a world-writable 0666 /dev/mixerN whose si_drv1->devinfo is the just-created AC97 codec. It then retains a failure point: pcm_register returns EINVAL (veto_load stuck) or ENODEV (>255 units). On failure, bad: runs ac97_destroy (kobj_delete + kfree) while /dev/mixerN still dispatches ioctls into it - UAF with indirect-dispatch potential; no mixer_uninit ever runs because attach failed. The FOURTH instance of the attach-error mixer family (DF-4156/4193/4299). Boot-gated precondition. Reorder-pcm_register-first + check-mixer_init fix (git-apply clean).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4334 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.4 KB | β raw | |
| VERDICT.md | β | 4.1 KB | β raw | |
| via8233_probe.c | β | 2.4 KB | view raw | |
| build.sh | β | 111 B | view raw | |
| run.sh | β | 145 B | view raw | |
| build.log | β | 281 B | view raw | |
| run.log | β | 281 B | view raw | |
| env.txt | β | 696 B | view raw | |
| fix.diff | β | 995 B | view raw | |
| manifest.json | β | 1.3 KB | view raw | |
| verdict.json | β | 3.6 KB | view raw |
DF-4334 β via_attach() late-failure path frees the AC97 codec while mixer_init()'s 0666 /dev/mixerN survives (mixer UAF)
File: sys/dev/sound/pci/via8233.c Β· pass 2 (GLM 5.3) Β· family: attach-error-path
(DF-4156 maestro, DF-4193 es137x, DF-4299 emu10kx-pcm)
Build
# on the audit guest (root):
cc -O -Wall -o /root/via8233_probe /root/via8233_probe.c
Run
./dfbsd-qemu/vm.sh run_root '/root/via8233_probe'
pciconf -l | grep 1106:3059 # -> nothing: no VIA audio function
ls /dev/mixer* # -> nothing: no pcm unit exists
Expected
The probe cannot and does not trigger the UAF: the defect fires only when
pcm_register() fails after mixer_init() succeeded during
snd_via8233 attach. On this guest no 1106:3059 PCI function exists
(host QEMU 7.2.22 predates the via-ac97 emulation added in QEMU 8.2), and
the pcm_register() failure preconditions (pcm_veto_load stuck at 1 β
cleared only by successful feeder init at boot; or pcm unit > 255) are
boot-time anomalies an unprivileged user cannot create. The probe records
that negative environment; the finding is source-certain
(via8233.c:1335 vs via8233.c:1354-1373).
Fix
fix.diff β reorder pcm_register() before AC97_CREATE/mixer_init()
and check mixer_init()'s return, eliminating every failure path after
the mixer is published (mirrors FreeBSD upstream, where pcm_register()
became void and the late-failure path no longer exists).
VERDICT β DF-4334 (via8233.c attach-path mixer UAF)
Status: untested (attach-failure-gated, source-certain). Severity Low. Confidence certain.
The bug (source chain, every step cited)
via_attach()initializes hardware, then creates the codec:via->codec = AC97_CREATE(dev, via, via_ac97)β via8233.c:1331 (ac97.c:1012-1030:codec->devinfo = via, kobj methods allocated).mixer_init(dev, ac97_getmixerclass(), via->codec)β via8233.c:1335 β its return value is ignored. Inside mixer_init:m = mixer_obj_create(dev, cls, devinfo=codec, ...)stores the codec asm->devinfo(mixer.c:635 area) andmake_dev(&mixer_ops, ..., 0666, "mixer%d")publishes the world-writable node withpdev->si_drv1 = m(mixer.c:730-733).snddev->mixer_dev = pdev.- The next failure point is
pcm_register(dev, via, ..., NWRCHANS)β via8233.c:1354-1355 β which returns non-zero whenpcm_veto_load != 0(sound.c:1021-1025; the variable is initialized to 1 at sound.c:50 and cleared only by successful feeder registration at boot, feeder.c:120-126) or whendevice_get_unit(dev) > PCMMAXUNIT(=255, sound.c:1027-1034, sound.h:107). goto badunwinds withac97_destroy(via->codec)β via8233.c:1372-1373 β whichkobj_delete(codec->methods)s the dispatch table, frees the codec lock andkfree(codec, M_AC97)(ac97.c:852-858). Nomixer_uninit()runs anywhere on this path (device attach failed, sovia_detach/pcm_unregisterare never called; sound.c:1205 shows the canonical unwind doesmixer_uninitfirst).- Result: the 0666
/dev/mixerNremains registered forever withsi_drv1pointing at the freedsnd_mixerβ freeddevinfocodec β freed kobj method table. Any lateropen/ioctl on that node (e.g.SOUND_MIXER_READ_DEVSβmixer_ioctlβMIXER_GETDEV(m->devinfo)) is a use-after-free with indirect-dispatch potential throughkobj_deleted memory.
This is the same defect family the audit already verified by source in
maestro (DF-4156), es137x (DF-4193) and emu10kx-pcm (DF-4299); via8233.c
is a fourth instance that pass 1 missed. FreeBSD upstream (current head)
sidesteps the class by making pcm_register() void β its file has no
post-mixer_init failure path at all; DragonFly kept the check, keeping
the bug reachable.
Why untested (not false-positive)
- The trigger needs an actual VIA 1106:3059 audio function whose attach
runs to
mixer_initand then fails atpcm_register. The audit guest has no 1106:3059 function: host QEMU is 7.2.22 (-device helplists only AC97/ES1370/intel-hda) and the via-ac97 emulation (hw/audio/via-ac97.c, rev 0x50 β probe-match "VIA VT8235", config 0x40=1 codec-ready) only exists from QEMU 8.2. - Even with the hardware, both
pcm_register()failure preconditions are boot-time anomalies (pcm_veto_loadfeeder-init failure / >255 pcm units) that an unprivileged user cannot create β same verdict logic as DF-4156. Hence Low severity,impact: nonedemonstrated,confidence: certainin the source chain.
PoC artifacts
via8233_probe.c+build.log/run.log: guest-side proof of the negative environment (no mixer node, no 1106:3059, module absent).env.txt: guest uname, host QEMU version/device list.
Fix
fix.diff (git-apply-able against the read-only sys/ tree; never applied
there): move pcm_register() before AC97_CREATE/mixer_init() and
fail attach on mixer_init() error. After the reorder, no failure path
exists after the mixer is published, so the bad: unwind stays
symmetric β no mixer_uninit bookkeeping needed. Not validated on the
guest for the same hardware-absence reasons (fix_status: not_testable);
git apply --check clean against sys/dev/sound/pci/via8233.c.
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 pcm_register() ahead of codec creation/mixer_init and checks mixer_init(), removing every failure path after the mixer is published; git apply --check is clean. Baseline vs patched behaviour is not observable on the audit guest for the same hardware/precondition absence (no 1106:3059 function, pcm_register failure not creatable).
findings/poc/DF-4334/fix.diff; canonical unwind order shown at sound.c:1205 (pcm_unregister does mixer_uninit before codec teardown)
Confirmed kernel references
Detail
Evidence (decisive lines)
findings/poc/DF-4334/VERDICT.md (full source chain); via8233.c:1335 mixer_init ignored vs via8233.c:1354 pcm_register check vs via8233.c:1372 bad: ac97_destroy; mixer.c:730 make_dev 0666 + si_drv1=m(devinfo=codec); ac97.c:852 kobj_delete+kfree; sound.c:1021/1027 failure set; build.log/run.log (guest-side negative-environment probe); env.txt (QEMU 7.2.22 device list, no via-ac97)
PoC changes
authored fresh probe (no seed); probe documents the absent trigger environment rather than the UAF itself
Verified recommended fix
Move pcm_register() before AC97_CREATE/mixer_init() and fail attach on mixer_init() error so no failure path exists after the mixer is published
Verdict
via_attach() ignores mixer_init()'s return at via8233.c:1335 (which publishes a 0666 /dev/mixerN whose si_drv1->devinfo is the AC97 codec, mixer.c:730-733) and keeps a failure path after it: pcm_register() at via8233.c:1354 returns EINVAL/ENODEV when pcm_veto_load is stuck at its sound.c:50 initial value 1 (cleared only by successful boot-time feeder init, feeder.c:126) or when the pcm unit exceeds PCMMAXUNIT=255 (sound.c:1027). On that failure attach jumps to bad: and ac97_destroy(via->codec) at via8233.c:1372-1373 kobj_delete()s the method table and kfrees the codec (ac97.c:852-858) while /dev/mixerN still dispatches ioctls into it -- use-after-free with indirect-dispatch potential, identical to the DF-4156/DF-4193/DF-4299 family. Untested rather than demonstrated: the trigger needs a real VIA 1106:3059 audio function whose attach reaches mixer_init and then fails at pcm_register; the audit guest has no such function (host QEMU 7.2.22 predates the via-ac97 emulation added in QEMU 8.2 -- see env.txt) and both pcm_register failure preconditions are boot-time anomalies an unprivileged user cannot create.
No comments yet.