Commit 161d1a0a authored by Takashi Iwai's avatar Takashi Iwai Committed by Greg Kroah-Hartman

ALSA: seq: Fix double port list deletion

commit 13d5e5d4 upstream.

The commit [7f0973e9: ALSA: seq: Fix lockdep warnings due to
double mutex locks] split the management of two linked lists (source
and destination) into two individual calls for avoiding the AB/BA
deadlock.  However, this may leave the possible double deletion of one
of two lists when the counterpart is being deleted concurrently.
It ends up with a list corruption, as revealed by syzkaller fuzzer.

This patch fixes it by checking the list emptiness and skipping the
deletion and the following process.

BugLink: http://lkml.kernel.org/r/CACT4Y+bay9qsrz6dQu31EcGaH9XwfW7o3oBzSQUG9fMszoh=Sg@mail.gmail.com
Fixes: 7f0973e9 ('ALSA: seq: Fix lockdep warnings due to 'double mutex locks)
Reported-by: default avatarDmitry Vyukov <dvyukov@google.com>
Tested-by: default avatarDmitry Vyukov <dvyukov@google.com>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 3676114f
...@@ -540,19 +540,22 @@ static void delete_and_unsubscribe_port(struct snd_seq_client *client, ...@@ -540,19 +540,22 @@ static void delete_and_unsubscribe_port(struct snd_seq_client *client,
bool is_src, bool ack) bool is_src, bool ack)
{ {
struct snd_seq_port_subs_info *grp; struct snd_seq_port_subs_info *grp;
struct list_head *list;
bool empty;
grp = is_src ? &port->c_src : &port->c_dest; grp = is_src ? &port->c_src : &port->c_dest;
list = is_src ? &subs->src_list : &subs->dest_list;
down_write(&grp->list_mutex); down_write(&grp->list_mutex);
write_lock_irq(&grp->list_lock); write_lock_irq(&grp->list_lock);
if (is_src) empty = list_empty(list);
list_del(&subs->src_list); if (!empty)
else list_del_init(list);
list_del(&subs->dest_list);
grp->exclusive = 0; grp->exclusive = 0;
write_unlock_irq(&grp->list_lock); write_unlock_irq(&grp->list_lock);
up_write(&grp->list_mutex); up_write(&grp->list_mutex);
unsubscribe_port(client, port, grp, &subs->info, ack); if (!empty)
unsubscribe_port(client, port, grp, &subs->info, ack);
} }
/* connect two ports */ /* connect two ports */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment