cs4281 attach ignores mixer_init()/pcm_addchan() failures; half-attached device NULL-derefs in cs4281_pci_resume (ch->parent NULL and mixer_dev NULL)
Summary
Three returns ignored; attach reports success via pcm_setstatus. If chan_init's sndbuf_alloc fails, the channels keep parent==NULL; if mixer_init failed, mixer_dev stays NULL - a later suspend/resume panics twice over (cs4281_rd with sc==NULL; mixer_reinit on the NULL devt). Root-gated DoS (DMA pressure + resume). Check-returns + resume-backstop fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4539 Β· 12 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.1 KB | β raw | |
| VERDICT.md | β | 2.5 KB | β raw | |
| cs4281_probe.c | β | 2.0 KB | view raw | |
| build.sh | β | 133 B | view raw | |
| run.sh | β | 138 B | view raw | |
| build.log | β | 94 B | view raw | |
| run.log | β | 428 B | view raw | |
| env.txt | β | 604 B | view raw | |
| wedge_episode.txt | β | 777 B | view raw | |
| fix.diff | β | 504 B | view raw | |
| manifest.json | β | 1016 B | view raw | |
| verdict.json | β | 3.4 KB | view raw |
DF-4539 β cs4281 attach ignores mixer_init()/pcm_addchan() failures; half-attached device NULL-derefs on suspend/resume
What this is
cs4281_pci_attach() ignores three return values and advertises success
unconditionally:
mixer_init(dev, ...)at cs4281.c:845 β return ignored. If it fails,snddev->mixer_devstays NULL, yet attach succeeds.pcm_addchan(dev, PCMDIR_PLAY, ...)(cs4281.c:850) andpcm_addchan(dev, PCMDIR_REC, ...)(cs4281.c:851) β returns ignored.pcm_addchanfails (sound.c:1125-1141) whenchn_initβcs4281chan_initfails, which happens whensndbuf_alloc()fails (cs4281.c:326-328). That early return leaves the embeddedsc->pch/sc->rchwithch->parent == NULL,ch->channel == NULL(set only at cs4281.c:329-330/337, after the failure return).
The driver then calls pcm_setstatus and reports the device healthy.
Consequences (both root-gated: ACPI suspend/resume)
cs4281_pci_resume()(cs4281.c:941-949) unconditionally invokescs4281chan_setspeed(NULL, &sc->rch, ...)etc. βadcdac_go(ch, 0)(cs4281.c:474-479) βcs4281_rd(sc, ...)withsc = ch->parent == NULLβ NULL-pointer dereference panic.cs4281_pci_resumeβmixer_reinit(dev)(cs4281.c:935) β mixer.c:828-829:pdev = mixer_get_devt(dev)returnssnddev->mixer_dev(NULL after the ignoredmixer_initfailure, mixer.c:118-124) and is derefencedpdev->si_drv1without a check β NULL-pointer dereference panic.
No unprivileged trigger: suspend/resume requires root. Severity Low (robustness/DoS under resource pressure at attach + later suspend).
Build / run
./build.sh # guest build of the environment probe ./run.sh # documents: no CS4281 hardware on this guest
Fix
fix.diff adds defensive guards to cs4281_pci_resume (skip restore
when either channel never initialized). The attach-side checks for
mixer_init/pcm_addchan returns + bad_unregister: unwind are in the
DF-4537 fix (same attach function); this diff is the resume-side
backstop. git apply --check clean; never applied to sys/.
DF-4539 VERDICT β untested (hardware-gated), analysis certain
Source chain
mixer_init()return ignored: cs4281.c:845.pcm_addchan()returns ignored: cs4281.c:850-851.pcm_addchanfails whenchn_initfails (sound.c:1125-1141);cs4281chan_initfails whensndbuf_allocfails (cs4281.c:326-328) and returns BEFOREch->parent = sc; ch->channel = c; ch->dma_chan = ...(cs4281.c:329-337), so the embedded channel structs keepparent == NULLfrom the M_ZERO'd softc (cs4281.c:765).- Despite both failures the attach path continues to
pcm_setstatus(cs4281.c:856) and returns 0. - On a later suspend/resume:
*
cs4281_pci_resume(cs4281.c:941-949) callscs4281chan_setspeed(NULL, &sc->rch, sc->rch.spd)βadcdac_go(ch, 0)(cs4281.c:474-479) βcs4281_rd(sc, ...)(cs4281.c:146-150) withsc = ch->parent == NULLβ NULL deref (readssc->stat offset of bus_space tag) β panic. *mixer_reinit(dev)at cs4281.c:935 β mixer.c:828-829pdev = mixer_get_devt(dev); m = pdev->si_drv1;wheremixer_get_devt(mixer.c:118-124) returnssnddev->mixer_dev== NULL when the ignoredmixer_initfailed β NULL deref panic.
Preconditions
sndbuf_allocfailure (busdma/DMA memory pressure at attach) for the addchan leg, or anymixer_initfailure for the mixer leg;- then ACPI suspend+resume (root).
No unprivileged path exists; both legs need root to trigger. Low.
Why untested
The audit guest has no vendor-1013 CS4281 function (env.txt) β the attach, hence both failure legs, cannot occur; suspend of a nonexistent device is equally unreachable. Analysis is line-verified; classification untested, confidence certain.
Fix validation
fix.diff guards cs4281_pci_resume to fail cleanly when either
channel never initialized (sc->rch.parent == NULL || sc->pch.parent ==
NULL β ENXIO before mixer_reinit/channel restore). The attach-side
half (check mixer_init/pcm_addchan, unwind via pcm_unregister)
ships in the DF-4537 fix; both apply clean (git apply --check).
Not observable on this guest β fix_status not_testable.
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 adds the resume-side guards (fail ENXIO before mixer_reinit/channel restore when sc->rch.parent or sc->pch.parent is NULL); git apply --check clean. Combined with the DF-4537 attach reorder, no half-attached device can reach resume. Not observable on this guest (no CS4281 hardware).
findings/poc/DF-4539/fix.diff; attach-side half in findings/poc/DF-4537/fix.diff
Confirmed kernel references
Detail
Evidence (decisive lines)
findings/poc/DF-4539/VERDICT.md; cs4281.c:845 ignored mixer_init, cs4281.c:850-851 ignored pcm_addchan, cs4281.c:326-337 early-return-before-parent-init, cs4281.c:941-949 unconditional resume restore, cs4281.c:474-479 adcdac_go parent deref, mixer.c:828-829 mixer_reinit NULL pdev, sound.c:1125-1141 pcm_addchan failure semantics; run.log/env.txt
PoC changes
no seed; environment probe shared with DF-4537 (same trigger-hardware absence)
Verified recommended fix
Check mixer_init() and pcm_addchan() returns and unwind via pcm_unregister on failure (DF-4537 fix.diff), plus guard cs4281_pci_resume to return ENXIO when either embedded channel never initialized
Verdict
cs4281_pci_attach() ignores the return of mixer_init() (cs4281.c:845) and of both pcm_addchan() calls (cs4281.c:850-851), then reports the device healthy via pcm_setstatus (cs4281.c:856). If sndbuf_alloc() fails inside cs4281chan_init (cs4281.c:326-328) the embedded sc->pch/sc->rch keep parent==NULL (set only later at cs4281.c:329-337), and if mixer_init() fails snddev->mixer_dev stays NULL. A later suspend/resume then NULL-derefs twice: cs4281_pci_resume (cs4281.c:941-949) calls cs4281chan_setspeed -> adcdac_go (cs4281.c:474-479) -> cs4281_rd with sc==ch->parent==NULL (cs4281.c:146-150), and mixer_reinit (cs4281.c:935) derefs mixer_get_devt()'s NULL return at mixer.c:828-829 (snddev->mixer_dev per mixer.c:118-124). Guaranteed panic when the half-attach condition holds; root-gated (ACPI suspend), plus resource-pressure precondition, so Low. Untested: needs a CS4281 PCI function whose attach half-fails then a suspend cycle; the audit guest has no vendor-1013 function (env.txt) so neither the failure legs nor suspend of this device can occur.
No comments yet.