diff options
author | Takashi Iwai <tiwai@suse.de> | 2020-02-14 12:13:15 +0100 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2020-02-14 15:53:09 +0100 |
commit | dc7497795e014d84699c3b8809ed6df35352dd74 (patch) | |
tree | 2268c9c6fe20d5638e558ae014d512b85e543599 /sound/core/seq/seq_queue.c | |
parent | bb51e669fa49feb5904f452b2991b240ef31bc97 (diff) |
ALSA: seq: Fix concurrent access to queue current tick/time
snd_seq_check_queue() passes the current tick and time of the given
queue as a pointer to snd_seq_prioq_cell_out(), but those might be
updated concurrently by the seq timer update.
Fix it by retrieving the current tick and time via the proper helper
functions at first, and pass those values to snd_seq_prioq_cell_out()
later in the loops.
snd_seq_timer_get_cur_time() takes a new argument and adjusts with the
current system time only when it's requested so; this update isn't
needed for snd_seq_check_queue(), as it's called either from the
interrupt handler or right after queuing.
Also, snd_seq_timer_get_cur_tick() is changed to read the value in the
spinlock for the concurrency, too.
Reported-by: syzbot+fd5e0eaa1a32999173b2@syzkaller.appspotmail.com
Link: https://lore.kernel.org/r/20200214111316.26939-3-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/core/seq/seq_queue.c')
-rw-r--r-- | sound/core/seq/seq_queue.c | 9 |
1 files changed, 6 insertions, 3 deletions
diff --git a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c index 20c552cf8398..71a6ea62c3be 100644 --- a/sound/core/seq/seq_queue.c +++ b/sound/core/seq/seq_queue.c @@ -238,6 +238,8 @@ void snd_seq_check_queue(struct snd_seq_queue *q, int atomic, int hop) { unsigned long flags; struct snd_seq_event_cell *cell; + snd_seq_tick_time_t cur_tick; + snd_seq_real_time_t cur_time; if (q == NULL) return; @@ -254,17 +256,18 @@ void snd_seq_check_queue(struct snd_seq_queue *q, int atomic, int hop) __again: /* Process tick queue... */ + cur_tick = snd_seq_timer_get_cur_tick(q->timer); for (;;) { - cell = snd_seq_prioq_cell_out(q->tickq, - &q->timer->tick.cur_tick); + cell = snd_seq_prioq_cell_out(q->tickq, &cur_tick); if (!cell) break; snd_seq_dispatch_event(cell, atomic, hop); } /* Process time queue... */ + cur_time = snd_seq_timer_get_cur_time(q->timer, false); for (;;) { - cell = snd_seq_prioq_cell_out(q->timeq, &q->timer->cur_time); + cell = snd_seq_prioq_cell_out(q->timeq, &cur_time); if (!cell) break; snd_seq_dispatch_event(cell, atomic, hop); |