From: Greg Kroah-Hartman Date: Mon, 9 Aug 2021 07:33:14 +0000 (+0200) Subject: 4.9-stable patches X-Git-Tag: v4.4.280~73 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=d4640be642550eb2a2fa14b2e3e0dade67969f95;p=thirdparty%2Fkernel%2Fstable-queue.git 4.9-stable patches added patches: alsa-seq-fix-racy-deletion-of-subscriber.patch --- 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 index 00000000000..f219d5c62e2 --- /dev/null +++ b/queue-4.9/alsa-seq-fix-racy-deletion-of-subscriber.patch @@ -0,0 +1,116 @@ +From 97367c97226aab8b298ada954ce12659ee3ad2a4 Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Tue, 3 Aug 2021 13:43:12 +0200 +Subject: ALSA: seq: Fix racy deletion of subscriber + +From: Takashi Iwai + +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 +Cc: +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 +Signed-off-by: Greg Kroah-Hartman +--- + 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; + }