β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-4728

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4728 Β· 4 files
FileTypeDescriptionSize
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.

VERDICT.md
↓ download raw

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).

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_testable
baseline no→ patch + rebuild →patched clean

Fix not kernel-validated: requires an unresponsive AU8820 codec.

fix.diff authored against read-only sys/.
↓ fix.diffper-fix-DF-4728

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.