Re: sound: use-after-free in snd_seq_deliver_single_event

Dmitry Vyukov
2016-02-01T11:12:43Z

On Mon, Feb 1, 2016 at 11:57 AM, Takashi Iwai <tiwai@suse.de> wrote:
> On Mon, 01 Feb 2016 11:26:57 +0100,
> Dmitry Vyukov wrote:
>>
>> Hello,
>>
>> The following program triggers use-after-free in
>> snd_seq_deliver_single_event (run in a tight parallel loop):
>>
>> // autogenerated by syzkaller (http://github.com/google/syzkaller)
>> #include <pthread.h>
>> #include <stdint.h>
>> #include <string.h>
>> #include <sys/syscall.h>
>> #include <unistd.h>
>>
>> #ifndef SYS_mmap
>> #define SYS_mmap 9
>> #endif
>> #ifndef SYS_syz_open_dev
>> #define SYS_syz_open_dev 1000001
>> #endif
>> #ifndef SYS_ioctl
>> #define SYS_ioctl 16
>> #endif
>> #ifndef SYS_getpgrp
>> #define SYS_getpgrp 111
>> #endif
>> #ifndef SYS_migrate_pages
>> #define SYS_migrate_pages 256
>> #endif
>> #ifndef SYS_read
>> #define SYS_read 0
>> #endif
>> #ifndef SYS_write
>> #define SYS_write 1
>> #endif
>> #ifndef SYS_dup3
>> #define SYS_dup3 292
>> #endif
>>
>> long r[59];
>>
>> void* thr(void* arg)
>> {
>> switch ((long)arg) {
>> case 0:
>> r[0] = syscall(SYS_mmap, 0x20000000ul, 0x47000ul, 0x3ul, 0x32ul,
>> 0xfffffffffffffffful, 0x0ul);
>> break;
>> case 1:
>> memcpy((void*)0x20000000,
>> "\x2f\x64\x65\x76\x2f\x73\x65\x71\x75\x65\x6e\x63\x65\x72",
>> 14);
>> r[2] =
>> syscall(SYS_open, "/dev/sequencer", 0x2ul, 0, 0, 0);
>> break;
>> case 2:
>> r[3] = syscall(SYS_mmap, 0x20047000ul, 0x1000ul, 0x3ul, 0x32ul,
>> 0xfffffffffffffffful, 0x0ul);
>> break;
>> case 3:
>> r[4] = syscall(SYS_ioctl, r[2], 0x540ful, 0x20047ffcul, 0, 0, 0);
>> if (r[4] != -1)
>> r[5] = *(uint32_t*)0x20047ffc;
>> break;
>> case 4:
>> r[6] = syscall(SYS_getpgrp, r[5], 0, 0, 0, 0, 0);
>> break;
>> case 5:
>> r[7] = syscall(SYS_mmap, 0x20047000ul, 0x1000ul, 0x3ul, 0x32ul,
>> 0xfffffffffffffffful, 0x0ul);
>> break;
>> case 6:
>> r[8] = syscall(SYS_mmap, 0x20047000ul, 0x1000ul, 0x3ul, 0x32ul,
>> 0xfffffffffffffffful, 0x0ul);
>> break;
>> case 7:
>> *(uint64_t*)0x20047000 = (uint64_t)0x5;
>> *(uint64_t*)0x20047000 = (uint64_t)0xfffffffffffffff8;
>> r[11] = syscall(SYS_migrate_pages, r[6], 0xfful, 0x20047000ul,
>> 0x20047000ul, 0, 0);
>> break;
>> case 8:
>> memcpy((void*)0x20043684,
>> "\x2f\x64\x65\x76\x2f\x64\x6d\x6d\x69\x64\x69\x23", 12);
>> r[13] = syscall(SYS_open, "/dev/dmmidi2", 0x8a000ul, 0, 0, 0);
>> break;
>> case 9:
>> r[14] = syscall(SYS_read, r[13], 0x20045000ul, 0x36ul, 0, 0, 0);
>> break;
>> case 10:
>> *(uint8_t*)0x2000042c = (uint8_t)0x0;
>> *(uint8_t*)0x2000042d = (uint8_t)0x9cf;
>> *(uint8_t*)0x2000042e = (uint8_t)0x9;
>> *(uint8_t*)0x2000042f = (uint8_t)0x0;
>> *(uint64_t*)0x20000444 = (uint64_t)0x0;
>> *(uint64_t*)0x2000044c = (uint64_t)0x989680;
>> *(uint8_t*)0x20000454 = (uint8_t)0x2;
>> *(uint8_t*)0x20000455 = (uint8_t)0x100000000;
>> *(uint8_t*)0x20000456 = (uint8_t)0x8d;
>> *(uint8_t*)0x20000457 = (uint8_t)0x7;
>> *(uint8_t*)0x20000464 = (uint8_t)0x51b2;
>> *(uint8_t*)0x20000465 = (uint8_t)0xfffffffffffff15a;
>> *(uint8_t*)0x20000466 = (uint8_t)0x4;
>> *(uint8_t*)0x20000467 = (uint8_t)0x4;
>> *(uint32_t*)0x20000468 = (uint32_t)0xffffffff;
>> *(uint8_t*)0x2000046c = (uint8_t)0x5;
>> *(uint8_t*)0x2000046d = (uint8_t)0xfffffffffffffff8;
>> *(uint8_t*)0x2000046e = (uint8_t)0x401;
>> *(uint8_t*)0x2000046f = (uint8_t)0x5;
>> *(uint64_t*)0x20000484 = (uint64_t)0x4;
>> *(uint64_t*)0x2000048c = (uint64_t)0x0;
>> *(uint8_t*)0x20000494 = (uint8_t)0xc2c1;
>> *(uint8_t*)0x20000495 = (uint8_t)0x1;
>> *(uint8_t*)0x20000496 = (uint8_t)0x6b;
>> *(uint8_t*)0x20000497 = (uint8_t)0x6;
>> *(uint8_t*)0x200004a0 = (uint8_t)0xfffffffffffffffa;
>> *(uint8_t*)0x200004a1 = (uint8_t)0x81;
>> *(uint8_t*)0x200004a2 = (uint8_t)0x0;
>> *(uint8_t*)0x200004a3 = (uint8_t)0xa6;
>> *(uint8_t*)0x200004a4 = (uint8_t)0x400;
>> *(uint8_t*)0x200004a5 = (uint8_t)0x3ff;
>> *(uint8_t*)0x200004a6 = (uint8_t)0x8;
>> *(uint8_t*)0x200004a7 = (uint8_t)0x3;
>> *(uint64_t*)0x200004bc = (uint64_t)0x0;
>> *(uint64_t*)0x200004c4 = (uint64_t)0x0;
>> *(uint8_t*)0x200004cc = (uint8_t)0x9e;
>> *(uint8_t*)0x200004cd = (uint8_t)0x7ff;
>> *(uint8_t*)0x200004ce = (uint8_t)0x7fff;
>> *(uint8_t*)0x200004cf = (uint8_t)0x9;
>> *(uint8_t*)0x200004e0 = (uint8_t)0xa;
>> *(uint32_t*)0x200004e4 = (uint32_t)0x6;
>> *(uint32_t*)0x200004e8 = (uint32_t)0xffffffff;
>> r[57] = syscall(SYS_write, r[2], 0x2000042cul, 0x78ul, 0, 0, 0);
>> break;
>> case 11:
>> r[58] = syscall(SYS_dup3, r[13], r[2], 0x80000ul, 0, 0, 0);
>> break;
>> }
>> return 0;
>> }
>>
>> int main()
>> {
>> long i;
>> pthread_t th[12];
>>
>> memset(r, -1, sizeof(r));
>> for (i = 0; i < 12; i++) {
>> pthread_create(&th[i], 0, thr, (void*)i);
>> usleep(10000);
>> }
>> for (i = 0; i < 12; i++) {
>> pthread_create(&th[i], 0, thr, (void*)i);
>> if (i % 2 == 0)
>> usleep(10000);
>> }
>> usleep(100000);
>> return 0;
>> }
>>
>>
>> ==================================================================
>> BUG: KASAN: use-after-free in do_raw_spin_lock+0x281/0x2b0 at addr
>> ffff88002abf1654
>> Read of size 4 by task syz-executor/13987
>> =============================================================================
>> BUG kmalloc-96 (Not tainted): kasan: bad access detected
>> -----------------------------------------------------------------------------
>>
>> INFO: Allocated in snd_midi_event_new+0x73/0x200 age=10 cpu=0 pid=13996
>> [< none >] ___slab_alloc+0x564/0x5b0 mm/slub.c:2470
>> [< none >] __slab_alloc+0x66/0xc0 mm/slub.c:2499
>> [< 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_midi_event_new+0x73/0x200
>> sound/core/seq/seq_midi_event.c:120
>> [< none >] snd_virmidi_input_open+0xfd/0x380
>> sound/core/seq/seq_virmidi.c:214
>> [< none >] open_substream+0x3eb/0x780 sound/core/rawmidi.c:269
>> [< none >] rawmidi_open_priv+0x144/0x300 sound/core/rawmidi.c:312
>> [< none >] snd_rawmidi_open+0x3fb/0xa90 sound/core/rawmidi.c:416
>> [< none >] soundcore_open+0x30f/0x640 sound/sound_core.c:639
>> [< none >] chrdev_open+0x22a/0x4c0 fs/char_dev.c:388
>> [< none >] do_dentry_open+0x6a2/0xcb0 fs/open.c:736
>> [< none >] vfs_open+0x17b/0x1f0 fs/open.c:853
>> [< inline >] do_last fs/namei.c:3254
>> [< none >] path_openat+0xde9/0x5e30 fs/namei.c:3386
>> [< none >] do_filp_open+0x18e/0x250 fs/namei.c:3421
>> [< none >] do_sys_open+0x1fc/0x420 fs/open.c:1022
>> [< inline >] SYSC_open fs/open.c:1040
>> [< none >] SyS_open+0x2d/0x40 fs/open.c:1035
>>
>> INFO: Freed in snd_midi_event_free+0x43/0x60 age=17 cpu=0 pid=13996
>> [< none >] __slab_free+0x1fc/0x320 mm/slub.c:2680
>> [< inline >] slab_free mm/slub.c:2835
>> [< none >] kfree+0x2ac/0x2c0 mm/slub.c:3664
>> [< none >] snd_midi_event_free+0x43/0x60
>> sound/core/seq/seq_midi_event.c:142
>> [< none >] snd_virmidi_input_close+0x87/0x120
>> sound/core/seq/seq_virmidi.c:261
>> [< none >] close_substream.part.14+0xda/0x540 sound/core/rawmidi.c:486
>> [< inline >] close_substream sound/core/rawmidi.c:514
>> [< none >] rawmidi_release_priv+0x1e4/0x250 sound/core/rawmidi.c:504
>> [< none >] snd_rawmidi_release+0x62/0xf0 sound/core/rawmidi.c:539
>> [< none >] __fput+0x236/0x780 fs/file_table.c:208
>> [< none >] ____fput+0x15/0x20 fs/file_table.c:244
>> [< none >] task_work_run+0x170/0x210 kernel/task_work.c:115
>> [< inline >] exit_task_work include/linux/task_work.h:21
>> [< none >] do_exit+0x8b5/0x2cb0 kernel/exit.c:748
>> [< none >] do_group_exit+0x108/0x330 kernel/exit.c:878
>> [< none >] get_signal+0x5e4/0x14f0 kernel/signal.c:2307
>> [< none >] do_signal+0x83/0x1c90 arch/x86/kernel/signal.c:712
>> [< none >] exit_to_usermode_loop+0x1a5/0x210
>> arch/x86/entry/common.c:247
>> [< inline >] prepare_exit_to_usermode arch/x86/entry/common.c:282
>> [< none >] syscall_return_slowpath+0x2ba/0x340
>> arch/x86/entry/common.c:344
>>
>> INFO: Slab 0xffffea0000aafc00 objects=28 used=11 fp=0xffff88002abf0470
>> flags=0x1fffc0000004080
>> INFO: Object 0xffff88002abf1630 @offset=5680 fp=0xffff88002abf06a8
>> CPU: 1 PID: 13987 Comm: syz-executor Tainted: G B 4.5.0-rc1+ #305
>> Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011
>> 00000000ffffffff ffff880061aaf7d8 ffffffff82be11ad ffff88003e804900
>> ffff88002abf1630 ffff88002abf0000 ffff880061aaf808 ffffffff8175b454
>> ffff88003e804900 ffffea0000aafc00 ffff88002abf1630 000000000000000a
>>
>> Call Trace:
>> [<ffffffff81764e3e>] __asan_report_load4_noabort+0x3e/0x40
>> mm/kasan/report.c:294
>> [< inline >] debug_spin_lock_before kernel/locking/spinlock_debug.c:83
>> [<ffffffff814663d1>] do_raw_spin_lock+0x281/0x2b0
>> kernel/locking/spinlock_debug.c:135
>> [< inline >] __raw_spin_lock_irqsave
>> include/linux/spinlock_api_smp.h:119
>> [<ffffffff86652b67>] _raw_spin_lock_irqsave+0xa7/0xd0
>> kernel/locking/spinlock.c:159
>> [<ffffffff852bb6a7>] snd_midi_event_decode+0x1f7/0x5c0
>> sound/core/seq/seq_midi_event.c:391
>> [< inline >] snd_virmidi_dev_receive_event
>> sound/core/seq/seq_virmidi.c:95
>> [<ffffffff852ce754>] snd_virmidi_event_input+0x214/0x310
>> sound/core/seq/seq_virmidi.c:133
>> [<ffffffff852a7934>]
>> snd_seq_deliver_single_event.constprop.11+0x3f4/0x740
>> sound/core/seq/seq_clientmgr.c:634
>> [<ffffffff852a7da2>] snd_seq_deliver_event+0x122/0x800
>> sound/core/seq/seq_clientmgr.c:828
>> [<ffffffff852a9186>] snd_seq_kernel_client_dispatch+0x126/0x170
>> sound/core/seq/seq_clientmgr.c:2401
>> [< inline >] snd_seq_oss_dispatch
>> sound/core/seq/oss/seq_oss_device.h:151
>> [<ffffffff852ca423>] snd_seq_oss_midi_reset+0x303/0x4a0
>> sound/core/seq/oss/seq_oss_midi.c:481
>> [<ffffffff852bd490>] snd_seq_oss_reset+0x130/0x260
>> sound/core/seq/oss/seq_oss_init.c:469
>> [<ffffffff852bd631>] snd_seq_oss_release+0x71/0x130
>> sound/core/seq/oss/seq_oss_init.c:425
>> [<ffffffff852bbcca>] odev_release+0x5a/0x80 sound/core/seq/oss/seq_oss.c:155
>> [<ffffffff817c0156>] __fput+0x236/0x780 fs/file_table.c:208
>> [<ffffffff817c0725>] ____fput+0x15/0x20 fs/file_table.c:244
>> [<ffffffff813b14e0>] task_work_run+0x170/0x210 kernel/task_work.c:115
>> [< inline >] tracehook_notify_resume include/linux/tracehook.h:191
>> [<ffffffff810066b1>] exit_to_usermode_loop+0x1d1/0x210
>> arch/x86/entry/common.c:251
>> [< inline >] prepare_exit_to_usermode arch/x86/entry/common.c:282
>> [<ffffffff810084ea>] syscall_return_slowpath+0x2ba/0x340
>> arch/x86/entry/common.c:344
>> [<ffffffff86653322>] int_ret_from_sys_call+0x25/0x9f
>> arch/x86/entry/entry_64.S:281
>
> Looks like a race at closing virmidi device.
> Does the patch below fix it?

This seems to help.

> thanks,
>
> Takashi
>
> ---
> diff --git a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c
> index 3da2d48610b3..f71aedfb408c 100644
> --- a/sound/core/seq/seq_virmidi.c
> +++ b/sound/core/seq/seq_virmidi.c
> @@ -254,9 +254,13 @@ static int snd_virmidi_output_open(struct snd_rawmidi_substream *substream)
> */
> static int snd_virmidi_input_close(struct snd_rawmidi_substream *substream)
> {
> + struct snd_virmidi_dev *rdev = substream->rmidi->private_data;
> struct snd_virmidi *vmidi = substream->runtime->private_data;
> - snd_midi_event_free(vmidi->parser);
> +
> + write_lock_irq(&rdev->filelist_lock);
> list_del(&vmidi->list);
> + write_unlock_irq(&rdev->filelist_lock);
> + snd_midi_event_free(vmidi->parser);
> substream->runtime->private_data = NULL;
> kfree(vmidi);
> return 0;