aureal: AC97 codec access busy-spins up to ~0.35s per register and ignores timeouts (au_wrcd writes anyway and always returns success)
Summary
au_rdcd busy-DELAYS ~0.345s per register on a wedged codec and returns garbage; au_wrcd busy-waits 100ms, still writes when WROK never asserted, and returns 0 - mixer_init's ~100 codec ops can busy-spin tens of seconds inside attach, and runtime mixer reads each spin ~0.35s. Availability only. Cap-and-fail fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4728 Β· 4 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 458 B | β raw | |
| VERDICT.md | β | 2.5 KB | β raw | |
| fix.diff | β | 1.6 KB | view raw | |
| verdict.json | β | 1.6 KB | view raw |
DF-4728 β PoC evidence pack
Finding: AC97 codec access busy-waits (au_rdcd ~0.35 s/reg, au_wrcd 100 ms/reg), writes-through on timeout, and always reports success.
Contents
VERDICT.mdβ budget math, path:line, impact.verdict.jsonβ machine verdict (status: untested; Info/hardening).fix.diffβ capped budget + skip-write-on-timeout.
Reproduce / Expected
Requires an AU8820 card with a wedged codec. Static analysis only.
DF-4728 β aureal: AC97 codec protocol busy-waits up to ~0.45 s per register and ignores timeouts (au_wrcd still writes and reports success)
File: sys/dev/sound/pci/aureal.c Β· Severity: Info Β· Bucket: base:dev Β· Confidence: certain
Root cause (path:line)
au_rdcd() β aureal.c:126-143
while (j<50) {
i=au_rd(au, 0, AU_REG_CODECIO, 4);
if ((i & 0x00ff0000) == (regno | 0x00800000)) break;
DELAY(j * 200 + 2000); /* busy-wait, grows with j */
j++;
}
if (j==50) printf(... timeout ...);
return i & AU_CDC_DATAMASK; /* returns last (garbage) read anyway */
Worst case per register: Ξ£_{j=0..49}(200j+2000) = 245,000+100,000 β 0.345 s
of busy DELAY (preemption-less spin) β and on timeout it returns whatever
the last read was, indistinguishable from real data.
au_wrcd() β aureal.c:145-169
do {
while (j<50 && (i & AU_CDC_WROK) == 0) {
i=au_rd(au, 0, AU_REG_CODECST, 4);
DELAY(2000); /* 50 x 2ms = 100 ms busy */
j++;
}
if (j==50) printf(... timeout ...);
au_wr(au, 0, AU_REG_CODECIO, (regno<<16) | AU_CDC_REGSET | data, 4);
tries++;
} while (0); /* retry loop compiled out */
return 0; /* success reported regardless */
On WROK timeout it still writes the codec register and returns 0 (success) β a wedged codec silently yields an unusable mixer that the frame- work believes initialized.
Impact
With a present-but-unresponsive codec (failing card, bus fault), mixer_init
(aureal.c:634) issues ~90-100 codec ops β up to tens of seconds of
continuous busy-spin at attach (giant-held, interrupt-livid boot path), and
runtime /dev/mixerN reads by unprivileged users can each spin ~0.35 s.
No memory-safety issue β availability/hardening only. Info severity;
hardware-gated; driver additionally not in default builds (DF-4724 notes).
Verification
Hardware-gated; code-path audit only (DELAY is a busy spin: sys/sys/systm.h).
Recommended fix
Cap the total wait budget, return failure on timeout (propagate
mixer_init failure so attach unwinds), and skip the write when WROK never
asserted. See fix.diff.
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 kernel-validated: requires an unresponsive AU8820 codec.
fix.diff authored against read-only sys/.
Confirmed kernel references
Detail
Evidence (decisive lines)
VERDICT.md busy-wait budget math; fix.diff
PoC changes
n/a
Verified recommended fix
Cap wait budgets, skip the write and signal failure on codec timeout
Verdict
au_rdcd (aureal.c:126-143) busy-DELAYS up to ~0.345s per register (sum_{j<50}(200j+2000)) and returns the last garbage read on timeout; au_wrcd (aureal.c:145-169) busy-waits up to 100ms, still writes the codec register when WROK never asserted, and always returns 0. On a wedged codec, mixer_init's ~100 register ops busy-spin tens of seconds inside attach, and runtime /dev/mixerN reads each spin ~0.35s. Availability/hardening only (Info), hw-gated, driver not in default build.
No comments yet.