]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
4.9-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 9 Aug 2021 07:33:14 +0000 (09:33 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 9 Aug 2021 07:33:14 +0000 (09:33 +0200)
added patches:
alsa-seq-fix-racy-deletion-of-subscriber.patch

queue-4.9/alsa-seq-fix-racy-deletion-of-subscriber.patch [new file with mode: 0644]

diff --git a/queue-4.9/alsa-seq-fix-racy-deletion-of-subscriber.patch b/queue-4.9/alsa-seq-fix-racy-deletion-of-subscriber.patch
new file mode 100644 (file)
index 0000000..f219d5c
--- /dev/null
@@ -0,0 +1,116 @@
+From 97367c97226aab8b298ada954ce12659ee3ad2a4 Mon Sep 17 00:00:00 2001
+From: Takashi Iwai <tiwai@suse.de>
+Date: Tue, 3 Aug 2021 13:43:12 +0200
+Subject: ALSA: seq: Fix racy deletion of subscriber
+
+From: Takashi Iwai <tiwai@suse.de>
+
+commit 97367c97226aab8b298ada954ce12659ee3ad2a4 upstream.
+
+It turned out that the current implementation of the port subscription
+is racy.  The subscription contains two linked lists, and we have to
+add to or delete from both lists.  Since both connection and
+disconnection procedures perform the same order for those two lists
+(i.e. src list, then dest list), when a deletion happens during a
+connection procedure, the src list may be deleted before the dest list
+addition completes, and this may lead to a use-after-free or an Oops,
+even though the access to both lists are protected via mutex.
+
+The simple workaround for this race is to change the access order for
+the disconnection, namely, dest list, then src list.  This assures
+that the connection has been established when disconnecting, and also
+the concurrent deletion can be avoided.
+
+Reported-and-tested-by: folkert <folkert@vanheusden.com>
+Cc: <stable@vger.kernel.org>
+Link: https://lore.kernel.org/r/20210801182754.GP890690@belle.intranet.vanheusden.com
+Link: https://lore.kernel.org/r/20210803114312.2536-1-tiwai@suse.de
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ sound/core/seq/seq_ports.c |   39 +++++++++++++++++++++++++++------------
+ 1 file changed, 27 insertions(+), 12 deletions(-)
+
+--- a/sound/core/seq/seq_ports.c
++++ b/sound/core/seq/seq_ports.c
+@@ -532,10 +532,11 @@ static int check_and_subscribe_port(stru
+       return err;
+ }
+-static void delete_and_unsubscribe_port(struct snd_seq_client *client,
+-                                      struct snd_seq_client_port *port,
+-                                      struct snd_seq_subscribers *subs,
+-                                      bool is_src, bool ack)
++/* called with grp->list_mutex held */
++static void __delete_and_unsubscribe_port(struct snd_seq_client *client,
++                                        struct snd_seq_client_port *port,
++                                        struct snd_seq_subscribers *subs,
++                                        bool is_src, bool ack)
+ {
+       struct snd_seq_port_subs_info *grp;
+       struct list_head *list;
+@@ -543,7 +544,6 @@ static void delete_and_unsubscribe_port(
+       grp = is_src ? &port->c_src : &port->c_dest;
+       list = is_src ? &subs->src_list : &subs->dest_list;
+-      down_write(&grp->list_mutex);
+       write_lock_irq(&grp->list_lock);
+       empty = list_empty(list);
+       if (!empty)
+@@ -553,6 +553,18 @@ static void delete_and_unsubscribe_port(
+       if (!empty)
+               unsubscribe_port(client, port, grp, &subs->info, ack);
++}
++
++static void delete_and_unsubscribe_port(struct snd_seq_client *client,
++                                      struct snd_seq_client_port *port,
++                                      struct snd_seq_subscribers *subs,
++                                      bool is_src, bool ack)
++{
++      struct snd_seq_port_subs_info *grp;
++
++      grp = is_src ? &port->c_src : &port->c_dest;
++      down_write(&grp->list_mutex);
++      __delete_and_unsubscribe_port(client, port, subs, is_src, ack);
+       up_write(&grp->list_mutex);
+ }
+@@ -608,27 +620,30 @@ int snd_seq_port_disconnect(struct snd_s
+                           struct snd_seq_client_port *dest_port,
+                           struct snd_seq_port_subscribe *info)
+ {
+-      struct snd_seq_port_subs_info *src = &src_port->c_src;
++      struct snd_seq_port_subs_info *dest = &dest_port->c_dest;
+       struct snd_seq_subscribers *subs;
+       int err = -ENOENT;
+-      down_write(&src->list_mutex);
++      /* always start from deleting the dest port for avoiding concurrent
++       * deletions
++       */
++      down_write(&dest->list_mutex);
+       /* look for the connection */
+-      list_for_each_entry(subs, &src->list_head, src_list) {
++      list_for_each_entry(subs, &dest->list_head, dest_list) {
+               if (match_subs_info(info, &subs->info)) {
+-                      atomic_dec(&subs->ref_count); /* mark as not ready */
++                      __delete_and_unsubscribe_port(dest_client, dest_port,
++                                                    subs, false,
++                                                    connector->number != dest_client->number);
+                       err = 0;
+                       break;
+               }
+       }
+-      up_write(&src->list_mutex);
++      up_write(&dest->list_mutex);
+       if (err < 0)
+               return err;
+       delete_and_unsubscribe_port(src_client, src_port, subs, true,
+                                   connector->number != src_client->number);
+-      delete_and_unsubscribe_port(dest_client, dest_port, subs, false,
+-                                  connector->number != dest_client->number);
+       kfree(subs);
+       return 0;
+ }