sound: use-after-free in snd_timer_start1

41 views
Skip to first unread message

Dmitry Vyukov

unread,
Feb 3, 2016, 11:25:16 AM2/3/16
to Jaroslav Kysela, Takashi Iwai, alsa-...@alsa-project.org, LKML, syzkaller, Kostya Serebryany, Alexander Potapenko, Sasha Levin
Hello,

The following program triggers use-after-free in snd_timer_start1:

// autogenerated by syzkaller (http://github.com/google/syzkaller)
#include <pthread.h>
#include <stdint.h>
#include <string.h>
#include <sys/syscall.h>
#include <unistd.h>

int main()
{
syscall(SYS_mmap, 0x20000000ul, 0x664000ul, 0x3ul, 0x32ul,
0xfffffffffffffffful, 0x0ul);
int fd = syscall(SYS_open, "/dev/snd/timer", 0x40000ul, 0, 0, 0);
*(uint32_t*)0x20653000 = (uint32_t)0x1;
*(uint32_t*)0x20653004 = (uint32_t)0xfffffffffffffffe;
*(uint32_t*)0x20653008 = (uint32_t)0x8;
*(uint32_t*)0x2065300c = (uint32_t)0x0;
*(uint32_t*)0x20653010 = (uint32_t)0x0;
*(uint8_t*)0x20653014 = (uint8_t)0x0;
*(uint8_t*)0x20653015 = (uint8_t)0x0;
*(uint8_t*)0x20653016 = (uint8_t)0x0;
*(uint8_t*)0x20653017 = (uint8_t)0x0;
*(uint8_t*)0x20653018 = (uint8_t)0x0;
*(uint8_t*)0x20653019 = (uint8_t)0x0;
*(uint8_t*)0x2065301a = (uint8_t)0x0;
*(uint8_t*)0x2065301b = (uint8_t)0x0;
*(uint8_t*)0x2065301c = (uint8_t)0x0;
*(uint8_t*)0x2065301d = (uint8_t)0x0;
*(uint8_t*)0x2065301e = (uint8_t)0x0;
*(uint8_t*)0x2065301f = (uint8_t)0x0;
*(uint8_t*)0x20653020 = (uint8_t)0x0;
*(uint8_t*)0x20653021 = (uint8_t)0x0;
*(uint8_t*)0x20653022 = (uint8_t)0x0;
*(uint8_t*)0x20653023 = (uint8_t)0x0;
*(uint8_t*)0x20653024 = (uint8_t)0x0;
*(uint8_t*)0x20653025 = (uint8_t)0x0;
*(uint8_t*)0x20653026 = (uint8_t)0x0;
*(uint8_t*)0x20653027 = (uint8_t)0x0;
*(uint8_t*)0x20653028 = (uint8_t)0x0;
*(uint8_t*)0x20653029 = (uint8_t)0x0;
*(uint8_t*)0x2065302a = (uint8_t)0x0;
*(uint8_t*)0x2065302b = (uint8_t)0x0;
*(uint8_t*)0x2065302c = (uint8_t)0x0;
*(uint8_t*)0x2065302d = (uint8_t)0x0;
*(uint8_t*)0x2065302e = (uint8_t)0x0;
*(uint8_t*)0x2065302f = (uint8_t)0x0;
*(uint8_t*)0x20653030 = (uint8_t)0x0;
*(uint8_t*)0x20653031 = (uint8_t)0x0;
*(uint8_t*)0x20653032 = (uint8_t)0x0;
*(uint8_t*)0x20653033 = (uint8_t)0x0;
syscall(SYS_ioctl, fd, 0x40345410ul, 0x20653000ul, 0, 0, 0);
syscall(SYS_ioctl, fd, 0x54a0ul, 0, 0, 0, 0);
return 0;
}

==================================================================
BUG: KASAN: use-after-free in __list_add+0x233/0x22016/02/03 13:44:32
executing program 0:
Read of size 8 by task syz-executor/27661

