sound/core/pcm_native.c | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-)
snd_pcm_drain() may select a runtime from a linked playback stream and
queue a stack wait entry on its embedded sleep waitqueue. The function
then drops the stream lock and sleeps before removing the wait entry.
commit 9b1dbd69ba6f ("ALSA: pcm: fix use-after-free on linked stream
runtime in snd_pcm_drain()") cached scalar fields from the linked
runtime before dropping the stream lock, because a concurrent close can
unlink the stream and free its runtime. The same lifetime issue remains
for runtime->sleep. If the linked runtime is detached while the wait
entry is queued, remove_wait_queue() can later operate on a freed or
reused waitqueue, or the stack wait entry can remain linked to freed
memory.
Take snd_pcm_link_rwsem for read while the wait entry is queued on the
linked runtime. Acquire it before re-taking the stream lock, matching the
link/unlink lock order, and drop it only after the wait entry has been
removed.
Fixes: 21cb2a2ec581 ("[ALSA] Fix races between PCM drain and other ops")
Signed-off-by: Guangshuo Li <lgs201920130244@gmail.com>
---
sound/core/pcm_native.c | 36 +++++++++++++++++++++++++++---------
1 file changed, 27 insertions(+), 9 deletions(-)
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index a541bb235cfa..1f7aba988e45 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -2175,6 +2175,22 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
result = -ERESTARTSYS;
break;
}
+
+ /*
+ * The wait below is queued on to_check->sleep, which is embedded
+ * in a runtime that may belong to a linked substream. Link/unlink
+ * paths take snd_pcm_link_rwsem for write before moving streams
+ * between groups and detaching runtimes, so take it for read while
+ * the wait entry is queued.
+ *
+ * Drop and re-take the stream lock so the lock order matches
+ * snd_pcm_link() and snd_pcm_unlink(): link_rwsem first, then the
+ * stream/group lock.
+ */
+ snd_pcm_stream_unlock_irq(substream);
+ down_read(&snd_pcm_link_rwsem);
+ snd_pcm_stream_lock_irq(substream);
+
/* find a substream to drain */
to_check = NULL;
group = snd_pcm_stream_group_ref(substream);
@@ -2188,8 +2204,14 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
}
}
snd_pcm_group_unref(group, substream);
- if (!to_check)
+
+ if (!to_check) {
+ snd_pcm_stream_unlock_irq(substream);
+ up_read(&snd_pcm_link_rwsem);
+ snd_pcm_stream_lock_irq(substream);
break; /* all drained */
+ }
+
/*
* Cache the runtime fields needed after unlock.
* A concurrent close() on the linked stream may free
@@ -2216,14 +2238,10 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
tout = schedule_timeout(tout);
snd_pcm_stream_lock_irq(substream);
- group = snd_pcm_stream_group_ref(substream);
- snd_pcm_group_for_each_entry(s, substream) {
- if (s->runtime == to_check) {
- remove_wait_queue(&to_check->sleep, &wait);
- break;
- }
- }
- snd_pcm_group_unref(group, substream);
+ remove_wait_queue(&to_check->sleep, &wait);
+ snd_pcm_stream_unlock_irq(substream);
+ up_read(&snd_pcm_link_rwsem);
+ snd_pcm_stream_lock_irq(substream);
if (card->shutdown) {
result = -ENODEV;
--
2.43.0
On Fri, 05 Jun 2026 07:16:37 +0200,
Guangshuo Li wrote:
>
> snd_pcm_drain() may select a runtime from a linked playback stream and
> queue a stack wait entry on its embedded sleep waitqueue. The function
> then drops the stream lock and sleeps before removing the wait entry.
>
> commit 9b1dbd69ba6f ("ALSA: pcm: fix use-after-free on linked stream
> runtime in snd_pcm_drain()") cached scalar fields from the linked
> runtime before dropping the stream lock, because a concurrent close can
> unlink the stream and free its runtime. The same lifetime issue remains
> for runtime->sleep. If the linked runtime is detached while the wait
> entry is queued, remove_wait_queue() can later operate on a freed or
> reused waitqueue, or the stack wait entry can remain linked to freed
> memory.
>
> Take snd_pcm_link_rwsem for read while the wait entry is queued on the
> linked runtime. Acquire it before re-taking the stream lock, matching the
> link/unlink lock order, and drop it only after the wait entry has been
> removed.
>
> Fixes: 21cb2a2ec581 ("[ALSA] Fix races between PCM drain and other ops")
> Signed-off-by: Guangshuo Li <lgs201920130244@gmail.com>
This should have been already fixed by the commit 88fe2e365872
ALSA: PCM: Fix wait queue list corruption in snd_pcm_drain() on
linked streams
in for-linus branch of sound.git tree.
Please check whether the issue still occurs.
thanks,
Takashi
© 2016 - 2026 Red Hat, Inc.