atiixp_chip_post_init mixer_init/pcm_register error-path asymmetry leaves a live 0666 /dev/mixerN over freed codec+softc β UAF on open/ioctl and NULL-deref suspend (DF-4156 family, 5th instance)
Summary
mixer_init publishes a world-writable /dev/mixerN pointing at the AC97 codec before pcm_register can still fail; the error path then ac97_destroys the codec and kfrees the softc while the mixer cdev and the still-attached device keep pointing at freed memory. Any later mixer open/ioctl dispatches through the kobj_delete'd methods (UAF); suspend runs on the devinfo pcm_register never stored (NULL-deref); detach leaks the cdev permanently. Attach-failure-gated (Low per family). mixer_uninit-first fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4344 Β· 7 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.9 KB | β raw | |
| VERDICT.md | β | 7.1 KB | β raw | |
| guest_check.log | β | 782 B | view raw | |
| panic_df4116_on_atiixp_unload.txt | β | 1.3 KB | view raw | |
| fix.diff | β | 1.1 KB | view raw | |
| manifest.json | β | 959 B | view raw | |
| verdict.json | β | 3.7 KB | view raw |
DF-4344 β atiixp_chip_post_init mixer/pcm_register error-path asymmetry (DF-4156 family, 5th instance)
What this is
sys/dev/sound/pci/atiixp.c orders device bring-up as:
mixer_init(sc->dev, ac97_getmixerclass(), sc->codec)β atiixp.c:1085 β publishes a world-writable (0666)/dev/mixerNcdev whosesi_drv1devinfo chain points at the AC97 codec and, through the pcm softc, atsc(mixer.c:687-737).pcm_register(...)β atiixp.c:1087 β can still fail (pcm_veto_loadEINVAL, sound.c:1021-1026; unit > PCMMAXUNIT ENODEV, sound.c:1028-1035).- On failure:
goto postinitbadβatiixp_release_resource(sc)(atiixp.c:1113-1115) βac97_destroy(sc->codec)(atiixp.c:1129-1132, kobj_delete + kfree) andkfree(sc, M_DEVBUF)(atiixp.c:1165) β while the mixer cdev created in step 1 stays registered forever.
The device also remains attached (attach already returned 0 β post_init
runs from a config_intrhook when cold, atiixp.c:1293-1300), so
suspend/resume later run against the freed sc (pcm_getdevinfo
returns the NULL/garbage devinfo left by the failed pcm_register;
atiixp_lock(NULL) in atiixp_pci_suspend atiixp.c:1337 is a NULL-deref
panic), and detach never tears the mixer down.
Same construct as the four verified family members: DF-4156 (maestro), DF-4193 (es137x), DF-4299 (emu10kx-pcm), DF-4334 (via8233). This file is the 5th instance, previously unfiled.
Reproduce (needs ATI IXP hardware)
Trigger: any machine with an ATI IXP 200/300/400/SB600 AC97 controller
(PCI 1002:4341/4361/4370/4382) where pcm_register fails after
mixer_init succeeded β e.g. boot with the sound feeder init failed
(pcm_veto_load stuck β 0) or unit count above hw.snd.maxunit.
Then, as any local user: open("/dev/mixer0") + SNDCTL_MIXER_* ioctl β
kernel operates on freed sc/codec (UAF; mixer dispatch through a
kobj_deleted methods table).
Success criterion: use-after-free fault / adjacent-heap corruption in
mixer_ioctl paths, or (suspend path) NULL-deref panic in
atiixp_pci_suspend.
On this audit guest
Not runtime-testable: QEMU 7.2 does not emulate the ATI IXP AC97
controller. guest_check.log documents: module loads, probe returns ENXIO
for every PCI device, no pcm device attaches, so the buggy path never
executes. Source-level certainty only (identical construct to 4 verified
family members).
panic_df4116_on_atiixp_unload.txt: incidental capture β unloading
snd_atiixp on this guest panics in sndstat_sysuninit
(known, already-filed DF-4116; not re-reported here). Kept as context:
the module-unload route to exercise teardown is itself blocked by that bug.
Fix
fix.diff β tear the mixer down (which destroys both the cdev and, via
ac97mix_uninit β ac97_destroy, the codec) before
atiixp_release_resource, and keep release_resource from destroying the
codec a second time.
DF-4344 VERDICT
Status: inconclusive (kernel runtime hardware-gated β no ATI IXP audio controller exists on the audit guest; defect certain at source level, the 5th verified instance of the DF-4156 family).
Root cause (code-proven)
atiixp_chip_post_init (atiixp.c:1001-1115):
1085: mixer_init(sc->dev, ac97_getmixerclass(), sc->codec); <-- publishes 0666 /dev/mixerN 1087: if (pcm_register(sc->dev, sc, ATI_IXP_NPCHAN, ATI_IXP_NRCHAN)) 1088: goto postinitbad; <-- still-failing step ... 1113: postinitbad: 1114: atiixp_release_resource(sc); <-- frees codec + sc
mixer_init (mixer.c:687-737) does make_dev(&mixer_ops, ..., 0666,
"mixer%d", unit) and stores the mixer (whose devinfo is the codec) in
pdev->si_drv1. Nothing in the failure path removes that cdev.
atiixp_release_resource then does ac97_destroy(sc->codec)
(atiixp.c:1129-1132; kobj_delete(codec->methods) + kfree(codec),
ac97.c:852-859) and kfree(sc, M_DEVBUF) (atiixp.c:1165).
Result: any later open/ioctl on /dev/mixerN reaches
mixer_ioctl β mix_getdevinfo(m) β freed codec, with dispatch through
the deleted kobj methods table (indirect-call UAF), plus snddev->β¦
state pointing at freed sc.
Secondary consequences of the same root:
- The device stays attached (attach returned 0 at atiixp.c:1302 long
before the delayed hook runs, and even the synchronous cold==0 branch
returns 0). atiixp_pci_suspend (atiixp.c:1337) does
atiixp_lock(sc) on the devinfo pcm_register never stored β
pcm_getdevinfo NULL β snd_mtxlock(NULL) β NULL-deref panic.
- atiixp_pci_detach (atiixp.c:1315-1321) only unregisters when
sc->codec != NULL; on this path it reads the stale softc pointer and
either skips cleanup (mixer cdev + devnode leak, permanent UAF) or
touches freed memory.
pcm_register failure modes on this path (sound.c:1016-1035):
pcm_veto_load (EINVAL β feeder init failure at boot) or
device_get_unit(dev) > PCMMAXUNIT (ENODEV). Both are operator/config
controlled, not attacker-on-demand β hence Low severity, matching the
family (DF-4156/4193/4299/4334 all Low).
Reproduction attempt on the audit guest
Guest: DragonFly 6.5-DEVELOPMENT X86_64_GENERIC (env below), QEMU 7.2
class emulator. pciconf -l: no vendor 0x1002 device of any kind β the
ATI IXP 200/300/400/SB600 AC97 controllers (1002:4341/4361/4370/4382)
are not emulated. Probe evidence (guest_check.log):
kldload snd_atiixpsucceeds (registers the driver),devinfoshows NO_PCM_DEVICE_ATTACHED βatiixp_pci_probereturns ENXIO for every PCI device,atiixp_pci_attachnever runs, so the buggypostinitbadpath is unreachable on this guest.
Therefore: kernel-runtime reproduction impossible here (hardware-gated), same classification the family got; the code defect itself is certain β byte-for-byte the same failure-ordering construct already verified in four sibling drivers.
Incidental capture (panic_df4116_on_atiixp_unload.txt): attempting to
unload the module to at least exercise teardown panics in
sndstat_sysuninit β that is known DF-4116 (already filed against
sys/dev/sound/pcm/sndstat.c), not re-reported here.
Fix validation
fix.diff (git apply --check OK against the pristine tree):
tear the mixer down before releasing everything it points at:
postinitbad:
if (mixer_created && mixer_uninit(sc->dev) == 0)
sc->codec = NULL; /* mixer_uninit ac97_destroy'd it */
atiixp_release_resource(sc);
mixer_uninit (mixer.c:767-822) does destroy_dev(pdev) and
MIXER_UNINIT(m) β ac97mix_uninit β ac97_destroy(codec)
(ac97.c:1030-1038) β i.e. it removes the dangling cdev AND the codec
exactly once; NULLing sc->codec keeps atiixp_release_resource's
ac97_destroy (atiixp.c:1129) from double-freeing. A kernel-build
validation was not possible: building and booting an atiixp-attachable
kernel requires the physical controller (probe gates attach), which the
guest cannot emulate. fix_status: inconclusive (not
hardware-demonstrable here), logic reviewed against the mixer.c/ac97.c
teardown paths.
Negative results (pass-2 depth on atiixp.c, classes hunted & killed)
- DMA descriptor ring geometry (atiixp.c:1272-1287, 474-478,
579-603): allocation is
CHSEGS_MAX(256) * NCHANS(2) * 12ops;atiixp_chan_initslices per channel atnum*256withregistered_channelsonly ever reaching NCHANS=2 (pcm_addchan called exactly NPCHAN+NRCHAN=2 times, atiixp.c:1090-1093; vchans use their own class).buildsgdtwrites< blkcnt β€ 256entries per slice β in bounds.nextpointers stay inside the slice. u16sizefield: blksz>>2 β€ 16384 fits (polling mode single-descriptor case included). CLEAN. - Fragment/buffer math (atiixp.c:529-566): blksz aligned/ clamped to
[32, maxsize/2], blkcnt to [2,256]; halving loop guarantees
blkszblkcnt β€ maxsize before
sndbuf_resize, andsndbuf_resize(buffer.c:161-205) independently rejects blkszblkcnt>maxsize / blkcnt<2 / blksz<16 β no div0, no oversize descriptors.sc->blkcnthint path (atiixp.c:1236-1252): ablocksizehint larger than bufsz makes blkcnt=0 β1 << (i-1)with i=0 is UB, but on x86 it yields 0x80000000 which the subsequent unsigned clamp maps to CHSEGS_MAX; root-only tunable; no memory-safety impact. KILLED. - pollticks division (atiixp.c:741-743, 783-785): divisor is
align*spd; speed is pinned to 48000 by
atiixp_chan_setspeed(atiixp.c:522-527) and align β {1,2,4,8} post-format β the pcm core negotiates format/speed before any trigger. Not reachable with 0. KILLED. - Codec ready waits (atiixp.c:360-405): all bounded (500 Γ DELAY(1) in waitready, 500 in rdcd's data phase) β bounded, under lock, fine. CLEAN.
- SPDIF paths: no spdif channel/parse code exists in this driver; only register-bit mentions (atiixp.c:235-236, 813-814, 947). N/A.
- Suspend/resume (atiixp.c:1330-1404):
atiixp_pci_suspendcomputesvalue |= POWERDOWN|AC_RESETbut writes onlyATI_REG_CMD_POWERDOWN(atiixp.c:1354-1355) β cosmetic logic wart (AC_RESET never written), no security impact. The NULL-scuttled suspend entry is part of THIS finding's root.mixer_reinitfailure returns ENXIO leaving interrupts disabled β availability-only, same attach-failure precondition. NOTED, not filed. - detach leak symmetry (atiixp.c:1310-1328): consistent with family;
pcm_unregister(sound.c:1205) performs mixer_uninit internally, so the normal detach path does not double-free. CLEAN.
Bottom line
Certain, Low (attach-failure-gated, not user-triggerable on demand), 5th instance of the DF-4156 family in sys/dev/sound/pci/. Runtime proof requires IXP hardware the guest cannot emulate; defect and fix are code-certain by symmetry with the four verified siblings.
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
inconclusivefix.diff (git apply --check OK) tears the mixer down before release; mixer_uninit semantics verified against mixer.c:767-822 (destroy_dev + MIXER_UNINIT -> ac97_destroy). A patched-kernel run is impossible on this guest: atiixp_pci_attach requires the IXP controller the guest cannot emulate, so baseline/patched kernel behavior is not demonstrable here.
fix.diff; VERDICT.md 'Fix validation' section citing mixer.c:767-822 and ac97.c:1030-1038
Confirmed kernel references
- sys/dev/sound/pci/atiixp.c:1085
- sys/dev/sound/pci/atiixp.c:1087
- sys/dev/sound/pci/atiixp.c:1113
- sys/dev/sound/pci/atiixp.c:1129
- sys/dev/sound/pci/atiixp.c:1165
- sys/dev/sound/pci/atiixp.c:1337
- sys/dev/sound/pcm/mixer.c:687
- sys/dev/sound/pcm/mixer.c:731
- sys/dev/sound/pcm/ac97.c:852
- sys/dev/sound/pcm/ac97.c:1037
- sys/dev/sound/pcm/sound.c:1016
Detail
Evidence (decisive lines)
VERDICT.md root-cause table (atiixp.c:1085-1115, mixer.c:687-737, ac97.c:852-859); guest_check.log (no ATI hardware, no attach); panic_df4116_on_atiixp_unload.txt (known DF-4116); fix.diff (mixer_uninit teardown, git apply --check OK)
PoC changes
no seed; family PoC shape (DF-4156/4334) adapted: trigger requires IXP hardware, guest probe substituted
Verified recommended fix
On the postinitbad path call mixer_uninit(sc->dev) (destroys /dev/mixerN and, via ac97mix_uninit, the codec) before atiixp_release_resource, and NULL sc->codec to prevent the double ac97_destroy
Verdict
Code defect certain, kernel runtime hardware-gated: atiixp_chip_post_init() orders mixer_init() (atiixp.c:1085, publishing a 0666 /dev/mixerN whose si_drv1 devinfo is the AC97 codec) before pcm_register() (atiixp.c:1087, which can still fail EINVAL/ENODEV via pcm_veto_load or PCMMAXUNIT, sound.c:1021-1035); the postinitbad path then frees the codec (ac97_destroy, atiixp.c:1129-1132) and sc (atiixp.c:1165) while leaving the mixer cdev and the still-attached device pointing at freed memory -- any later mixer open/ioctl is a UAF through a kobj_delete'd methods table, and suspend/resume NULL-deref through the devinfo pcm_register never stored. Identical construct to the four verified family members (DF-4156 maestro, DF-4193 es137x, DF-4299 emu10kx-pcm, DF-4334 via8233); this is the 5th instance. Not runtime-reproducible on the audit guest: QEMU has no ATI IXP AC97 controller (1002:4341/4361/4370/4382 absent, guest_check.log: kldload snd_atiixp loads but no pcm device attaches -- probe returns ENXIO). Incidental: kldunload of the module on the guest panics in sndstat_sysuninit, which is KNOWN DF-4116 (not re-reported).
No comments yet.