summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2016-02-16 14:15:59 +0100
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2016-02-25 12:01:14 -0800
commita40efb855068a20cf769425a799642aa95c57635 (patch)
tree72f0b61ee8dc6892d112370aa13f304550ca6eb6
parent6bb345ac7b30680018be6d7e6b4738fab7283b4f (diff)
ALSA: seq: Fix double port list deletion
commit 13d5e5d4725c64ec06040d636832e78453f477b7 upstream. The commit [7f0973e973cd: 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: 7f0973e973cd ('ALSA: seq: Fix lockdep warnings due to 'double mutex locks) Reported-by: Dmitry Vyukov <dvyukov@google.com> Tested-by: Dmitry Vyukov <dvyukov@google.com> Signed-off-by: Takashi Iwai <tiwai@suse.de> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--sound/core/seq/seq_ports.c13
1 files changed, 8 insertions, 5 deletions
diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c
index 921fb2bd8fad..fe686ee41c6d 100644
--- a/sound/core/seq/seq_ports.c
+++ b/sound/core/seq/seq_ports.c
@@ -535,19 +535,22 @@ static void delete_and_unsubscribe_port(struct snd_seq_client *client,
bool is_src, bool ack)
{
struct snd_seq_port_subs_info *grp;
+ struct list_head *list;
+ bool empty;
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);
- if (is_src)
- list_del(&subs->src_list);
- else
- list_del(&subs->dest_list);
+ empty = list_empty(list);
+ if (!empty)
+ list_del_init(list);
grp->exclusive = 0;
write_unlock_irq(&grp->list_lock);
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 */