diff options
author | Takashi Iwai <tiwai@suse.de> | 2017-02-28 22:15:51 +0100 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2017-02-28 22:15:51 +0100 |
commit | f3ac9f737603da80c2da3e84b89e74429836bb6d (patch) | |
tree | 8f6aef03fd1ea117451942feb37df49709c0f7cf /sound | |
parent | 493de342748cc6f52938096f5480cf291da58a0b (diff) | |
download | op-kernel-dev-f3ac9f737603da80c2da3e84b89e74429836bb6d.zip op-kernel-dev-f3ac9f737603da80c2da3e84b89e74429836bb6d.tar.gz |
ALSA: seq: Fix link corruption by event error handling
The sequencer FIFO management has a bug that may lead to a corruption
(shortage) of the cell linked list. When a sequencer client faces an
error at the event delivery, it tries to put back the dequeued cell.
When the first queue was put back, this forgot the tail pointer
tracking, and the link will be screwed up.
Although there is no memory corruption, the sequencer client may stall
forever at exit while flushing the pending FIFO cells in
snd_seq_pool_done(), as spotted by syzkaller.
This patch addresses the missing tail pointer tracking at
snd_seq_fifo_cell_putback(). Also the patch makes sure to clear the
cell->enxt pointer at snd_seq_fifo_event_in() for avoiding a similar
mess-up of the FIFO linked list.
Reported-by: Dmitry Vyukov <dvyukov@google.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/core/seq/seq_fifo.c | 3 |
1 files changed, 3 insertions, 0 deletions
diff --git a/sound/core/seq/seq_fifo.c b/sound/core/seq/seq_fifo.c index 1d5acbe..86240d0 100644 --- a/sound/core/seq/seq_fifo.c +++ b/sound/core/seq/seq_fifo.c @@ -135,6 +135,7 @@ int snd_seq_fifo_event_in(struct snd_seq_fifo *f, f->tail = cell; if (f->head == NULL) f->head = cell; + cell->next = NULL; f->cells++; spin_unlock_irqrestore(&f->lock, flags); @@ -214,6 +215,8 @@ void snd_seq_fifo_cell_putback(struct snd_seq_fifo *f, spin_lock_irqsave(&f->lock, flags); cell->next = f->head; f->head = cell; + if (!f->tail) + f->tail = cell; f->cells++; spin_unlock_irqrestore(&f->lock, flags); } |