]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ALSA: pcm: wake linked drain waiters on unlink
authorNorbert Szetei <norbert@doyensec.com>
Tue, 28 Jul 2026 12:50:01 +0000 (14:50 +0200)
committerTakashi Iwai <tiwai@suse.de>
Tue, 28 Jul 2026 16:29:48 +0000 (18:29 +0200)
snd_pcm_drain() on a linked stream parks an on-stack wait entry on the
drained peer's runtime->sleep, and after schedule_timeout() removes it
only if that peer is still found in the caller's group.  If group
membership changes during the wait and the sleep ends by signal or
timeout (so autoremove_wake_function() does not run), finish_wait() is
skipped and snd_pcm_drain() returns with the entry still queued on that
stream's sleep list; a later wake_up() then walks a freed stack frame.
This is reachable by unlinking either the drained or the draining stream.

Unlike the close path (snd_pcm_drop() -> snd_pcm_post_stop()),
snd_pcm_unlink() never wakes the sleep queues.  Wake every group member
under the group lock before the membership change, so a linked drainer is
released and drops its entry while the streams are still grouped.

The window was opened when snd_pcm_link_rwsem stopped being held across
the wait and the removal became conditional on group membership (see
Fixes). The later switch to finish_wait() kept that conditional removal,
so the signal/timeout case remained.

Fixes: f57f3df03a8e ("ALSA: pcm: More fine-grained PCM link locking")
Cc: stable@vger.kernel.org
Assisted-by: Claude:claude-opus-5
Signed-off-by: Norbert Szetei <norbert@doyensec.com>
Link: https://patch.msgid.link/A0705100-D10B-4286-9980-0142ABEEAD51@doyensec.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/core/pcm_native.c

index 7dc0060617f1e28d64957ab5c8360abee17eb7c3..c9d0c2bb55b2699b029dd11f6dfba2778cdd10ae 100644 (file)
@@ -2367,6 +2367,7 @@ static void relink_to_local(struct snd_pcm_substream *substream)
 
 static int snd_pcm_unlink(struct snd_pcm_substream *substream)
 {
+       struct snd_pcm_substream *s;
        struct snd_pcm_group *group;
        bool nonatomic = substream->pcm->nonatomic;
        bool do_free = false;
@@ -2379,6 +2380,12 @@ static int snd_pcm_unlink(struct snd_pcm_substream *substream)
        group = substream->group;
        snd_pcm_group_lock_irq(group, nonatomic);
 
+       /* release drain waiters before changing membership, else snd_pcm_drain()
+        * leaves its on-stack wait entry queued on a member's sleep list
+        */
+       snd_pcm_group_for_each_entry(s, substream)
+               wake_up(&s->runtime->sleep);
+
        relink_to_local(substream);
        refcount_dec(&group->refs);