NULL-ptr deref in irqfd_update (via KVM)

From: Robert ÅwiÄcki
Date: Fri Feb 19 2016 - 21:07:27 EST


Works under both AMD and Intel

[7]kdb> summary
sysname Linux
release 4.5.0-rc4
version #1 SMP PREEMPT Tue Feb 16 04:24:23 CET 2016
machine x86_64
nodename jag-l2
domainname (none)
ccversion CCVERSION
date 2016-02-20 01:57:30 tz_minuteswest 0
uptime 00:06
load avg 0.35 0.14 0.04

MemTotal: 16409820 kB
MemFree: 16054940 kB
Buffers: 37564 kB


[ 70.223390] BUG: unable to handle kernel NULL pointer dereference
at 0000000000000120
[ 70.224535] IP: [<ffffffff81009f69>] kvm_irq_map_gsi+0x12/0x5a
[ 70.225400] PGD bd3ca067 PUD bd0ed067 PMD 0
[ 70.226018] Oops: 0000 [#1] PREEMPT SMP

Entering kdb (current=0xffff88042a4ae040, pid 4403) on processor 7 Oops: (null)
due to oops @ 0xffffffff81009f69
CPU: 7 PID: 4403 Comm: o Not tainted 4.5.0-rc4 #1
Hardware name: To be filled by O.E.M. To be filled by O.E.M./Crosshair
V Formula, BIOS 1703 10/17/2012
task: ffff88042a4ae040 ti: ffff8800bb0ec000 task.ti: ffff8800bb0ec000
RIP: 0010:[<ffffffff81009f69>] [<ffffffff81009f69>] kvm_irq_map_gsi+0x12/0x5a
RSP: 0018:ffff8800bb0efc90 EFLAGS: 00010046
RAX: 0000000000000000 RBX: ffff8804228fc000 RCX: 0000000000000000
RDX: 0000000000000003 RSI: ffff8800bb0efca0 RDI: ffff8800bb1dc000
RBP: ffff8800bb0efc90 R08: 0000000000000007 R09: 00000000000000ff
R10: ffff8800bb0efd70 R11: 0000000000000212 R12: ffff8800bb0efca0
R13: 0000000000000001 R14: ffff8804228fc000 R15: ffff8800bb1dca50
FS: 00007f44d4e1b700(0000) GS:ffff88043d5c0000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000000000000120 CR3: 00000000bb018000 CR4: 00000000000406e0
Stack:
ffff8800bb0efd78 ffffffff81008e6d ffff8800bb1dc158 ffff8800bb1dc120
ffff8800bb0efce0 ffff8800bb1dc101 0000000000000002 ffff8804228fc0a8
ffff8800bb0efd50 ffffffff81168356 0000000000000000 ffffffff81167aed
Call Trace:
[<ffffffff81008e6d>] irqfd_update+0x37/0x78
[<ffffffff81168356>] ? __synchronize_srcu+0xdf/0x12a
[<ffffffff81167aed>] ? trace_raw_output_rcu_utilization+0x60/0x60
[<ffffffff813ca0cd>] ? __this_cpu_preempt_check+0x13/0x15
[<ffffffff811685f0>] ? __srcu_read_lock+0x43/0x56
[<ffffffff81009ae1>] kvm_irqfd+0x3f2/0x53f
[<ffffffff810066a1>] kvm_vm_ioctl+0x2f9/0x65f
[<ffffffff8115b065>] ? __raw_write_unlock+0x11/0x23
[<ffffffff81ba6c16>] ? _raw_spin_unlock+0xe/0x10
[<ffffffff812315dd>] vfs_ioctl+0x18/0x34
[<ffffffff81231fae>] do_vfs_ioctl+0x493/0x514
[<ffffffff8108e1f1>] ? __do_page_fault+0x2b0/0x324
[<ffffffff81232086>] SyS_ioctl+0x57/0x79
[<ffffffff81ba70b2>] entry_SYSCALL_64_fastpath+0x16/0x71
Code: 5b 41 5c 41 5d 41 5e 5d c3 55 be c0 80 40 02 48 89 e5 e8 d9 a2
20 00 5d c3 0f 1f 44 00 00 55 48 8b 8f 08 2e 00 00 31 c0 48 89 e5 <39>
91 20 01 00 00 76 3e 48 63 d2 49 89 f1 48 8b 94 d1 28 01 00

Testcase:
===================================
// autogenerated by syzkaller (http://github.com/google/syzkaller)
#include <unistd.h>
#include <sys/syscall.h>
#include <string.h>
#include <stdint.h>
#include <pthread.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_eventfd
#define SYS_eventfd 284
#endif

long r[26];

int main()
{
memset(r, -1, sizeof(r));
r[0] = syscall(SYS_mmap, 0x20000000ul, 0x17000ul, 0x3ul, 0x32ul,
0xfffffffffffffffful, 0x0ul);
memcpy((void*)0x2000c000, "\x2f\x64\x65\x76\x2f\x6b\x76\x6d\x00", 9);
r[2] =
syscall(SYS_open, 0x2000c000ul, 0x0ul, 0x400ul, 0, 0, 0);
r[3] = syscall(SYS_ioctl, r[2], 0xae01ul, 0x0ul, 0, 0, 0);
r[4] = syscall(SYS_eventfd, 0x5ul, 0, 0, 0, 0, 0);
*(uint32_t*)0x2000cfef = r[4];
*(uint32_t*)0x2000cff3 = (uint32_t)0x3;
*(uint32_t*)0x2000cff7 = (uint32_t)0x2;
*(uint32_t*)0x2000cffb = r[4];
*(uint8_t*)0x2000cfff = (uint8_t)0x0;
*(uint8_t*)0x2000d000 = (uint8_t)0x0;
*(uint8_t*)0x2000d001 = (uint8_t)0x0;
*(uint8_t*)0x2000d002 = (uint8_t)0x0;
*(uint8_t*)0x2000d003 = (uint8_t)0x0;
*(uint8_t*)0x2000d004 = (uint8_t)0x0;
*(uint8_t*)0x2000d005 = (uint8_t)0x0;
*(uint8_t*)0x2000d006 = (uint8_t)0x0;
*(uint8_t*)0x2000d007 = (uint8_t)0x0;
*(uint8_t*)0x2000d008 = (uint8_t)0x0;
*(uint8_t*)0x2000d009 = (uint8_t)0x0;
*(uint8_t*)0x2000d00a = (uint8_t)0x0;
*(uint8_t*)0x2000d00b = (uint8_t)0x0;
*(uint8_t*)0x2000d00c = (uint8_t)0x0;
*(uint8_t*)0x2000d00d = (uint8_t)0x0;
*(uint8_t*)0x2000d00e = (uint8_t)0x0;
r[25] = syscall(SYS_ioctl, r[3], 0x4020ae76ul, 0x2000cfeful, 0, 0, 0);
return 0;
}
===================================

>From KGDB:
>>> target remote /dev/ttyUSB0
Remote debugging using /dev/ttyUSB0
Ignoring packet error, continuing...
warning: unrecognized item "timeout" in "qSupported" response
kvm_irq_map_gsi (kvm=0xffff8800bb1dc000, entries=0xffff8800bb0efca0,
gsi=3) at arch/x86/kvm/../../../virt/kvm/irqchip.c:43
43 if (gsi < irq_rt->nr_rt_entries) {
âââ Assembly ââââââââ
0xffffffff81009f5d kvm_irq_map_gsi+6 mov 0x2e08(%rdi),%rcx
0xffffffff81009f64 kvm_irq_map_gsi+13 xor %eax,%eax
0xffffffff81009f66 kvm_irq_map_gsi+15 mov %rsp,%rbp
0xffffffff81009f69 kvm_irq_map_gsi+18 cmp %edx,0x120(%rcx) <--
THIS INSN CAUSES GPF
0xffffffff81009f6f kvm_irq_map_gsi+24 jbe 0xffffffff81009faf
<kvm_irq_map_gsi+88>
0xffffffff81009f71 kvm_irq_map_gsi+26 movslq %edx,%rdx
0xffffffff81009f74 kvm_irq_map_gsi+29 mov %rsi,%r9
âââ Expressions âââââââ
âââ History ââââââââââ
âââ Memory ââââââââââ
âââ Registers âââââââââ
rax 0x0000000000000000 rbx 0xffff8804228fc000 rcx
0x0000000000000000 rdx 0x0000000000000003
rsi 0xffff8800bb0efca0 rdi 0xffff8800bb1dc000 rbp
0xffff8800bb0efc90 rsp 0xffff8800bb0efc90
r8 0x0000000000000007 r9 0x00000000000000ff r10
0xffff8800bb0efd70 r11 0x0000000000000212
r12 0xffff8800bb0efca0 r13 0x0000000000000001 r14
0xffff8804228fc000 r15 0xffff8800bb1dca50
rip 0xffffffff81009f69 eflags [ PF ZF RF ] cs
0x00000010 ss 0x00000018
ds 0x00000000 es 0x00000000 fs
0x00000000 gs 0x00000000
âââ Source ââââââââââ
38 struct kvm_kernel_irq_routing_entry *e;
39 int n = 0;
40
41 irq_rt = srcu_dereference_check(kvm->irq_routing, &kvm->irq_srcu,
42 lockdep_is_held(&kvm->irq_lock));
43 if (gsi < irq_rt->nr_rt_entries) { <--- THIS LINE CAUSES GPF
44 hlist_for_each_entry(e, &irq_rt->map[gsi], link) {
45 entries[n] = *e;
46 ++n;
47 }
âââ Stack âââââ
[0] from 0xffffffff81009f69 in kvm_irq_map_gsi+18 at
arch/x86/kvm/../../../virt/kvm/irqchip.c:43
arg kvm = 0xffff8800bb1dc000
arg entries = 0xffff8800bb0efca0
arg gsi = 3
[1] from 0xffffffff81008e6d in irqfd_update+55 at
arch/x86/kvm/../../../virt/kvm/eventfd.c:251
arg kvm = <optimized out>
arg irqfd = 0xffff8804228fc000
[+]

>>> bt
#0 kvm_irq_map_gsi (kvm=0xffff8800bb1dc000,
entries=0xffff8800bb0efca0, gsi=3) at
arch/x86/kvm/../../../virt/kvm/irqchip.c:43
#1 0xffffffff81008e6d in irqfd_update (kvm=<optimized out>,
irqfd=0xffff8804228fc000) at
arch/x86/kvm/../../../virt/kvm/eventfd.c:251
#2 0xffffffff81009ae1 in kvm_irqfd_assign (args=<optimized out>,
kvm=<optimized out>) at arch/x86/kvm/../../../virt/kvm/eventfd.c:389
#3 kvm_irqfd (kvm=0xffff8800bb1dc000, args=<optimized out>) at
arch/x86/kvm/../../../virt/kvm/eventfd.c:570
#4 0xffffffff810066a1 in kvm_vm_ioctl (filp=<optimized out>,
ioctl=1075883638, arg=536924143) at
arch/x86/kvm/../../../virt/kvm/kvm_main.c:2792
#5 0xffffffff812315dd in vfs_ioctl (filp=<optimized out>,
cmd=<optimized out>, arg=<optimized out>) at fs/ioctl.c:43
#6 0xffffffff81231fae in do_vfs_ioctl (filp=0xffff8804228c8100,
fd=<optimized out>, cmd=<optimized out>, arg=536924143) at
fs/ioctl.c:674
#7 0xffffffff81232086 in SYSC_ioctl (arg=<optimized out>,
cmd=<optimized out>, fd=<optimized out>) at fs/ioctl.c:689
#8 SyS_ioctl (fd=4, cmd=1075883638, arg=536924143) at fs/ioctl.c:680
#9 0xffffffff81ba70b2 in entry_SYSCALL_64_fastpath () at
arch/x86/entry/entry_64.S:185

--
Robert ÅwiÄcki