aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2016-01-14 10:30:58 -0500
committerTakashi Iwai <tiwai@suse.de>2016-01-15 09:50:34 -0500
commitb5a663aa426f4884c71cd8580adae73f33570f0d (patch)
tree87f6d8a3994ca42949130ddb53cb51603fea704f /sound
parentcf52103a218744f3fd18111325c28e95aa9cd226 (diff)
ALSA: timer: Harden slave timer list handling
A slave timer instance might be still accessible in a racy way while operating the master instance as it lacks of locking. Since the master operation is mostly protected with timer->lock, we should cope with it while changing the slave instance, too. Also, some linked lists (active_list and ack_list) of slave instances aren't unlinked immediately at stopping or closing, and this may lead to unexpected accesses. This patch tries to address these issues. It adds spin lock of timer->lock (either from master or slave, which is equivalent) in a few places. For avoiding a deadlock, we ensure that the global slave_active_lock is always locked at first before each timer lock. Also, ack and active_list of slave instances are properly unlinked at snd_timer_stop() and snd_timer_close(). Last but not least, remove the superfluous call of _snd_timer_stop() at removing slave links. This is a noop, and calling it may confuse readers wrt locking. Further cleanup will follow in a later patch. Actually we've got reports of use-after-free by syzkaller fuzzer, and this hopefully fixes these issues. 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/timer.c18
1 files changed, 14 insertions, 4 deletions
diff --git a/sound/core/timer.c b/sound/core/timer.c
index 3810ee8f1205..4e8d7bfffff6 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -215,11 +215,13 @@ static void snd_timer_check_master(struct snd_timer_instance *master)
215 slave->slave_id == master->slave_id) { 215 slave->slave_id == master->slave_id) {
216 list_move_tail(&slave->open_list, &master->slave_list_head); 216 list_move_tail(&slave->open_list, &master->slave_list_head);
217 spin_lock_irq(&slave_active_lock); 217 spin_lock_irq(&slave_active_lock);
218 spin_lock(&master->timer->lock);
218 slave->master = master; 219 slave->master = master;
219 slave->timer = master->timer; 220 slave->timer = master->timer;
220 if (slave->flags & SNDRV_TIMER_IFLG_RUNNING) 221 if (slave->flags & SNDRV_TIMER_IFLG_RUNNING)
221 list_add_tail(&slave->active_list, 222 list_add_tail(&slave->active_list,
222 &master->slave_active_head); 223 &master->slave_active_head);
224 spin_unlock(&master->timer->lock);
223 spin_unlock_irq(&slave_active_lock); 225 spin_unlock_irq(&slave_active_lock);
224 } 226 }
225 } 227 }
@@ -346,15 +348,18 @@ int snd_timer_close(struct snd_timer_instance *timeri)
346 timer->hw.close) 348 timer->hw.close)
347 timer->hw.close(timer); 349 timer->hw.close(timer);
348 /* remove slave links */ 350 /* remove slave links */
351 spin_lock_irq(&slave_active_lock);
352 spin_lock(&timer->lock);
349 list_for_each_entry_safe(slave, tmp, &timeri->slave_list_head, 353 list_for_each_entry_safe(slave, tmp, &timeri->slave_list_head,
350 open_list) { 354 open_list) {
351 spin_lock_irq(&slave_active_lock);
352 _snd_timer_stop(slave, 1, SNDRV_TIMER_EVENT_RESOLUTION);
353 list_move_tail(&slave->open_list, &snd_timer_slave_list); 355 list_move_tail(&slave->open_list, &snd_timer_slave_list);
354 slave->master = NULL; 356 slave->master = NULL;
355 slave->timer = NULL; 357 slave->timer = NULL;
356 spin_unlock_irq(&slave_active_lock); 358 list_del_init(&slave->ack_list);
359 list_del_init(&slave->active_list);
357 } 360 }
361 spin_unlock(&timer->lock);
362 spin_unlock_irq(&slave_active_lock);
358 mutex_unlock(&register_mutex); 363 mutex_unlock(&register_mutex);
359 } 364 }
360 out: 365 out:
@@ -441,9 +446,12 @@ static int snd_timer_start_slave(struct snd_timer_instance *timeri)
441 446
442 spin_lock_irqsave(&slave_active_lock, flags); 447 spin_lock_irqsave(&slave_active_lock, flags);
443 timeri->flags |= SNDRV_TIMER_IFLG_RUNNING; 448 timeri->flags |= SNDRV_TIMER_IFLG_RUNNING;
444 if (timeri->master) 449 if (timeri->master && timeri->timer) {
450 spin_lock(&timeri->timer->lock);
445 list_add_tail(&timeri->active_list, 451 list_add_tail(&timeri->active_list,
446 &timeri->master->slave_active_head); 452 &timeri->master->slave_active_head);
453 spin_unlock(&timeri->timer->lock);
454 }
447 spin_unlock_irqrestore(&slave_active_lock, flags); 455 spin_unlock_irqrestore(&slave_active_lock, flags);
448 return 1; /* delayed start */ 456 return 1; /* delayed start */
449} 457}
@@ -489,6 +497,8 @@ static int _snd_timer_stop(struct snd_timer_instance * timeri,
489 if (!keep_flag) { 497 if (!keep_flag) {
490 spin_lock_irqsave(&slave_active_lock, flags); 498 spin_lock_irqsave(&slave_active_lock, flags);
491 timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING; 499 timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
500 list_del_init(&timeri->ack_list);
501 list_del_init(&timeri->active_list);
492 spin_unlock_irqrestore(&slave_active_lock, flags); 502 spin_unlock_irqrestore(&slave_active_lock, flags);
493 } 503 }
494 goto __end; 504 goto __end;