t4dwave: attach-failure paths after mixer_init() leave a live 0666 /dev/mixerN over freed codec/tr_info β mixer-UAF family (9th instance)
Summary
mixer_init publishes the world-writable mixer cdev before three fallible steps (IRQ setup, dma tag, pcm_register); every failure goto frees the codec and tr_info without mixer_uninit (reachable only via pcm_unregister). Any subsequent unpriv mixer ioctl dispatches kobj methods on the freed codec over the freed tr_info whose lock was also freed. Same precondition class as the whole verified family. Fix is subtle: mixer_uninit itself destroys the codec, so bad: must call mixer_uninit INSTEAD of ac97_destroy (not both). mixinit-flag fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4504 Β· 7 files| File | Type | Description | Size | |
|---|---|---|---|---|
| t4dwave_probe.c | β | 820 B | view raw | |
| build.sh | β | 111 B | view raw | |
| run.sh | β | 100 B | view raw | |
| run.log | β | 95 B | view raw | |
| fix.diff | β | 1.2 KB | view raw | |
| env.txt | β | 449 B | view raw | |
| VERDICT.md | β | 2.9 KB | β raw |
DF-4504 β VERDICT
NOT DEMONSTRATED LIVE (hardware-blocked) β code-certain UAF, established 9-member family (DF-4156 maestro, DF-4193 es137x, DF-4299/4334/4344/4421/ 4457/4468). fix.diff applies clean; runtime fix validation impossible on this guest.
Root cause (path:line)
- t4dwave.c:880-882 β
AC97_CREATE()thenmixer_init(dev, ac97_getmixerclass(), codec); mixer.c:730-733 doesmake_dev(&mixer_ops, ..., 0666, "mixer%d")+si_drv1 = mwithm->devinfo = codec(mixer.c:635 via mixer_obj_create). - Still-failing paths after that point: t4dwave.c:887 (IRQ alloc / snd_setup_intr), t4dwave.c:920-922 (bus_dma_tag_create), t4dwave.c:928-929 (pcm_register).
- t4dwave.c:937-945
bad:βac97_destroy(codec)(938: kobj_delete of the method table + snd_mtxfree + kfree, ac97.c:852-861) andkfree(tr)(944), but the mixer cdev is never destroyed:mixer_uninit()is called only frompcm_unregister()(sound.c:1205), unreachable on failed attach. - Post-failure, any user opening the surviving world-writable
/dev/mixerNand issuing a mixer ioctl runs the kobj method dispatch (ac97mix_set/recsrc->tr_wrcd/tr_rdcd) on freed codec + freedtr_info(whosetr->lockwas alsosnd_mtxfreed at 943): UAF read/ write and indirect-dispatch control-flow potential.
Why not reproduced
The audit guest's QEMU emulates no Trident 4DWave DX/NX (1023:2000/2001),
SiS 7018 (1039:7018) or ALi M5451 (1102:5451) audio controller;
snd_t4dwave.ko exists in /boot/kernel but tr_pci_probe() returns ENXIO
for every emulated PCI id, so no /dev/mixerN from this driver exists
(run.log). Additionally the trigger itself requires a partially failing
attach (IRQ/dma-tag/pcm_register failure after a successful mixer_init) β
a boot-time resource anomaly an unprivileged user cannot create on demand.
Both limitations are shared with the filed family (DF-4156/DF-4193 were
verified "untested" for the same reasons). A logic-level harness cannot
honestly demonstrate a hardware-lifetime UAF, so no harness was faked.
Fix
fix.diff: track mixinit; on bad:, if the mixer was created call
mixer_uninit(dev) β which destroys the cdev, the snd_mixer, and the codec
itself (ac97mix_uninit -> ac97_destroy, ac97.c:1027-1039, mirroring the
pcm_unregister order at sound.c:1205) β otherwise ac97_destroy(codec) as
today. git apply --check clean against sys/. Note the subtlety this fixes
correctly: calling both mixer_uninit and ac97_destroy would double-free
the codec (mixer_uninit already destroys it).
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_testableNo trigger environment on the audit guest (no 4DWave-family controller; QEMU cannot emulate it; attach-failure precondition not creatable on demand), so baseline vs patched behaviour cannot be observed. fix.diff applies cleanly (git apply --check) and mirrors the pcm_unregister() unwind order.
findings/poc/DF-4504/fix.diff (git apply --check clean); sound.c:1205 canonical order
Confirmed kernel references
Detail
Evidence (decisive lines)
t4dwave.c:882 vs bad:-path 937-945 (mixer never uninitialized); mixer.c:730-733 (0666 make_dev + si_drv1=m, m->devinfo=codec); ac97.c:852-861 + ac97.c:1027-1039 (ac97_destroy frees the kobj; ac97mix_uninit shows mixer_uninit owns codec destruction); sound.c:1205 (mixer_uninit only via pcm_unregister); run.log (no t4dwave/mixer on guest)
PoC changes
authored fresh hardware-presence probe (no seed)
Verified recommended fix
On the bad: path, mixer_uninit(dev) when mixer_init() succeeded (destroys the 0666 cdev, snd_mixer and codec together); ac97_destroy(codec) only otherwise
Verdict
tr_pci_attach() publishes the 0666 /dev/mixerN (mixer_init, t4dwave.c:882; mixer.c:730-733) before three fallible steps (t4dwave.c:887 IRQ, 920 dma-tag, 928 pcm_register); every one of those gotos lands in bad: which ac97_destroy()s the codec (938, kobj_delete+kfree per ac97.c:852-861) and kfrees tr (944) while the mixer cdev survives β mixer_uninit() runs only from pcm_unregister() (sound.c:1205), unreachable on failed attach. Any subsequent unprivileged mixer ioctl dispatches kobj methods on the freed codec into tr_rdcd/tr_wrcd on the freed tr_info (whose lock was also freed at 943): UAF with indirect-dispatch potential. 9th instance of the filed family (DF-4156 maestro, DF-4193 es137x, DF-4299/4334/4344/4421/4457/4468). Untested because the audit guest has no Trident/SiS/ALI 4DWave controller (run.log: no /dev/mixer*, no matching PCI ids; QEMU cannot emulate this hardware) and the precondition is a boot-time attach failure not creatable on demand β identical blockers to DF-4156/DF-4193.
No comments yet.