From c0317c0e87094f5b5782b6fdef5ae0a4b150496c Mon Sep 17 00:00:00 2001 From: Wang Wensheng Date: Wed, 3 Nov 2021 03:35:17 +0000 Subject: ALSA: timer: Fix use-after-free problem When the timer instance was add into ack_list but was not currently in process, the user could stop it via snd_timer_stop1() without delete it from the ack_list. Then the user could free the timer instance and when it was actually processed UAF occurred. This issue could be reproduced via testcase snd_timer01 in ltp - running several instances of that testcase at the same time. What I actually met was that the ack_list of the timer broken and the kernel went into deadloop with irqoff. That could be detected by hardlockup detector on board or when we run it on qemu, we could use gdb to dump the ack_list when the console has no response. To fix this issue, we delete the timer instance from ack_list and active_list unconditionally in snd_timer_stop1(). Signed-off-by: Wang Wensheng Suggested-by: Takashi Iwai Cc: Link: https://lore.kernel.org/r/20211103033517.80531-1-wangwensheng4@huawei.com Signed-off-by: Takashi Iwai --- sound/core/timer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound/core/timer.c') diff --git a/sound/core/timer.c b/sound/core/timer.c index 92b7008fcdb8..4f9bab931951 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -624,13 +624,13 @@ static int snd_timer_stop1(struct snd_timer_instance *timeri, bool stop) if (!timer) return -EINVAL; spin_lock_irqsave(&timer->lock, flags); + list_del_init(&timeri->ack_list); + list_del_init(&timeri->active_list); if (!(timeri->flags & (SNDRV_TIMER_IFLG_RUNNING | SNDRV_TIMER_IFLG_START))) { result = -EBUSY; goto unlock; } - list_del_init(&timeri->ack_list); - list_del_init(&timeri->active_list); if (timer->card && timer->card->shutdown) goto unlock; if (stop) { -- cgit From ffdd98277f0a1d15a67a74ae09bee713df4c0dbc Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 5 Nov 2021 10:15:17 +0100 Subject: ALSA: timer: Unconditionally unlink slave instances, too Like the previous fix (commit c0317c0e8709 "ALSA: timer: Fix use-after-free problem"), we have to unlink slave timer instances immediately at snd_timer_stop(), too. Otherwise it may leave a stale entry in the list if the slave instance is freed before actually running. Cc: Link: https://lore.kernel.org/r/20211105091517.21733-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/core/timer.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'sound/core/timer.c') diff --git a/sound/core/timer.c b/sound/core/timer.c index 4f9bab931951..b3214baa8919 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -665,23 +665,22 @@ static int snd_timer_stop1(struct snd_timer_instance *timeri, bool stop) static int snd_timer_stop_slave(struct snd_timer_instance *timeri, bool stop) { unsigned long flags; + bool running; spin_lock_irqsave(&slave_active_lock, flags); - if (!(timeri->flags & SNDRV_TIMER_IFLG_RUNNING)) { - spin_unlock_irqrestore(&slave_active_lock, flags); - return -EBUSY; - } + running = timeri->flags & SNDRV_TIMER_IFLG_RUNNING; timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING; if (timeri->timer) { spin_lock(&timeri->timer->lock); list_del_init(&timeri->ack_list); list_del_init(&timeri->active_list); - snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP : - SNDRV_TIMER_EVENT_PAUSE); + if (running) + snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP : + SNDRV_TIMER_EVENT_PAUSE); spin_unlock(&timeri->timer->lock); } spin_unlock_irqrestore(&slave_active_lock, flags); - return 0; + return running ? 0 : -EBUSY; } /* -- cgit