INFO: Allocated in snd_timer_instance_new+0x52/0x3a0 age=7 cpu=0 pid=27660
[< inline >] slab_alloc_node mm/slub.c:2562
[< inline >] slab_alloc mm/slub.c:2604
[< none >] kmem_cache_alloc_trace+0x25c/0x300 mm/slub.c:2621
[< inline >] kmalloc include/linux/slab.h:463
[< inline >] kzalloc include/linux/slab.h:607
[< none >] snd_timer_instance_new+0x52/0x3a0 sound/core/timer.c:106
[< none >] snd_timer_open+0x522/0xd20 sound/core/timer.c:289
[< inline >] snd_timer_user_tselect sound/core/timer.c:1598
[< inline >] __snd_timer_user_ioctl sound/core/timer.c:1874
[< none >] snd_timer_user_ioctl+0x8db/0x25f0 sound/core/timer.c:1904
[< inline >] vfs_ioctl fs/ioctl.c:43
[< none >] do_vfs_ioctl+0x18c/0xfb0 fs/ioctl.c:674
[< inline >] SYSC_ioctl fs/ioctl.c:689
[< none >] SyS_ioctl+0x8f/0xc0 fs/ioctl.c:680
[< none >] entry_SYSCALL_64_fastpath+0x16/0x7a
arch/x86/entry/entry_64.S:185

INFO: Freed in snd_timer_close+0x3ee/0x750 age=9 cpu=3 pid=27661
[< inline >] slab_free mm/slub.c:2835
[< none >] kfree+0x2ac/0x2c0 mm/slub.c:3664
[< none >] snd_timer_close+0x3ee/0x750 sound/core/timer.c:375
[< inline >] snd_timer_user_tselect sound/core/timer.c:1588
[< inline >] __snd_timer_user_ioctl sound/core/timer.c:1874
[< none >] snd_timer_user_ioctl+0x7b6/0x25f0 sound/core/timer.c:1904
[< inline >] vfs_ioctl fs/ioctl.c:43
[< none >] do_vfs_ioctl+0x18c/0xfb0 fs/ioctl.c:674
[< inline >] SYSC_ioctl fs/ioctl.c:689
[< none >] SyS_ioctl+0x8f/0xc0 fs/ioctl.c:680
[< none >] entry_SYSCALL_64_fastpath+0x16/0x7a
arch/x86/entry/entry_64.S:185

Call Trace:
[< inline >] kasan_report mm/kasan/report.c:274
[<ffffffff8176640e>] __asan_report_load8_noabort+0x3e/0x40
mm/kasan/report.c:295
[<ffffffff82c4a7e3>] __list_add+0x233/0x250 lib/list_debug.c:39
[< inline >] list_move_tail include/linux/list.h:77
[<ffffffff85240fdc>] snd_timer_start1+0x6c/0x2c0 sound/core/timer.c:432
[<ffffffff85241a7b>] snd_timer_start+0x20b/0x2b0 sound/core/timer.c:499
[< inline >] snd_timer_user_start sound/core/timer.c:1799
[< inline >] __snd_timer_user_ioctl sound/core/timer.c:1883
[<ffffffff85245fb7>] snd_timer_user_ioctl+0x767/0x25f0 sound/core/timer.c:1904
[< inline >] vfs_ioctl fs/ioctl.c:43
[<ffffffff817fa9ec>] do_vfs_ioctl+0x18c/0xfb0 fs/ioctl.c:674
[< inline >] SYSC_ioctl fs/ioctl.c:689
[<ffffffff817fb89f>] SyS_ioctl+0x8f/0xc0 fs/ioctl.c:680
[<ffffffff8665ebb6>] entry_SYSCALL_64_fastpath+0x16/0x7a
arch/x86/entry/entry_64.S:185
==================================================================


Sometimes it manifests as:

