Skip to content

Commit b5a663a

Browse files
committed
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 Cc: Signed-off-by: Takashi Iwai
1 parent cf52103 commit b5a663a

File tree

1 file changed

+14
-4
lines changed

1 file changed

+14
-4
lines changed

sound/core/timer.c

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -215,11 +215,13 @@ static void snd_timer_check_master(struct snd_timer_instance *master)
215215
slave->slave_id == master->slave_id) {
216216
list_move_tail(&slave->open_list, &master->slave_list_head);
217217
spin_lock_irq(&slave_active_lock);
218+
spin_lock(&master->timer->lock);
218219
slave->master = master;
219220
slave->timer = master->timer;
220221
if (slave->flags & SNDRV_TIMER_IFLG_RUNNING)
221222
list_add_tail(&slave->active_list,
222223
&master->slave_active_head);
224+
spin_unlock(&master->timer->lock);
223225
spin_unlock_irq(&slave_active_lock);
224226
}
225227
}
@@ -346,15 +348,18 @@ int snd_timer_close(struct snd_timer_instance *timeri)
346348
timer->hw.close)
347349
timer->hw.close(timer);
348350
/* remove slave links */
351+
spin_lock_irq(&slave_active_lock);
352+
spin_lock(&timer->lock);
349353
list_for_each_entry_safe(slave, tmp, &timeri->slave_list_head,
350354
open_list) {
351-
spin_lock_irq(&slave_active_lock);
352-
_snd_timer_stop(slave, 1, SNDRV_TIMER_EVENT_RESOLUTION);
353355
list_move_tail(&slave->open_list, &snd_timer_slave_list);
354356
slave->master = NULL;
355357
slave->timer = NULL;
356-
spin_unlock_irq(&slave_active_lock);
358+
list_del_init(&slave->ack_list);
359+
list_del_init(&slave->active_list);
357360
}
361+
spin_unlock(&timer->lock);
362+
spin_unlock_irq(&slave_active_lock);
358363
mutex_unlock(&register_mutex);
359364
}
360365
out:
@@ -441,9 +446,12 @@ static int snd_timer_start_slave(struct snd_timer_instance *timeri)
441446

442447
spin_lock_irqsave(&slave_active_lock, flags);
443448
timeri->flags |= SNDRV_TIMER_IFLG_RUNNING;
444-
if (timeri->master)
449+
if (timeri->master && timeri->timer) {
450+
spin_lock(&timeri->timer->lock);
445451
list_add_tail(&timeri->active_list,
446452
&timeri->master->slave_active_head);
453+
spin_unlock(&timeri->timer->lock);
454+
}
447455
spin_unlock_irqrestore(&slave_active_lock, flags);
448456
return 1; /* delayed start */
449457
}
@@ -489,6 +497,8 @@ static int _snd_timer_stop(struct snd_timer_instance * timeri,
489497
if (!keep_flag) {
490498
spin_lock_irqsave(&slave_active_lock, flags);
491499
timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
500+
list_del_init(&timeri->ack_list);
501+
list_del_init(&timeri->active_list);
492502
spin_unlock_irqrestore(&slave_active_lock, flags);
493503
}
494504
goto __end;

0 commit comments

Comments
 (0)