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)
PoC verification
Evidence pack
findings/poc/DF-4700 Β· 2 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | file | 2.2 KB | β raw | |
| verdict.json | file | 2.0 KB | view 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 sharedsc->ctrl_register(:486-487), writesHDSPE_CONTROL_REG(:488) andHDSPE_FREQ_REG(:498), and storessc->speed(:500) β nosnd_mtxlock(sc->lock)anywhere in the function, in contrast tosetblocksizewhich takes the lock for the same operation.- The PCM framework calls channel ops under the per-channel
CHN_LOCK; play and record channels are differentpcm_channelobjects. A rec channel'ssetspeed(SNDCTL_DSP_SPEED on /dev/dspN) can therefore interleave with a play channel'sPCMTRIG_START/STOP(hdspe_start_audio/hdspe_stop_audioRMW the same register undersc->lock) and withsetblocksize(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_testablerequires physical RME HDSPe AIO/RayDAT hardware (PCI 10ee:3fc6); no QEMU device model exists
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.
No comments yet.