------------[ cut here ]------------
WARNING: CPU: 1 PID: 6548 at lib/list_debug.c:42 __list_add+0x1db/0x250()
list_add corruption. prev->next should be next (ffff8800697c10e8), but
was ffff880030441fd0. (prev=ffff880030441fd0).
Modules linked in:
CPU: 1 PID: 6548 Comm: a.out Not tainted 4.5.0-rc2+ #309
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011
00000000ffffffff ffff88002fa17918 ffffffff82be2c8d ffff88002fa17988
ffff88002fd62f80 ffffffff86acf380 ffff88002fa17958 ffffffff81355139
ffffffff82c4a78b ffffed0005f42f2d ffffffff86acf380 000000000000002a
Call Trace:
[< inline >] __dump_stack lib/dump_stack.c:15
[<ffffffff82be2c8d>] dump_stack+0x6f/0xa2 lib/dump_stack.c:50
[<ffffffff81355139>] warn_slowpath_common+0xd9/0x140 kernel/panic.c:482
[<ffffffff81355249>] warn_slowpath_fmt+0xa9/0xd0 kernel/panic.c:494
[<ffffffff82c4a78b>] __list_add+0x1db/0x250 lib/list_debug.c:39
[< inline >] list_move_tail include/linux/list.h:77
[<ffffffff85240fdc>] snd_timer_start1+0x6c/0x2c0 sound/core/timer.c:432
[<ffffffff85241a7b>] snd_timer_start+0x20b/0x2b0 sound/core/timer.c:499
[< inline >] snd_timer_user_start sound/core/timer.c:1799
[< inline >] __snd_timer_user_ioctl sound/core/timer.c:1883
[<ffffffff85245fb7>] snd_timer_user_ioctl+0x767/0x25f0 sound/core/timer.c:1904
[< inline >] vfs_ioctl fs/ioctl.c:43
[<ffffffff817fa9ec>] do_vfs_ioctl+0x18c/0xfb0 fs/ioctl.c:674
[< inline >] SYSC_ioctl fs/ioctl.c:689
[<ffffffff817fb89f>] SyS_ioctl+0x8f/0xc0 fs/ioctl.c:680
[<ffffffff8665ebb6>] entry_SYSCALL_64_fastpath+0x16/0x7a
arch/x86/entry/entry_64.S:185
---[ end trace 77da812dd399e78b ]---

On commit 34229b277480f46c1e9a19f027f30b074512e68b + recent fixes from Takashi.

Takashi Iwai

unread,
Feb 4, 2016, 11:24:14 AM2/4/16
to Dmitry Vyukov, alsa-...@alsa-project.org, Jaroslav Kysela, LKML, Alexander Potapenko, Kostya Serebryany, syzkaller, Sasha Levin
Thanks for the report. This is indeed an old bug, but it appears now
more easily due to the recent fix to avoid double-stop. Below is the
fix patch. topic/core-fixes branch was updated with this, too.


Takashi

-- 8< --
From: Takashi Iwai <ti...@suse.de>
Subject: [PATCH] ALSA: timer: Fix leftover link at closing

In ALSA timer core, the active timer instance is managed in
active_list linked list. Each element is added / removed dynamically
at timer start, stop and in timer interrupt. The problem is that
snd_timer_interrupt() has a thinko and leaves the element in
active_list when it's the last opened element. This eventually leads
to list corruption or use-after-free error.

This hasn't been revealed because we used to delete the list forcibly
in snd_timer_stop() in the past. However, the recent fix avoids the
double-stop behavior (in commit [f784beb75ce8: ALSA: timer: Fix link
corruption due to double start or stop]), and this leak hits reality.

This patch fixes the link management in snd_timer_interrupt(). Now it
simply unlinks no matter which stream is.

BugLink: http://lkml.kernel.org/r/CACT4Y+Yy2aukHP-EDp8-ziNqNNmb-NTf=jDWXMP7j...@mail.gmail.com
Reported-by: Dmitry Vyukov <dvy...@google.com>
Cc: <sta...@vger.kernel.org>
Signed-off-by: Takashi Iwai <ti...@suse.de>
---
sound/core/timer.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/sound/core/timer.c b/sound/core/timer.c
index b419e612f987..9b513a05765a 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -744,8 +744,8 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
ti->cticks = ti->ticks;
} else {
ti->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
- if (--timer->running)
- list_del_init(&ti->active_list);
+ --timer->running;
+ list_del_init(&ti->active_list);
}
if ((timer->hw.flags & SNDRV_TIMER_HW_TASKLET) ||
(ti->flags & SNDRV_TIMER_IFLG_FAST))
--
2.7.0


Dmitry Vyukov

unread,
Feb 4, 2016, 11:32:51 AM2/4/16
to Takashi Iwai, alsa-...@alsa-project.org, Jaroslav Kysela, LKML, Alexander Potapenko, Kostya Serebryany, syzkaller, Sasha Levin
Fixes the crash for me.
I think this was the last one in sound that happened periodically
during fuzzing. Let's see what the fuzzer will say now :)
Thanks for quick fixes!

Takashi Iwai

unread,
Feb 4, 2016, 11:34:54 AM2/4/16
to Dmitry Vyukov, alsa-...@alsa-project.org, Jaroslav Kysela, LKML, Alexander Potapenko, Kostya Serebryany, syzkaller, Sasha Levin
Great, thanks for your patient reports and testing!


Takashi
Reply all
Reply to author
Forward
0 new messages