ALSA: pcm: Fix races among concurrent prepare and hw_params/hw_free calls
authorTakashi Iwai <tiwai@suse.de>
Fri, 6 May 2022 09:10:11 +0000 (12:10 +0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 12 May 2022 10:23:49 +0000 (12:23 +0200)
commit 3c3201f8c7bb77eb53b08a3ca8d9a4ddc500b4c0 upstream.

Like the previous fixes to hw_params and hw_free ioctl races, we need
to paper over the concurrent prepare ioctl calls against hw_params and
hw_free, too.

This patch implements the locking with the existing
runtime->buffer_mutex for prepare ioctls.  Unlike the previous case
for snd_pcm_hw_hw_params() and snd_pcm_hw_free(), snd_pcm_prepare() is
performed to the linked streams, hence the lock can't be applied
simply on the top.  For tracking the lock in each linked substream, we
modify snd_pcm_action_group() slightly and apply the buffer_mutex for
the case stream_lock=false (formerly there was no lock applied)
there.

Cc: <stable@vger.kernel.org>
Reviewed-by: Jaroslav Kysela <perex@perex.cz>
Link: https://lore.kernel.org/r/20220322170720.3529-4-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
[OP: backport to 5.4: adjusted context]
Signed-off-by: Ovidiu Panait <ovidiu.panait@windriver.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
sound/core/pcm_native.c

index b15ef9df114aff4eca3ac060673ab7e942cd55fd..4f53e6103fd56be0f3ff7a916d1b1e8261facc06 100644 (file)
@@ -1042,15 +1042,17 @@ struct action_ops {
  */
 static int snd_pcm_action_group(const struct action_ops *ops,
                                struct snd_pcm_substream *substream,
-                               int state, int do_lock)
+                               int state, int stream_lock)
 {
        struct snd_pcm_substream *s = NULL;
        struct snd_pcm_substream *s1;
        int res = 0, depth = 1;
 
        snd_pcm_group_for_each_entry(s, substream) {
-               if (do_lock && s != substream) {
-                       if (s->pcm->nonatomic)
+               if (s != substream) {
+                       if (!stream_lock)
+                               mutex_lock_nested(&s->runtime->buffer_mutex, depth);
+                       else if (s->pcm->nonatomic)
                                mutex_lock_nested(&s->self_group.mutex, depth);
                        else
                                spin_lock_nested(&s->self_group.lock, depth);
@@ -1078,18 +1080,18 @@ static int snd_pcm_action_group(const struct action_ops *ops,
                ops->post_action(s, state);
        }
  _unlock:
-       if (do_lock) {
-               /* unlock streams */
-               snd_pcm_group_for_each_entry(s1, substream) {
-                       if (s1 != substream) {
-                               if (s1->pcm->nonatomic)
-                                       mutex_unlock(&s1->self_group.mutex);
-                               else
-                                       spin_unlock(&s1->self_group.lock);
-                       }
-                       if (s1 == s)    /* end */
-                               break;
+       /* unlock streams */
+       snd_pcm_group_for_each_entry(s1, substream) {
+               if (s1 != substream) {
+                       if (!stream_lock)
+                               mutex_unlock(&s1->runtime->buffer_mutex);
+                       else if (s1->pcm->nonatomic)
+                               mutex_unlock(&s1->self_group.mutex);
+                       else
+                               spin_unlock(&s1->self_group.lock);
                }
+               if (s1 == s)    /* end */
+                       break;
        }
        return res;
 }
@@ -1219,10 +1221,12 @@ static int snd_pcm_action_nonatomic(const struct action_ops *ops,
 
        /* Guarantee the group members won't change during non-atomic action */
        down_read(&snd_pcm_link_rwsem);
+       mutex_lock(&substream->runtime->buffer_mutex);
        if (snd_pcm_stream_linked(substream))
                res = snd_pcm_action_group(ops, substream, state, 0);
        else
                res = snd_pcm_action_single(ops, substream, state);
+       mutex_unlock(&substream->runtime->buffer_mutex);
        up_read(&snd_pcm_link_rwsem);
        return res;
 }