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

snd_hdspe PCM: hdspechan_setspeed() read-modify-writes the shared ctrl_register and writes CONTROL/FREQ registers without sc->lock β€” cross-channel lost-update race (interrupt storm / stuck-enable / rate scramble)

Summary

setspeed does an unlocked RMW of the shared ctrl_register and writes the card's CONTROL/FREQ regs with no snd_mtxlock, while every sibling path (setblocksize, trigger) is serialized by sc->lock; play and rec are distinct channels with distinct CHN locks, so a setspeed on one races trigger/setblocksize on the other - a lost update can resurrect ENABLE|AUDIO_INT_ENABLE after stop (interrupt storm) or scramble latency/frequency bits. RME hardware-gated. Take-the-lock fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4700 Β· 2 files
FileTypeDescriptionSize
README.md file 2.2 KB ↓ raw
verdict.json file 2.0 KB view raw
README.md file
↓ download raw

DF-4700 β€” snd_hdspe PCM: hdspechan_setspeed() mutates shared ctrl_register/PCI config without sc->lock β€” cross-channel lost-update race

Where

sys/dev/sound/pci/hdspe-pcm.c:440-503 (hdspechan_setspeed) vs. the locked siblings hdspechan_setblocksize (:547-552), hdspechan_trigger→hdspe_start_audio/hdspe_stop_audio (:222-239, lock at :354), hdspechan_enable (:172-188).

Trace

  • hdspechan_setspeed() performs unlocked read-modify-write of the shared sc->ctrl_register (:486-487), writes HDSPE_CONTROL_REG (:488) and HDSPE_FREQ_REG (:498), and stores sc->speed (:500) β€” no snd_mtxlock(sc->lock) anywhere in the function, in contrast to setblocksize which takes the lock for the same operation.
  • The PCM framework calls channel ops under the per-channel CHN_LOCK; play and record channels are different pcm_channel objects. A rec channel's setspeed (SNDCTL_DSP_SPEED on /dev/dspN) can therefore interleave with a play channel's PCMTRIG_START/STOP (hdspe_start_audio/hdspe_stop_audio RMW the same register under sc->lock) and with setblocksize (latency bits in the same register).
  • Lost update example: setspeed's ctrl_register &= ~HDSPE_FREQ_MASK; |= rate (:486-487) races stop_audio's &= ~(AUDIO_INT_ENABLE|ENABLE) (:237) β†’ the enable bits are resurrected (or the frequency bits clobbered) in the merged value β†’ stuck interrupts (interrupt storm from a card that keeps asserting) or dead channels; sc->speed/ DDS-register desync produces wrong sample rates. Not memory-unsafe, but a genuine unsynchronized shared-state race on a multi-channel device (RayDAT exposes 19 stereo pairs across 8 pcm children, hdspe.c:60-85).

Threat

Local unprivileged user with access to the dsp devices (operator group): audio DoS / interrupt storm on real RME HDSPe hardware. No kernel memory-safety impact demonstrated.

PoC

Not runnable on the audit guest: hdspe(4) requires real RME HDSPe AIO/RayDAT PCI hardware (PCI ID 10ee:3fc6); no QEMU device model exists. untested (analysis-certain).

Fix sketch

Wrap the register-update tail of hdspechan_setspeed() (:486-500) in snd_mtxlock(sc->lock) / snd_mtxunlock(sc->lock), matching hdspechan_setblocksize.

Fix verification

not_testable
baseline no→ patch + rebuild →patched clean

requires physical RME HDSPe AIO/RayDAT hardware (PCI 10ee:3fc6); no QEMU device model exists

per-fix-DF-4700

Confirmed kernel references

Detail

Evidence (decisive lines)

findings/poc/DF-4700/README.md; hdspe-pcm.c:486-498 vs :547-552 and :222-239

PoC changes

no seed; analysis-only pack

Verified recommended fix

Take snd_mtxlock(sc->lock) around the ctrl_register/FREQ_REG update tail of hdspechan_setspeed(), mirroring hdspechan_setblocksize().

Verdict

Analysis-certain locking defect: hdspechan_setspeed() RMW-writes the shared sc->ctrl_register and writes CONTROL/FREQ registers with no sc->lock (hdspe-pcm.c:486-498) while the sibling ops (setblocksize:547-552, trigger->start/stop_audio:222-239) do the same under sc->lock. Play and record channels hold different CHN locks, so a setspeed on one channel races trigger/setblocksize on another: lost updates on ctrl_register can resurrect HDSPE_ENABLE/AUDIO_INT_ENABLE after stop (interrupt storm) or scramble latency/frequency bits. Functional/DoS impact only; requires real RME HDSPe hardware (no emulator exists), so PoC left untested by design.