aboutsummaryrefslogtreecommitdiffstats
path: root/executor
diff options
context:
space:
mode:
Diffstat (limited to 'executor')
-rw-r--r--executor/common.h9
-rw-r--r--executor/common_kvm.h765
-rw-r--r--executor/executor.cc2
-rw-r--r--executor/kvm.S315
-rw-r--r--executor/kvm.S.h10
-rw-r--r--executor/kvm.h75
-rw-r--r--executor/kvm_gen.cc33
-rw-r--r--executor/test.go13
-rw-r--r--executor/test_kvm.cc222
-rw-r--r--executor/test_test.go16
10 files changed, 1460 insertions, 0 deletions
diff --git a/executor/common.h b/executor/common.h
index 3b6b27297..ba0303752 100644
--- a/executor/common.h
+++ b/executor/common.h
@@ -22,6 +22,7 @@
#include <linux/capability.h>
#include <linux/if.h>
#include <linux/if_tun.h>
+#include <linux/kvm.h>
#include <linux/sched.h>
#include <net/if_arp.h>
@@ -367,6 +368,10 @@ static uintptr_t syz_fuseblk_mount(uintptr_t a0, uintptr_t a1, uintptr_t a2, uin
}
#endif
+#ifdef __NR_syz_kvm_setup_cpu
+#include "common_kvm.h"
+#endif // #ifdef __NR_syz_kvm_setup_cpu
+
static uintptr_t execute_syscall(int nr, uintptr_t a0, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5, uintptr_t a6, uintptr_t a7, uintptr_t a8)
{
switch (nr) {
@@ -396,6 +401,10 @@ static uintptr_t execute_syscall(int nr, uintptr_t a0, uintptr_t a1, uintptr_t a
case __NR_syz_emit_ethernet:
return syz_emit_ethernet(a0, a1);
#endif
+#ifdef __NR_syz_kvm_setup_cpu
+ case __NR_syz_kvm_setup_cpu:
+ return syz_kvm_setup_cpu(a0, a1, a2, a3, a4, a5, a6, a7);
+#endif
}
}
diff --git a/executor/common_kvm.h b/executor/common_kvm.h
new file mode 100644
index 000000000..30055cb16
--- /dev/null
+++ b/executor/common_kvm.h
@@ -0,0 +1,765 @@
+// Copyright 2017 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+// This file is shared between executor and csource package.
+
+// Implementation of syz_kvm_setup_cpu pseudo-syscall.
+// See Intel Software Developer’s Manual Volume 3: System Programming Guide
+// for details on what happens here.
+
+#include "kvm.S.h"
+#include "kvm.h"
+
+#ifndef KVM_SMI
+#define KVM_SMI _IO(KVMIO, 0xb7)
+#endif
+
+#define CR0_PE 1
+#define CR0_MP (1 << 1)
+#define CR0_EM (1 << 2)
+#define CR0_TS (1 << 3)
+#define CR0_ET (1 << 4)
+#define CR0_NE (1 << 5)
+#define CR0_WP (1 << 16)
+#define CR0_AM (1 << 18)
+#define CR0_NW (1 << 29)
+#define CR0_CD (1 << 30)
+#define CR0_PG (1 << 31)
+
+#define CR4_VME 1
+#define CR4_PVI (1 << 1)
+#define CR4_TSD (1 << 2)
+#define CR4_DE (1 << 3)
+#define CR4_PSE (1 << 4)
+#define CR4_PAE (1 << 5)
+#define CR4_MCE (1 << 6)
+#define CR4_PGE (1 << 7)
+#define CR4_PCE (1 << 8)
+#define CR4_OSFXSR (1 << 8)
+#define CR4_OSXMMEXCPT (1 << 10)
+#define CR4_UMIP (1 << 11)
+#define CR4_VMXE (1 << 13)
+#define CR4_SMXE (1 << 14)
+#define CR4_FSGSBASE (1 << 16)
+#define CR4_PCIDE (1 << 17)
+#define CR4_OSXSAVE (1 << 18)
+#define CR4_SMEP (1 << 20)
+#define CR4_SMAP (1 << 21)
+#define CR4_PKE (1 << 22)
+
+#define EFER_SCE 1
+#define EFER_LME (1 << 8)
+#define EFER_LMA (1 << 10)
+#define EFER_NXE (1 << 11)
+#define EFER_SVME (1 << 12)
+#define EFER_LMSLE (1 << 13)
+#define EFER_FFXSR (1 << 14)
+#define EFER_TCE (1 << 15)
+
+// 32-bit page directory entry bits
+#define PDE32_PRESENT 1
+#define PDE32_RW (1 << 1)
+#define PDE32_USER (1 << 2)
+#define PDE32_PS (1 << 7)
+
+// 64-bit page * entry bits
+#define PDE64_PRESENT 1
+#define PDE64_RW (1 << 1)
+#define PDE64_USER (1 << 2)
+#define PDE64_ACCESSED (1 << 5)
+#define PDE64_DIRTY (1 << 6)
+#define PDE64_PS (1 << 7)
+#define PDE64_G (1 << 8)
+
+struct tss16 {
+ uint16_t prev;
+ uint16_t sp0;
+ uint16_t ss0;
+ uint16_t sp1;
+ uint16_t ss1;
+ uint16_t sp2;
+ uint16_t ss2;
+ uint16_t ip;
+ uint16_t flags;
+ uint16_t ax;
+ uint16_t cx;
+ uint16_t dx;
+ uint16_t bx;
+ uint16_t sp;
+ uint16_t bp;
+ uint16_t si;
+ uint16_t di;
+ uint16_t es;
+ uint16_t cs;
+ uint16_t ss;
+ uint16_t ds;
+ uint16_t ldt;
+} __attribute__((packed));
+
+struct tss32 {
+ uint16_t prev, prevh;
+ uint32_t sp0;
+ uint16_t ss0, ss0h;
+ uint32_t sp1;
+ uint16_t ss1, ss1h;
+ uint32_t sp2;
+ uint16_t ss2, ss2h;
+ uint32_t cr3;
+ uint32_t ip;
+ uint32_t flags;
+ uint32_t ax;
+ uint32_t cx;
+ uint32_t dx;
+ uint32_t bx;
+ uint32_t sp;
+ uint32_t bp;
+ uint32_t si;
+ uint32_t di;
+ uint16_t es, esh;
+ uint16_t cs, csh;
+ uint16_t ss, ssh;
+ uint16_t ds, dsh;
+ uint16_t fs, fsh;
+ uint16_t gs, gsh;
+ uint16_t ldt, ldth;
+ uint16_t trace;
+ uint16_t io_bitmap;
+} __attribute__((packed));
+
+struct tss64 {
+ uint32_t reserved0;
+ uint64_t rsp[3];
+ uint64_t reserved1;
+ uint64_t ist[7];
+ uint64_t reserved2;
+ uint32_t reserved3;
+ uint32_t io_bitmap;
+} __attribute__((packed));
+
+static void fill_segment_descriptor(uint64_t* dt, uint64_t* lt, struct kvm_segment* seg)
+{
+ uint16_t index = seg->selector >> 3;
+ uint64_t limit = seg->g ? seg->limit >> 12 : seg->limit;
+ uint64_t sd = (limit & 0xffff) | (seg->base & 0xffffff) << 16 | (uint64_t)seg->type << 40 | (uint64_t)seg->s << 44 | (uint64_t)seg->dpl << 45 | (uint64_t)seg->present << 47 | (limit & 0xf0000ULL) << 48 | (uint64_t)seg->avl << 52 | (uint64_t)seg->l << 53 | (uint64_t)seg->db << 54 | (uint64_t)seg->g << 55 | (seg->base & 0xff000000ULL) << 56;
+ NONFAILING(dt[index] = sd);
+ NONFAILING(lt[index] = sd);
+}
+
+static void fill_segment_descriptor_dword(uint64_t* dt, uint64_t* lt, struct kvm_segment* seg)
+{
+ fill_segment_descriptor(dt, lt, seg);
+ uint16_t index = seg->selector >> 3;
+ NONFAILING(dt[index + 1] = 0);
+ NONFAILING(lt[index + 1] = 0);
+}
+
+static void setup_syscall_msrs(int cpufd, uint16_t sel_cs, uint16_t sel_cs_cpl3)
+{
+ char buf[sizeof(struct kvm_msrs) + 5 * sizeof(struct kvm_msr_entry)];
+ memset(buf, 0, sizeof(buf));
+ struct kvm_msrs* msrs = (struct kvm_msrs*)buf;
+ msrs->nmsrs = 5;
+ msrs->entries[0].index = MSR_IA32_SYSENTER_CS;
+ msrs->entries[0].data = sel_cs;
+ msrs->entries[1].index = MSR_IA32_SYSENTER_ESP;
+ msrs->entries[1].data = ADDR_STACK0;
+ msrs->entries[2].index = MSR_IA32_SYSENTER_EIP;
+ msrs->entries[2].data = ADDR_VAR_SYSEXIT;
+ msrs->entries[3].index = MSR_IA32_STAR;
+ msrs->entries[3].data = ((uint64_t)sel_cs << 32) | ((uint64_t)sel_cs_cpl3 << 48);
+ msrs->entries[4].index = MSR_IA32_LSTAR;
+ msrs->entries[4].data = ADDR_VAR_SYSRET;
+ ioctl(cpufd, KVM_SET_MSRS, msrs);
+}
+
+static void setup_32bit_idt(struct kvm_sregs* sregs, char* host_mem, uintptr_t guest_mem)
+{
+ sregs->idt.base = guest_mem + ADDR_VAR_IDT;
+ sregs->idt.limit = 0x1ff;
+ uint64_t* idt = (uint64_t*)(host_mem + sregs->idt.base);
+ for (int i = 0; i < 32; i++) {
+ struct kvm_segment gate;
+ gate.selector = i << 3;
+ switch (i % 6) {
+ case 0:
+ // 16-bit interrupt gate
+ gate.type = 6;
+ gate.base = SEL_CS16;
+ break;
+ case 1:
+ // 16-bit trap gate
+ gate.type = 7;
+ gate.base = SEL_CS16;
+ break;
+ case 2:
+ // 16-bit task gate
+ gate.type = 3;
+ gate.base = SEL_TGATE16;
+ break;
+ case 3:
+ // 32-bit interrupt gate
+ gate.type = 14;
+ gate.base = SEL_CS32;
+ break;
+ case 4:
+ // 32-bit trap gate
+ gate.type = 15;
+ gate.base = SEL_CS32;
+ break;
+ case 6:
+ // 32-bit task gate
+ gate.type = 11;
+ gate.base = SEL_TGATE32;
+ break;
+ }
+ gate.limit = guest_mem + ADDR_VAR_USER_CODE2; // entry offset
+ gate.present = 1;
+ gate.dpl = 0;
+ gate.s = 0;
+ gate.g = 0;
+ gate.db = 0;
+ gate.l = 0;
+ gate.avl = 0;
+ fill_segment_descriptor(idt, idt, &gate);
+ }
+}
+
+static void setup_64bit_idt(struct kvm_sregs* sregs, char* host_mem, uintptr_t guest_mem)
+{
+ sregs->idt.base = guest_mem + ADDR_VAR_IDT;
+ sregs->idt.limit = 0x1ff;
+ uint64_t* idt = (uint64_t*)(host_mem + sregs->idt.base);
+ for (int i = 0; i < 32; i++) {
+ struct kvm_segment gate;
+ gate.selector = (i * 2) << 3;
+ gate.type = (i & 1) ? 14 : 15; // interrupt or trap gate
+ gate.base = SEL_CS64;
+ gate.limit = guest_mem + ADDR_VAR_USER_CODE2; // entry offset
+ gate.present = 1;
+ gate.dpl = 0;
+ gate.s = 0;
+ gate.g = 0;
+ gate.db = 0;
+ gate.l = 0;
+ gate.avl = 0;
+ fill_segment_descriptor_dword(idt, idt, &gate);
+ }
+}
+
+struct kvm_text {
+ uintptr_t typ;
+ const void* text;
+ uintptr_t size;
+};
+
+struct kvm_opt {
+ uint64_t typ;
+ uint64_t val;
+};
+
+#define KVM_SETUP_PAGING (1 << 0)
+#define KVM_SETUP_PAE (1 << 1)
+#define KVM_SETUP_PROTECTED (1 << 2)
+#define KVM_SETUP_CPL3 (1 << 3)
+#define KVM_SETUP_VIRT86 (1 << 4)
+#define KVM_SETUP_SMM (1 << 5)
+#define KVM_SETUP_VM (1 << 6)
+
+// syz_kvm_setup_cpu(fd fd_kvmvm, cpufd fd_kvmcpu, usermem vma[24], text ptr[in, array[kvm_text, 1]], ntext len[text], flags flags[kvm_setup_flags], opts ptr[in, array[kvm_setup_opt, 0:2]], nopt len[opts])
+static uintptr_t syz_kvm_setup_cpu(uintptr_t a0, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5, uintptr_t a6, uintptr_t a7)
+{
+ const int vmfd = a0;
+ const int cpufd = a1;
+ char* const host_mem = (char*)a2;
+ const struct kvm_text* const text_array_ptr = (struct kvm_text*)a3;
+ const uintptr_t text_count = a4;
+ const uintptr_t flags = a5;
+ const struct kvm_opt* const opt_array_ptr = (struct kvm_opt*)a6;
+ uintptr_t opt_count = a7;
+
+ const uintptr_t page_size = 4 << 10;
+ const uintptr_t ioapic_page = 10;
+ const uintptr_t guest_mem_size = 24 * page_size;
+ const uintptr_t guest_mem = 0;
+
+ if (text_count != 1)
+ fail("syz_kvm_setup_cpu: bad text count %d, want 1", text_count);
+ int text_type = 0;
+ const void* text = 0;
+ int text_size = 0;
+ NONFAILING(text_type = text_array_ptr[0].typ);
+ NONFAILING(text = text_array_ptr[0].text);
+ NONFAILING(text_size = text_array_ptr[0].size);
+
+ uintptr_t i;
+ for (i = 0; i < guest_mem_size / page_size; i++) {
+ struct kvm_userspace_memory_region memreg;
+ memreg.slot = i;
+ memreg.flags = 0; // can be KVM_MEM_LOG_DIRTY_PAGES | KVM_MEM_READONLY
+ memreg.guest_phys_addr = guest_mem + i * page_size;
+ if (i == ioapic_page)
+ memreg.guest_phys_addr = 0xfec00000;
+ memreg.memory_size = page_size;
+ memreg.userspace_addr = (uintptr_t)host_mem + i * page_size;
+ ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, &memreg);
+ }
+ // SMRAM
+ struct kvm_userspace_memory_region memreg;
+ memreg.slot = 1 + (1 << 16);
+ memreg.flags = 0;
+ memreg.guest_phys_addr = 0x30000;
+ memreg.memory_size = 64 << 10;
+ memreg.userspace_addr = (uintptr_t)host_mem;
+ ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, &memreg);
+
+ struct kvm_sregs sregs;
+ if (ioctl(cpufd, KVM_GET_SREGS, &sregs))
+ return -1;
+
+ struct kvm_regs regs;
+ memset(&regs, 0, sizeof(regs));
+ regs.rip = guest_mem + ADDR_TEXT;
+ regs.rsp = ADDR_STACK0;
+
+ if (opt_count > 2)
+ opt_count = 2;
+ for (i = 0; i < opt_count; i++) {
+ uint64_t typ = 0;
+ uint64_t val = 0;
+ NONFAILING(typ = opt_array_ptr[i].typ);
+ NONFAILING(val = opt_array_ptr[i].val);
+ switch (typ) {
+ case 1:
+ sregs.cr0 ^= val & (CR0_MP | CR0_EM | CR0_ET | CR0_NE | CR0_WP | CR0_AM | CR0_NW | CR0_CD);
+ break;
+ case 2:
+ sregs.cr4 ^= val & (CR4_VME | CR4_PVI | CR4_TSD | CR4_DE | CR4_MCE | CR4_PGE | CR4_PCE |
+ CR4_OSFXSR | CR4_OSXMMEXCPT | CR4_UMIP | CR4_VMXE | CR4_SMXE | CR4_FSGSBASE | CR4_PCIDE |
+ CR4_OSXSAVE | CR4_SMEP | CR4_SMAP | CR4_PKE);
+ break;
+ case 3:
+ sregs.efer ^= val & (EFER_SCE | EFER_NXE | EFER_SVME | EFER_LMSLE | EFER_FFXSR | EFER_TCE);
+ break;
+ case 4:
+ regs.rflags ^= val & ((1 << 8) | (1 << 9) | (1 << 10) | (1 << 12) | (1 << 13) | (1 << 14) |
+ (1 << 15) | (1 << 18) | (1 << 19) | (1 << 20) | (1 << 21));
+ break;
+ }
+ }
+ regs.rflags |= 2; // bit 1 is always set
+
+ sregs.gdt.base = guest_mem + ADDR_GDT;
+ sregs.gdt.limit = 256 * sizeof(uint64_t) - 1;
+ uint64_t* gdt = (uint64_t*)(host_mem + sregs.gdt.base);
+
+ struct kvm_segment seg_ldt;
+ seg_ldt.selector = SEL_LDT;
+ seg_ldt.type = 2;
+ seg_ldt.base = guest_mem + ADDR_LDT;
+ seg_ldt.limit = 256 * sizeof(uint64_t) - 1;
+ seg_ldt.present = 1;
+ seg_ldt.dpl = 0;
+ seg_ldt.s = 0;
+ seg_ldt.g = 0;
+ seg_ldt.db = 1;
+ seg_ldt.l = 0;
+ uint64_t* ldt = (uint64_t*)(host_mem + sregs.ldt.base);
+ fill_segment_descriptor(gdt, ldt, &seg_ldt);
+ sregs.ldt = seg_ldt;
+
+ struct kvm_segment seg_cs16;
+ seg_cs16.selector = SEL_CS16;
+ seg_cs16.type = 11;
+ seg_cs16.base = 0;
+ seg_cs16.limit = 0xfffff;
+ seg_cs16.present = 1;
+ seg_cs16.dpl = 0;
+ seg_cs16.s = 1;
+ seg_cs16.g = 0;
+ seg_cs16.db = 0;
+ seg_cs16.l = 0;
+ fill_segment_descriptor(gdt, ldt, &seg_cs16);
+
+ struct kvm_segment seg_ds16 = seg_cs16;
+ seg_ds16.selector = SEL_DS16;
+ seg_ds16.type = 3;
+ fill_segment_descriptor(gdt, ldt, &seg_ds16);
+
+ struct kvm_segment seg_cs16_cpl3 = seg_cs16;
+ seg_cs16_cpl3.selector = SEL_CS16_CPL3;
+ seg_cs16_cpl3.dpl = 3;
+ fill_segment_descriptor(gdt, ldt, &seg_cs16_cpl3);
+
+ struct kvm_segment seg_ds16_cpl3 = seg_ds16;
+ seg_ds16_cpl3.selector = SEL_DS16_CPL3;
+ seg_ds16_cpl3.dpl = 3;
+ fill_segment_descriptor(gdt, ldt, &seg_ds16_cpl3);
+
+ struct kvm_segment seg_cs32 = seg_cs16;
+ seg_cs32.selector = SEL_CS32;
+ seg_cs32.db = 1;
+ fill_segment_descriptor(gdt, ldt, &seg_cs32);
+
+ struct kvm_segment seg_ds32 = seg_ds16;
+ seg_ds32.selector = SEL_DS32;
+ seg_ds32.db = 1;
+ fill_segment_descriptor(gdt, ldt, &seg_ds32);
+
+ struct kvm_segment seg_cs32_cpl3 = seg_cs32;
+ seg_cs32_cpl3.selector = SEL_CS32_CPL3;
+ seg_cs32_cpl3.dpl = 3;
+ fill_segment_descriptor(gdt, ldt, &seg_cs32_cpl3);
+
+ struct kvm_segment seg_ds32_cpl3 = seg_ds32;
+ seg_ds32_cpl3.selector = SEL_DS32_CPL3;
+ seg_ds32_cpl3.dpl = 3;
+ fill_segment_descriptor(gdt, ldt, &seg_ds32_cpl3);
+
+ struct kvm_segment seg_cs64 = seg_cs16;
+ seg_cs64.selector = SEL_CS64;
+ seg_cs64.l = 1;
+ fill_segment_descriptor(gdt, ldt, &seg_cs64);
+
+ struct kvm_segment seg_ds64 = seg_ds32;
+ seg_ds64.selector = SEL_DS64;
+ fill_segment_descriptor(gdt, ldt, &seg_ds64);
+
+ struct kvm_segment seg_cs64_cpl3 = seg_cs64;
+ seg_cs64_cpl3.selector = SEL_CS64_CPL3;
+ seg_cs64_cpl3.dpl = 3;
+ fill_segment_descriptor(gdt, ldt, &seg_cs64_cpl3);
+
+ struct kvm_segment seg_ds64_cpl3 = seg_ds64;
+ seg_ds64_cpl3.selector = SEL_DS64_CPL3;
+ seg_ds64_cpl3.dpl = 3;
+ fill_segment_descriptor(gdt, ldt, &seg_ds64_cpl3);
+
+ struct kvm_segment seg_tss32;
+ seg_tss32.selector = SEL_TSS32;
+ seg_tss32.type = 9;
+ seg_tss32.base = ADDR_VAR_TSS32;
+ seg_tss32.limit = 0x1ff;
+ seg_tss32.present = 1;
+ seg_tss32.dpl = 0;
+ seg_tss32.s = 0;
+ seg_tss32.g = 0;
+ seg_tss32.db = 0;
+ seg_tss32.l = 0;
+ fill_segment_descriptor(gdt, ldt, &seg_tss32);
+
+ struct kvm_segment seg_tss32_2 = seg_tss32;
+ seg_tss32_2.selector = SEL_TSS32_2;
+ seg_tss32_2.base = ADDR_VAR_TSS32_2;
+ fill_segment_descriptor(gdt, ldt, &seg_tss32_2);
+
+ struct kvm_segment seg_tss32_cpl3 = seg_tss32;
+ seg_tss32_cpl3.selector = SEL_TSS32_CPL3;
+ seg_tss32_cpl3.base = ADDR_VAR_TSS32_CPL3;
+ fill_segment_descriptor(gdt, ldt, &seg_tss32_cpl3);
+
+ struct kvm_segment seg_tss32_vm86 = seg_tss32;
+ seg_tss32_vm86.selector = SEL_TSS32_VM86;
+ seg_tss32_vm86.base = ADDR_VAR_TSS32_VM86;
+ fill_segment_descriptor(gdt, ldt, &seg_tss32_vm86);
+
+ struct kvm_segment seg_tss16 = seg_tss32;
+ seg_tss16.selector = SEL_TSS16;
+ seg_tss16.base = ADDR_VAR_TSS16;
+ seg_tss16.limit = 0xff;
+ seg_tss16.type = 1;
+ fill_segment_descriptor(gdt, ldt, &seg_tss16);
+
+ struct kvm_segment seg_tss16_2 = seg_tss16;
+ seg_tss16_2.selector = SEL_TSS16_2;
+ seg_tss16_2.base = ADDR_VAR_TSS16_2;
+ seg_tss16_2.dpl = 0;
+ fill_segment_descriptor(gdt, ldt, &seg_tss16_2);
+
+ struct kvm_segment seg_tss16_cpl3 = seg_tss16;
+ seg_tss16_cpl3.selector = SEL_TSS16_CPL3;
+ seg_tss16_cpl3.base = ADDR_VAR_TSS16_CPL3;
+ seg_tss16_cpl3.dpl = 3;
+ fill_segment_descriptor(gdt, ldt, &seg_tss16_cpl3);
+
+ struct kvm_segment seg_tss64 = seg_tss32;
+ seg_tss64.selector = SEL_TSS64;
+ seg_tss64.base = ADDR_VAR_TSS64;
+ seg_tss64.limit = 0x1ff;
+ fill_segment_descriptor_dword(gdt, ldt, &seg_tss64);
+
+ struct kvm_segment seg_tss64_cpl3 = seg_tss64;
+ seg_tss64_cpl3.selector = SEL_TSS64_CPL3;
+ seg_tss64_cpl3.base = ADDR_VAR_TSS64_CPL3;
+ seg_tss64_cpl3.dpl = 3;
+ fill_segment_descriptor_dword(gdt, ldt, &seg_tss64_cpl3);
+
+ struct kvm_segment seg_cgate16;
+ seg_cgate16.selector = SEL_CGATE16;
+ seg_cgate16.type = 4;
+ seg_cgate16.base = SEL_CS16 | (2 << 16); // selector + param count
+ seg_cgate16.limit = ADDR_VAR_USER_CODE2; // entry offset
+ seg_cgate16.present = 1;
+ seg_cgate16.dpl = 0;
+ seg_cgate16.s = 0;
+ seg_cgate16.g = 0;
+ seg_cgate16.db = 0;
+ seg_cgate16.l = 0;
+ seg_cgate16.avl = 0;
+ fill_segment_descriptor(gdt, ldt, &seg_cgate16);
+
+ struct kvm_segment seg_tgate16 = seg_cgate16;
+ seg_tgate16.selector = SEL_TGATE16;
+ seg_tgate16.type = 3;
+ seg_cgate16.base = SEL_TSS16_2;
+ seg_tgate16.limit = 0;
+ fill_segment_descriptor(gdt, ldt, &seg_tgate16);
+
+ struct kvm_segment seg_cgate32 = seg_cgate16;
+ seg_cgate32.selector = SEL_CGATE32;
+ seg_cgate32.type = 12;
+ seg_cgate32.base = SEL_CS32 | (2 << 16); // selector + param count
+ fill_segment_descriptor(gdt, ldt, &seg_cgate32);
+
+ struct kvm_segment seg_tgate32 = seg_cgate32;
+ seg_tgate32.selector = SEL_TGATE32;
+ seg_tgate32.type = 11;
+ seg_tgate32.base = SEL_TSS32_2;
+ seg_tgate32.limit = 0;
+ fill_segment_descriptor(gdt, ldt, &seg_tgate32);
+
+ struct kvm_segment seg_cgate64 = seg_cgate16;
+ seg_cgate64.selector = SEL_CGATE64;
+ seg_cgate64.type = 12;
+ seg_cgate64.base = SEL_CS64;
+ fill_segment_descriptor_dword(gdt, ldt, &seg_cgate64);
+
+ int kvmfd = open("/dev/kvm", O_RDWR);
+ char buf[sizeof(kvm_cpuid2) + 128 * sizeof(struct kvm_cpuid_entry2)];
+ memset(buf, 0, sizeof(buf));
+ struct kvm_cpuid2* cpuid = (struct kvm_cpuid2*)buf;
+ cpuid->nent = 128;
+ ioctl(kvmfd, KVM_GET_SUPPORTED_CPUID, cpuid);
+ ioctl(cpufd, KVM_SET_CPUID2, cpuid);
+ close(kvmfd);
+
+ const char* text_prefix;
+ int text_prefix_size = 0;
+ char* host_text = host_mem + ADDR_TEXT;
+
+ if (text_type == 16) {
+ if (flags & KVM_SETUP_SMM) {
+ if (flags & KVM_SETUP_PROTECTED) {
+ sregs.cs = seg_cs16;
+ sregs.ds = sregs.es = sregs.fs = sregs.gs = sregs.ss = seg_ds16;
+ sregs.cr0 |= CR0_PE;
+ } else {
+ sregs.cs.selector = 0;
+ sregs.cs.base = 0;
+ }
+
+ NONFAILING(*(host_mem + ADDR_TEXT) = 0xf4); // hlt for rsm
+ host_text = host_mem + 0x8000;
+
+ ioctl(cpufd, KVM_SMI, 0);
+ } else if (flags & KVM_SETUP_VIRT86) {
+ sregs.cs = seg_cs32;
+ sregs.ds = sregs.es = sregs.fs = sregs.gs = sregs.ss = seg_ds32;
+ sregs.cr0 |= CR0_PE;
+ sregs.efer |= EFER_SCE;
+
+ setup_syscall_msrs(cpufd, SEL_CS32, SEL_CS32_CPL3);
+ setup_32bit_idt(&sregs, host_mem, guest_mem);
+
+ if (flags & KVM_SETUP_PAGING) {
+ uint64_t pd_addr = guest_mem + ADDR_PD;
+ uint64_t* pd = (uint64_t*)(host_mem + ADDR_PD);
+ // A single 4MB page to cover the memory region
+ NONFAILING(pd[0] = PDE32_PRESENT | PDE32_RW | PDE32_USER | PDE32_PS);
+ sregs.cr3 = pd_addr;
+ sregs.cr4 |= CR4_PSE;
+
+ text_prefix = kvm_asm32_paged_vm86;
+ text_prefix_size = sizeof(kvm_asm32_paged_vm86) - 1;
+ } else {
+ text_prefix = kvm_asm32_vm86;
+ text_prefix_size = sizeof(kvm_asm32_vm86) - 1;
+ }
+ } else if (flags & KVM_SETUP_PROTECTED) {
+ sregs.cr0 |= CR0_PE;
+ sregs.cs = seg_cs16;
+ sregs.ds = sregs.es = sregs.fs = sregs.gs = sregs.ss = seg_ds16;
+ } else if (flags & KVM_SETUP_CPL3) {
+ sregs.cs = seg_cs16;
+ sregs.ds = sregs.es = sregs.fs = sregs.gs = sregs.ss = seg_ds16;
+
+ text_prefix = kvm_asm16_cpl3;
+ text_prefix_size = sizeof(kvm_asm16_cpl3) - 1;
+ } else {
+ sregs.cs.selector = 0;
+ sregs.cs.base = 0;
+ }
+ } else if (text_type == 32) {
+ sregs.cr0 |= CR0_PE;
+ sregs.efer |= EFER_SCE;
+
+ setup_syscall_msrs(cpufd, SEL_CS32, SEL_CS32_CPL3);
+ setup_32bit_idt(&sregs, host_mem, guest_mem);
+
+ if (flags & KVM_SETUP_SMM) {
+ sregs.cs = seg_cs32;
+ sregs.ds = sregs.es = sregs.fs = sregs.gs = sregs.ss = seg_ds32;
+
+ NONFAILING(*(host_mem + ADDR_TEXT) = 0xf4); // hlt for rsm
+ host_text = host_mem + 0x8000;
+
+ ioctl(cpufd, KVM_SMI, 0);
+ } else if (flags & KVM_SETUP_PAGING) {
+ sregs.cs = seg_cs32;
+ sregs.ds = sregs.es = sregs.fs = sregs.gs = sregs.ss = seg_ds32;
+
+ uint64_t pd_addr = guest_mem + ADDR_PD;
+ uint64_t* pd = (uint64_t*)(host_mem + ADDR_PD);
+ // A single 4MB page to cover the memory region
+ NONFAILING(pd[0] = PDE32_PRESENT | PDE32_RW | PDE32_USER | PDE32_PS);
+ sregs.cr3 = pd_addr;
+ sregs.cr4 |= CR4_PSE;
+
+ text_prefix = kvm_asm32_paged;
+ text_prefix_size = sizeof(kvm_asm32_paged) - 1;
+ } else if (flags & KVM_SETUP_CPL3) {
+ sregs.cs = seg_cs32_cpl3;
+ sregs.ds = sregs.es = sregs.fs = sregs.gs = sregs.ss = seg_ds32_cpl3;
+ } else {
+ sregs.cs = seg_cs32;
+ sregs.ds = sregs.es = sregs.fs = sregs.gs = sregs.ss = seg_ds32;
+ }
+ } else {
+ sregs.efer |= EFER_LME | EFER_SCE;
+ sregs.cr0 |= CR0_PE;
+
+ setup_syscall_msrs(cpufd, SEL_CS64, SEL_CS64_CPL3);
+ setup_64bit_idt(&sregs, host_mem, guest_mem);
+
+ sregs.cs = seg_cs32;
+ sregs.ds = sregs.es = sregs.fs = sregs.gs = sregs.ss = seg_ds32;
+
+ uint64_t pml4_addr = guest_mem + ADDR_PML4;
+ uint64_t* pml4 = (uint64_t*)(host_mem + ADDR_PML4);
+ uint64_t pdpt_addr = guest_mem + ADDR_PDP;
+ uint64_t* pdpt = (uint64_t*)(host_mem + ADDR_PDP);
+ uint64_t pd_addr = guest_mem + ADDR_PD;
+ uint64_t* pd = (uint64_t*)(host_mem + ADDR_PD);
+ NONFAILING(pml4[0] = PDE64_PRESENT | PDE64_RW | PDE64_USER | pdpt_addr);
+ NONFAILING(pdpt[0] = PDE64_PRESENT | PDE64_RW | PDE64_USER | pd_addr);
+ NONFAILING(pd[0] = PDE64_PRESENT | PDE64_RW | PDE64_USER | PDE64_PS);
+ sregs.cr3 = pml4_addr;
+ sregs.cr4 |= CR4_PAE;
+
+ if (flags & KVM_SETUP_VM) {
+ sregs.cr0 |= CR0_NE;
+
+ NONFAILING(*((uint64_t*)(host_mem + ADDR_VAR_VMXON_PTR)) = ADDR_VAR_VMXON);
+ NONFAILING(*((uint64_t*)(host_mem + ADDR_VAR_VMCS_PTR)) = ADDR_VAR_VMCS);
+ NONFAILING(memcpy(host_mem + ADDR_VAR_VMEXIT_CODE, kvm_asm64_vm_exit, sizeof(kvm_asm64_vm_exit) - 1));
+ NONFAILING(*((uint64_t*)(host_mem + ADDR_VAR_VMEXIT_PTR)) = ADDR_VAR_VMEXIT_CODE);
+
+ text_prefix = kvm_asm64_init_vm;
+ text_prefix_size = sizeof(kvm_asm64_init_vm) - 1;
+ } else if (flags & KVM_SETUP_CPL3) {
+ text_prefix = kvm_asm64_cpl3;
+ text_prefix_size = sizeof(kvm_asm64_cpl3) - 1;
+ } else {
+ text_prefix = kvm_asm64_enable_long;
+ text_prefix_size = sizeof(kvm_asm64_enable_long) - 1;
+ }
+ }
+
+ NONFAILING(
+ struct tss16* tss = (struct tss16*)(host_mem + seg_tss16_2.base);
+ memset(tss, 0, sizeof(*tss));
+ tss->ss0 = tss->ss1 = tss->ss2 = SEL_DS16;
+ tss->sp0 = tss->sp1 = tss->sp2 = ADDR_STACK0;
+ tss->ip = ADDR_VAR_USER_CODE2;
+ tss->flags = (1 << 1);
+ tss->cs = SEL_CS16;
+ tss->es = tss->ds = tss->ss = SEL_DS16;
+ tss->ldt = SEL_LDT);
+ NONFAILING(
+ struct tss16* tss = (struct tss16*)(host_mem + seg_tss16_cpl3.base);
+ memset(tss, 0, sizeof(*tss));
+ tss->ss0 = tss->ss1 = tss->ss2 = SEL_DS16;
+ tss->sp0 = tss->sp1 = tss->sp2 = ADDR_STACK0;
+ tss->ip = ADDR_VAR_USER_CODE2;
+ tss->flags = (1 << 1);
+ tss->cs = SEL_CS16_CPL3;
+ tss->es = tss->ds = tss->ss = SEL_DS16_CPL3;
+ tss->ldt = SEL_LDT);
+ NONFAILING(
+ struct tss32* tss = (struct tss32*)(host_mem + seg_tss32_vm86.base);
+ memset(tss, 0, sizeof(*tss));
+ tss->ss0 = tss->ss1 = tss->ss2 = SEL_DS32;
+ tss->sp0 = tss->sp1 = tss->sp2 = ADDR_STACK0;
+ tss->ip = ADDR_VAR_USER_CODE;
+ tss->flags = (1 << 1) | (1 << 17);
+ tss->ldt = SEL_LDT;
+ tss->cr3 = sregs.cr3;
+ tss->io_bitmap = offsetof(struct tss32, io_bitmap));
+ NONFAILING(
+ struct tss32* tss = (struct tss32*)(host_mem + seg_tss32_2.base);
+ memset(tss, 0, sizeof(*tss));
+ tss->ss0 = tss->ss1 = tss->ss2 = SEL_DS32;
+ tss->sp0 = tss->sp1 = tss->sp2 = ADDR_STACK0;
+ tss->ip = ADDR_VAR_USER_CODE;
+ tss->flags = (1 << 1);
+ tss->cr3 = sregs.cr3;
+ tss->es = tss->ds = tss->ss = tss->gs = tss->fs = SEL_DS32;
+ tss->cs = SEL_CS32;
+ tss->ldt = SEL_LDT;
+ tss->cr3 = sregs.cr3;
+ tss->io_bitmap = offsetof(struct tss32, io_bitmap));
+ NONFAILING(
+ struct tss64* tss = (struct tss64*)(host_mem + seg_tss64.base);
+ memset(tss, 0, sizeof(*tss));
+ tss->rsp[0] = ADDR_STACK0;
+ tss->rsp[1] = ADDR_STACK0;
+ tss->rsp[2] = ADDR_STACK0;
+ tss->io_bitmap = offsetof(struct tss64, io_bitmap));
+ NONFAILING(
+ struct tss64* tss = (struct tss64*)(host_mem + seg_tss64_cpl3.base);
+ memset(tss, 0, sizeof(*tss));
+ tss->rsp[0] = ADDR_STACK0;
+ tss->rsp[1] = ADDR_STACK0;
+ tss->rsp[2] = ADDR_STACK0;
+ tss->io_bitmap = offsetof(struct tss64, io_bitmap));
+
+ if (text_size > 1000)
+ text_size = 1000;
+ if (text_prefix) {
+ NONFAILING(memcpy(host_text, text_prefix, text_prefix_size));
+ void* patch = 0;
+ // Replace 0xbadc0de in LJMP with offset of a next instruction.
+ NONFAILING(patch = memmem(host_text, text_prefix_size, "\xde\xc0\xad\x0b", 4));
+ if (patch)
+ NONFAILING(*((uint32_t*)patch) = guest_mem + ADDR_TEXT + ((char*)patch - host_text) + 6);
+ uint16_t magic = PREFIX_SIZE;
+ patch = 0;
+ NONFAILING(patch = memmem(host_text, text_prefix_size, &magic, sizeof(magic)));
+ if (patch)
+ NONFAILING(*((uint16_t*)patch) = guest_mem + ADDR_TEXT + text_prefix_size);
+ }
+ NONFAILING(memcpy((void*)(host_text + text_prefix_size), text, text_size));
+ NONFAILING(*(host_text + text_prefix_size + text_size) = 0xf4); // hlt
+
+ NONFAILING(memcpy(host_mem + ADDR_VAR_USER_CODE, text, text_size));
+ NONFAILING(*(host_mem + ADDR_VAR_USER_CODE + text_size) = 0xf4); // hlt
+
+ NONFAILING(*(host_mem + ADDR_VAR_HLT) = 0xf4); // hlt
+ NONFAILING(memcpy(host_mem + ADDR_VAR_SYSRET, "\x0f\x07\xf4", 3));
+ NONFAILING(memcpy(host_mem + ADDR_VAR_SYSEXIT, "\x0f\x35\xf4", 3));
+
+ if (ioctl(cpufd, KVM_SET_SREGS, &sregs))
+ return -1;
+ if (ioctl(cpufd, KVM_SET_REGS, &regs))
+ return -1;
+ return 0;
+}
diff --git a/executor/executor.cc b/executor/executor.cc
index a6d45c432..94658a5cc 100644
--- a/executor/executor.cc
+++ b/executor/executor.cc
@@ -1,6 +1,8 @@
// Copyright 2015 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+// +build
+
#include <algorithm>
#include <errno.h>
#include <fcntl.h>
diff --git a/executor/kvm.S b/executor/kvm.S
new file mode 100644
index 000000000..9f4df257e
--- /dev/null
+++ b/executor/kvm.S
@@ -0,0 +1,315 @@
+// Copyright 2017 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+// kvm_gen.cc generates machine code from this file and saves it into kvm.S.h.
+
+// +build
+
+#include "kvm.h"
+
+.global kvm_asm64_enable_long, kvm_asm64_enable_long_end
+kvm_asm64_enable_long:
+.code32
+ mov %cr0, %eax
+ or $0x80000000, %eax
+ mov %eax, %cr0
+ ljmp $SEL_CS64, NEXT_INSN
+.code64
+ mov $SEL_TSS64, %rax
+ ltr %ax
+kvm_asm64_enable_long_end:
+ nop
+
+.global kvm_asm32_paged, kvm_asm32_paged_end
+kvm_asm32_paged:
+.code32
+ mov %cr0, %eax
+ or $0x80000000, %eax
+ mov %eax, %cr0
+kvm_asm32_paged_end:
+ nop
+
+.global kvm_asm32_vm86, kvm_asm32_vm86_end
+kvm_asm32_vm86:
+.code32
+ mov $SEL_TSS32, %ax
+ ltr %ax
+ ljmp $SEL_TSS32_VM86, $0
+kvm_asm32_vm86_end:
+ nop
+
+.global kvm_asm32_paged_vm86, kvm_asm32_paged_vm86_end
+kvm_asm32_paged_vm86:
+.code32
+ mov %cr0, %eax
+ or $0x80000000, %eax
+ mov %eax, %cr0
+ mov $SEL_TSS32, %ax
+ ltr %ax
+ ljmp $SEL_TSS32_VM86, $0
+kvm_asm32_paged_vm86_end:
+ nop
+
+.global kvm_asm64_vm86, kvm_asm64_vm86_end
+kvm_asm64_vm86:
+.code32
+ mov %cr0, %eax
+ or $0x80000000, %eax
+ mov %eax, %cr0
+ mov $SEL_TSS32, %ax
+ ltr %ax
+ ljmp $SEL_TSS32_VM86, $0
+kvm_asm64_vm86_end:
+ nop
+
+.global kvm_asm16_cpl3, kvm_asm16_cpl3_end
+kvm_asm16_cpl3:
+.code16
+ mov %cr0, %eax
+ or $1, %eax
+ mov %eax, %cr0
+ mov $SEL_TSS16, %ax
+ ltr %ax
+ mov $SEL_DS16_CPL3, %ax
+ mov %ax, %ds
+ mov %ax, %es
+ mov %ax, %fs
+ mov %ax, %gs
+ mov $0x100, %sp
+ movw $PREFIX_SIZE, 0x100
+ movw $SEL_CS16_CPL3, 0x102
+ movw $0x100, 0x104
+ movw $SEL_DS16_CPL3, 0x106
+ lret
+kvm_asm16_cpl3_end:
+ nop
+
+.global kvm_asm64_cpl3, kvm_asm64_cpl3_end
+kvm_asm64_cpl3:
+.code32
+ mov %cr0, %eax
+ or $0x80000000, %eax
+ mov %eax, %cr0
+ ljmp $SEL_CS64, NEXT_INSN
+.code64
+ mov $SEL_TSS64, %rax
+ ltr %ax
+ mov $SEL_DS64_CPL3, %rax
+ mov %ax, %ds
+ mov %ax, %es
+ mov %ax, %fs
+ mov %ax, %gs
+ mov $ADDR_STACK0, %rsp
+ movq $PREFIX_SIZE, 0(%rsp)
+ movq $SEL_CS64_CPL3, 4(%rsp)
+ movq $ADDR_STACK0, 8(%rsp)
+ movq $SEL_DS64_CPL3, 12(%rsp)
+ lret
+kvm_asm64_cpl3_end:
+ nop
+
+.global kvm_asm64_init_vm, kvm_asm64_init_vm_end
+kvm_asm64_init_vm:
+.code32
+ // CR0.PG = 1
+ mov %cr0, %eax
+ or $0x80000000, %eax
+ mov %eax, %cr0
+ ljmp $SEL_CS64, NEXT_INSN
+.code64
+ mov $SEL_TSS64, %rax
+ ltr %ax
+
+ // Enable and lock non-SMM VM
+ mov $MSR_IA32_FEATURE_CONTROL, %rcx
+ rdmsr
+ or $0x5, %rax
+ wrmsr
+
+ // CR4.VMXE = 1
+ mov %cr4, %rax
+ or $0x2000, %rax
+ mov %rax, %cr4
+
+ // Write VMCS revision into VMXON and VMCS regions
+ mov $MSR_IA32_VMX_BASIC, %rcx
+ rdmsr
+ mov $ADDR_VAR_VMXON,%rdx
+ mov %eax,(%rdx)
+ mov $ADDR_VAR_VMCS,%rdx
+ mov %eax,(%rdx)
+
+ mov $ADDR_VAR_VMXON_PTR, %rax
+ vmxon (%rax)
+ mov $ADDR_VAR_VMCS_PTR, %rax
+ vmclear (%rax)
+ vmptrld (%rax)
+
+#define VMSET(FIELD, VAL) \
+ mov $FIELD, %rdx; \
+ mov VAL, %rax; \
+ vmwrite %rax, %rdx; \
+ /**/
+
+#define VMSET_LIMITED(FIELD, VAL, MSR) \
+ mov $MSR, %rcx; \
+ rdmsr; \
+ or VAL, %rax; \
+ and %rdx, %rax; \
+ mov $FIELD, %rdx; \
+ vmwrite %rax, %rdx; \
+ /**/
+
+ VMSET_LIMITED(0x00004000, $0x3f, 0x481) // Pin-based VM-execution controls
+ //VMSET_LIMITED(0x00004002, $0x61999e84, 0x482) // Primary processor-based VM-execution controls
+ VMSET(0x00004002, $0xf3999e84)
+ VMSET(0x0000401E, $((1<<0) | (1<<7))) // Secondary processor-based VM-execution controls
+ VMSET_LIMITED(0x0000400C, $0x36fff, 0x483) // VM-exit controls (F6FFF)
+ VMSET_LIMITED(0x00004012, $0x17ff, 0x484) // VM-entry controls (51FF)
+
+ VMSET(0x00002C04, $0) // Host IA32_PERF_GLOBAL_CTR
+ VMSET(0x00002800, $0xffffffffffffffff) // VMCS link pointer
+
+ VMSET(0x00000C02, $SEL_CS64) // host CS
+
+ mov $SEL_DS64, %rax
+ mov $0x00000C00, %rdx // host ES
+ vmwrite %rax, %rdx
+ mov $0x00000C04, %rdx // host SS
+ vmwrite %rax, %rdx
+ mov $0x00000C06, %rdx // host DS
+ vmwrite %rax, %rdx
+ mov $0x00000C08, %rdx // host FS
+ vmwrite %rax, %rdx
+ mov $0x00000C0A, %rdx // host GS
+ vmwrite %rax, %rdx
+ mov $SEL_TSS64, %rax
+ mov $0x00000C0C, %rdx // host TR
+ vmwrite %rax, %rdx
+
+ VMSET(0x00002C02, $0x500) // host EFER
+
+ VMSET(0x00004C00, $SEL_CS64) // Host IA32_SYSENTER_CS
+ VMSET(0x00006C10, $0) // Host IA32_SYSENTER_ESP
+ VMSET(0x00006C12, $0) // Host IA32_SYSENTER_EIP
+
+ mov %cr0, %rax
+ VMSET(0x00006C00, %rax) // host CR0
+ mov %cr3, %rax
+ VMSET(0x00006C02, %rax) // host CR3
+ mov %cr4, %rax
+ VMSET(0x00006C04, %rax) // host CR4
+
+ VMSET(0x00006C06, $0) // host FS base
+ VMSET(0x00006C08, $0) // host GS base
+ VMSET(0x00006C0A, $ADDR_VAR_TSS64) // host TR base
+
+ VMSET(0x00006C0C, $ADDR_GDT) // host GDTR base
+ VMSET(0x00006C0E, $ADDR_VAR_IDT) // host IDTR base
+
+ VMSET(0x00006C14, $0) // host RSP
+ VMSET(0x00006C16, ADDR_VAR_VMEXIT_PTR) // host RIP
+
+ VMSET(0x00000000, $1) // VPID
+ VMSET(0x00000002, $0) // Posted-interrupt notification vector
+ //VMSET(0x00000004, $0) // EPTP index
+
+ VMSET(0x00002000, $0) // Address of I/O bitmap A
+ VMSET(0x00002002, $0) // Address of I/O bitmap B
+ VMSET(0x00002004, $0) // Address of MSR bitmaps
+ VMSET(0x00002006, $0) // VM-exit MSR-store address
+
+ mov $0x277, %rcx
+ rdmsr
+ shl $32, %rdx
+ or %rdx, %rax
+ VMSET(0x00002C00, %rax) // Host IA32_PAT
+
+ VMSET(0x00004004, $0) // Exception bitmap
+ VMSET(0x0000400A, $0) // CR3-target count
+ VMSET(0x0000400E, $0) // VM-exit MSR-store count
+ VMSET(0x00004010, $0) // VM-exit MSR-load count
+ VMSET(0x00004016, $0) // VM-entry interruption-information field
+ VMSET(0x00004014, $0) // VM-entry MSR-load count
+
+ VMSET(0x00006000, $0xffffffffffffffff) // CR0 guest/host mask
+ VMSET(0x00006002, $0xffffffffffffffff) // CR4 guest/host mask
+
+ VMSET(0x0000201C, $0) // EOI-exit bitmap 0
+ VMSET(0x0000201E, $0) // EOI-exit bitmap 1
+ VMSET(0x00002020, $0) // EOI-exit bitmap 2
+ VMSET(0x00002022, $0) // EOI-exit bitmap 3
+
+ VMSET(0x00000800, $SEL_DS64) // Guest ES selector
+ VMSET(0x00000802, $SEL_CS64) // Guest CS selector
+ VMSET(0x00000804, $SEL_DS64) // Guest SS selector
+ VMSET(0x00000806, $SEL_DS64) // Guest DS selector
+ VMSET(0x00000808, $SEL_DS64) // Guest FS selector
+ VMSET(0x0000080A, $SEL_DS64) // Guest GS selector
+ VMSET(0x0000080C, $0) // Guest LDTR selector
+ VMSET(0x0000080E, $SEL_TSS64) // Guest TR selector
+
+ VMSET(0x00006812, $0) // Guest LDTR base
+ VMSET(0x00006814, $ADDR_VAR_TSS64) // Guest TR base
+ VMSET(0x00006816, $ADDR_GDT) // Guest GDTR base
+ VMSET(0x00006818, $ADDR_VAR_IDT) // Guest IDTR base
+
+ VMSET(0x00004800, $0xfffff) // Guest ES limit
+ VMSET(0x00004802, $0xfffff) // Guest CS limit
+ VMSET(0x00004804, $0xfffff) // Guest SS limit
+ VMSET(0x00004806, $0xfffff) // Guest DS limit
+ VMSET(0x00004808, $0xfffff) // Guest FS limit
+ VMSET(0x0000480A, $0xfffff) // Guest GS limit
+ VMSET(0x0000480C, $0) // Guest LDTR limit
+ VMSET(0x0000480E, $0x1fff) // Guest TR limit
+ VMSET(0x00004810, $0x1fff) // Guest GDTR limit
+ VMSET(0x00004812, $0x1fff) // Guest IDTR limit
+
+ VMSET(0x00004814, $0x4093) // Guest ES access rights
+ VMSET(0x00004816, $0x209b) // Guest CS access rights
+ VMSET(0x00004818, $0x4093) // Guest SS access rights
+ VMSET(0x0000481A, $0x4093) // Guest DS access rights
+ VMSET(0x0000481C, $0x4093) // Guest FS access rights
+ VMSET(0x0000481E, $0x4093) // Guest GS access rights
+ VMSET(0x00004820, $0x82) // Guest LDTR access rights
+ VMSET(0x00004822, $0x8b) // Guest TR access rights
+
+ VMSET(0x0000681C, $0) // Guest RSP
+ VMSET(0x0000681E, $ADDR_VAR_USER_CODE) // Guest RIP
+ VMSET(0x00006820, $((1<<1))) // Guest RFLAGS
+ VMSET(0x00002806, $0x500) // Guest IA32_EFER
+ VMSET(0x0000280A, $0) // Guest PDPTE0
+ VMSET(0x0000280C, $0) // Guest PDPTE1
+ VMSET(0x0000280E, $0) // Guest PDPTE2
+ VMSET(0x00002810, $0) // Guest PDPTE3
+
+ mov %cr0, %rax
+ VMSET(0x00006800, %rax) // Guest CR0
+ mov %cr3, %rax
+ VMSET(0x00006802, %rax) // Guest CR3
+ mov %cr4, %rax
+ VMSET(0x00006804, %rax) // Guest CR4
+
+ vmlaunch
+
+ mov $0x00004400, %rdx
+ vmread %rdx, %rax
+ hlt
+kvm_asm64_init_vm_end:
+ nop
+
+.global kvm_asm64_vm_exit, kvm_asm64_vm_exit_end
+kvm_asm64_vm_exit:
+.code64
+ //vmresume
+ mov $0x00004400, %rbx // VM-instruction error
+ vmread %rbx, %rdx
+ mov $0x00004402, %rbx // Exit reason
+ vmread %rbx, %rcx
+ mov $0x00006400, %rax // Exit qualification
+ vmread %rax, %rax
+ mov $0x0000681E, %rbx // Guest RIP
+ vmread %rbx, %rbx
+ hlt
+kvm_asm64_vm_exit_end:
+ nop
diff --git a/executor/kvm.S.h b/executor/kvm.S.h
new file mode 100644
index 000000000..c6a53c798
--- /dev/null
+++ b/executor/kvm.S.h
@@ -0,0 +1,10 @@
+// AUTOGENERATED FILE
+const char kvm_asm16_cpl3[] = "\x0f\x20\xc0\x66\x83\xc8\x01\x0f\x22\xc0\xb8\xa0\x00\x0f\x00\xd8\xb8\x2b\x00\x8e\xd8\x8e\xc0\x8e\xe0\x8e\xe8\xbc\x00\x01\xc7\x06\x00\x01\x1d\xba\xc7\x06\x02\x01\x23\x00\xc7\x06\x04\x01\x00\x01\xc7\x06\x06\x01\x2b\x00\xcb";
+const char kvm_asm32_paged[] = "\x0f\x20\xc0\x0d\x00\x00\x00\x80\x0f\x22\xc0";
+const char kvm_asm32_vm86[] = "\x66\xb8\xb8\x00\x0f\x00\xd8\xea\x00\x00\x00\x00\xd0\x00";
+const char kvm_asm32_paged_vm86[] = "\x0f\x20\xc0\x0d\x00\x00\x00\x80\x0f\x22\xc0\x66\xb8\xb8\x00\x0f\x00\xd8\xea\x00\x00\x00\x00\xd0\x00";
+const char kvm_asm64_vm86[] = "\x0f\x20\xc0\x0d\x00\x00\x00\x80\x0f\x22\xc0\x66\xb8\xb8\x00\x0f\x00\xd8\xea\x00\x00\x00\x00\xd0\x00";
+const char kvm_asm64_enable_long[] = "\x0f\x20\xc0\x0d\x00\x00\x00\x80\x0f\x22\xc0\xea\xde\xc0\xad\x0b\x50\x00\x48\xc7\xc0\xd8\x00\x00\x00\x0f\x00\xd8";
+const char kvm_asm64_init_vm[] = "\x0f\x20\xc0\x0d\x00\x00\x00\x80\x0f\x22\xc0\xea\xde\xc0\xad\x0b\x50\x00\x48\xc7\xc0\xd8\x00\x00\x00\x0f\x00\xd8\x48\xc7\xc1\x3a\x00\x00\x00\x0f\x32\x48\x83\xc8\x05\x0f\x30\x0f\x20\xe0\x48\x0d\x00\x20\x00\x00\x0f\x22\xe0\x48\xc7\xc1\x80\x04\x00\x00\x0f\x32\x48\xc7\xc2\x00\x60\x00\x00\x89\x02\x48\xc7\xc2\x00\x70\x00\x00\x89\x02\x48\xc7\xc0\x00\x5f\x00\x00\xf3\x0f\xc7\x30\x48\xc7\xc0\x08\x5f\x00\x00\x66\x0f\xc7\x30\x0f\xc7\x30\x48\xc7\xc1\x81\x04\x00\x00\x0f\x32\x48\x83\xc8\x3f\x48\x21\xd0\x48\xc7\xc2\x00\x40\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x02\x40\x00\x00\x48\xb8\x84\x9e\x99\xf3\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x1e\x40\x00\x00\x48\xc7\xc0\x81\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc1\x83\x04\x00\x00\x0f\x32\x48\x0d\xff\x6f\x03\x00\x48\x21\xd0\x48\xc7\xc2\x0c\x40\x00\x00\x0f\x79\xd0\x48\xc7\xc1\x84\x04\x00\x00\x0f\x32\x48\x0d\xff\x17\x00\x00\x48\x21\xd0\x48\xc7\xc2\x12\x40\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x04\x2c\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x00\x28\x00\x00\x48\xc7\xc0\xff\xff\xff\xff\x0f\x79\xd0\x48\xc7\xc2\x02\x0c\x00\x00\x48\xc7\xc0\x50\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc0\x58\x00\x00\x00\x48\xc7\xc2\x00\x0c\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x04\x0c\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x06\x0c\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x08\x0c\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x0a\x0c\x00\x00\x0f\x79\xd0\x48\xc7\xc0\xd8\x00\x00\x00\x48\xc7\xc2\x0c\x0c\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x02\x2c\x00\x00\x48\xc7\xc0\x00\x05\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x00\x4c\x00\x00\x48\xc7\xc0\x50\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x10\x6c\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x12\x6c\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x0f\x20\xc0\x48\xc7\xc2\x00\x6c\x00\x00\x48\x89\xc0\x0f\x79\xd0\x0f\x20\xd8\x48\xc7\xc2\x02\x6c\x00\x00\x48\x89\xc0\x0f\x79\xd0\x0f\x20\xe0\x48\xc7\xc2\x04\x6c\x00\x00\x48\x89\xc0\x0f\x79\xd0\x48\xc7\xc2\x06\x6c\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x08\x6c\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x0a\x6c\x00\x00\x48\xc7\xc0\x00\x3a\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x0c\x6c\x00\x00\x48\xc7\xc0\x00\x10\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x0e\x6c\x00\x00\x48\xc7\xc0\x00\x38\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x14\x6c\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x16\x6c\x00\x00\x48\x8b\x04\x25\x10\x5f\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x00\x00\x00\x00\x48\xc7\xc0\x01\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x02\x00\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x00\x20\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x02\x20\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x04\x20\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x06\x20\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc1\x77\x02\x00\x00\x0f\x32\x48\xc1\xe2\x20\x48\x09\xd0\x48\xc7\xc2\x00\x2c\x00\x00\x48\x89\xc0\x0f\x79\xd0\x48\xc7\xc2\x04\x40\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x0a\x40\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x0e\x40\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x10\x40\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x16\x40\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x14\x40\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x00\x60\x00\x00\x48\xc7\xc0\xff\xff\xff\xff\x0f\x79\xd0\x48\xc7\xc2\x02\x60\x00\x00\x48\xc7\xc0\xff\xff\xff\xff\x0f\x79\xd0\x48\xc7\xc2\x1c\x20\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x1e\x20\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x20\x20\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x22\x20\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x00\x08\x00\x00\x48\xc7\xc0\x58\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x02\x08\x00\x00\x48\xc7\xc0\x50\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x04\x08\x00\x00\x48\xc7\xc0\x58\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x06\x08\x00\x00\x48\xc7\xc0\x58\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x08\x08\x00\x00\x48\xc7\xc0\x58\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x0a\x08\x00\x00\x48\xc7\xc0\x58\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x0c\x08\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x0e\x08\x00\x00\x48\xc7\xc0\xd8\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x12\x68\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x14\x68\x00\x00\x48\xc7\xc0\x00\x3a\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x16\x68\x00\x00\x48\xc7\xc0\x00\x10\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x18\x68\x00\x00\x48\xc7\xc0\x00\x38\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x00\x48\x00\x00\x48\xc7\xc0\xff\xff\x0f\x00\x0f\x79\xd0\x48\xc7\xc2\x02\x48\x00\x00\x48\xc7\xc0\xff\xff\x0f\x00\x0f\x79\xd0\x48\xc7\xc2\x04\x48\x00\x00\x48\xc7\xc0\xff\xff\x0f\x00\x0f\x79\xd0\x48\xc7\xc2\x06\x48\x00\x00\x48\xc7\xc0\xff\xff\x0f\x00\x0f\x79\xd0\x48\xc7\xc2\x08\x48\x00\x00\x48\xc7\xc0\xff\xff\x0f\x00\x0f\x79\xd0\x48\xc7\xc2\x0a\x48\x00\x00\x48\xc7\xc0\xff\xff\x0f\x00\x0f\x79\xd0\x48\xc7\xc2\x0c\x48\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x0e\x48\x00\x00\x48\xc7\xc0\xff\x1f\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x10\x48\x00\x00\x48\xc7\xc0\xff\x1f\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x12\x48\x00\x00\x48\xc7\xc0\xff\x1f\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x14\x48\x00\x00\x48\xc7\xc0\x93\x40\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x16\x48\x00\x00\x48\xc7\xc0\x9b\x20\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x18\x48\x00\x00\x48\xc7\xc0\x93\x40\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x1a\x48\x00\x00\x48\xc7\xc0\x93\x40\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x1c\x48\x00\x00\x48\xc7\xc0\x93\x40\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x1e\x48\x00\x00\x48\xc7\xc0\x93\x40\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x20\x48\x00\x00\x48\xc7\xc0\x82\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x22\x48\x00\x00\x48\xc7\xc0\x8b\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x1c\x68\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x1e\x68\x00\x00\x48\xc7\xc0\x00\x91\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x20\x68\x00\x00\x48\xc7\xc0\x02\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x06\x28\x00\x00\x48\xc7\xc0\x00\x05\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x0a\x28\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x0c\x28\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x0e\x28\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x10\x28\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x0f\x20\xc0\x48\xc7\xc2\x00\x68\x00\x00\x48\x89\xc0\x0f\x79\xd0\x0f\x20\xd8\x48\xc7\xc2\x02\x68\x00\x00\x48\x89\xc0\x0f\x79\xd0\x0f\x20\xe0\x48\xc7\xc2\x04\x68\x00\x00\x48\x89\xc0\x0f\x79\xd0\x0f\x01\xc2\x48\xc7\xc2\x00\x44\x00\x00\x0f\x78\xd0\xf4";
+const char kvm_asm64_vm_exit[] = "\x48\xc7\xc3\x00\x44\x00\x00\x0f\x78\xda\x48\xc7\xc3\x02\x44\x00\x00\x0f\x78\xd9\x48\xc7\xc0\x00\x64\x00\x00\x0f\x78\xc0\x48\xc7\xc3\x1e\x68\x00\x00\x0f\x78\xdb\xf4";
+const char kvm_asm64_cpl3[] = "\x0f\x20\xc0\x0d\x00\x00\x00\x80\x0f\x22\xc0\xea\xde\xc0\xad\x0b\x50\x00\x48\xc7\xc0\xd8\x00\x00\x00\x0f\x00\xd8\x48\xc7\xc0\x6b\x00\x00\x00\x8e\xd8\x8e\xc0\x8e\xe0\x8e\xe8\x48\xc7\xc4\x80\x0f\x00\x00\x48\xc7\x04\x24\x1d\xba\x00\x00\x48\xc7\x44\x24\x04\x63\x00\x00\x00\x48\xc7\x44\x24\x08\x80\x0f\x00\x00\x48\xc7\x44\x24\x0c\x6b\x00\x00\x00\xcb";
diff --git a/executor/kvm.h b/executor/kvm.h
new file mode 100644
index 000000000..feb21490d
--- /dev/null
+++ b/executor/kvm.h
@@ -0,0 +1,75 @@
+// Copyright 2017 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+#define ADDR_TEXT 0x0000
+#define ADDR_GDT 0x1000
+#define ADDR_LDT 0x1800
+#define ADDR_PML4 0x2000
+#define ADDR_PDP 0x3000
+#define ADDR_PD 0x4000
+#define ADDR_STACK0 0x0f80
+#define ADDR_VAR_HLT 0x2800
+#define ADDR_VAR_SYSRET 0x2808
+#define ADDR_VAR_SYSEXIT 0x2810
+#define ADDR_VAR_IDT 0x3800
+#define ADDR_VAR_TSS64 0x3a00
+#define ADDR_VAR_TSS64_CPL3 0x3c00
+#define ADDR_VAR_TSS16 0x3d00
+#define ADDR_VAR_TSS16_2 0x3e00
+#define ADDR_VAR_TSS16_CPL3 0x3f00
+#define ADDR_VAR_TSS32 0x4800
+#define ADDR_VAR_TSS32_2 0x4a00
+#define ADDR_VAR_TSS32_CPL3 0x4c00
+#define ADDR_VAR_TSS32_VM86 0x4e00
+#define ADDR_VAR_VMXON_PTR 0x5f00
+#define ADDR_VAR_VMCS_PTR 0x5f08
+#define ADDR_VAR_VMEXIT_PTR 0x5f10
+#define ADDR_VAR_VMXON 0x6000
+#define ADDR_VAR_VMCS 0x7000
+#define ADDR_VAR_VMEXIT_CODE 0x9000
+#define ADDR_VAR_USER_CODE 0x9100
+#define ADDR_VAR_USER_CODE2 0x9120
+
+#define SEL_LDT (1 << 3)
+#define SEL_CS16 (2 << 3)
+#define SEL_DS16 (3 << 3)
+#define SEL_CS16_CPL3 ((4 << 3) + 3)
+#define SEL_DS16_CPL3 ((5 << 3) + 3)
+#define SEL_CS32 (6 << 3)
+#define SEL_DS32 (7 << 3)
+#define SEL_CS32_CPL3 ((8 << 3) + 3)
+#define SEL_DS32_CPL3 ((9 << 3) + 3)
+#define SEL_CS64 (10 << 3)
+#define SEL_DS64 (11 << 3)
+#define SEL_CS64_CPL3 ((12 << 3) + 3)
+#define SEL_DS64_CPL3 ((13 << 3) + 3)
+#define SEL_CGATE16 (14 << 3)
+#define SEL_TGATE16 (15 << 3)
+#define SEL_CGATE32 (16 << 3)
+#define SEL_TGATE32 (17 << 3)
+#define SEL_CGATE64 (18 << 3)
+#define SEL_CGATE64_HI (19 << 3)
+#define SEL_TSS16 (20 << 3)
+#define SEL_TSS16_2 (21 << 3)
+#define SEL_TSS16_CPL3 ((22 << 3) + 3)
+#define SEL_TSS32 (23 << 3)
+#define SEL_TSS32_2 (24 << 3)
+#define SEL_TSS32_CPL3 ((25 << 3) + 3)
+#define SEL_TSS32_VM86 (26 << 3)
+#define SEL_TSS64 (27 << 3)
+#define SEL_TSS64_HI (28 << 3)
+#define SEL_TSS64_CPL3 ((29 << 3) + 3)
+#define SEL_TSS64_CPL3_HI (30 << 3)
+
+#define MSR_IA32_FEATURE_CONTROL 0x3a
+#define MSR_IA32_VMX_BASIC 0x480
+#define MSR_IA32_SMBASE 0x9e
+#define MSR_IA32_SYSENTER_CS 0x174
+#define MSR_IA32_SYSENTER_ESP 0x175
+#define MSR_IA32_SYSENTER_EIP 0x176
+#define MSR_IA32_STAR 0xC0000081
+#define MSR_IA32_LSTAR 0xC0000082
+#define MSR_IA32_VMX_PROCBASED_CTLS2 0x48B
+
+#define NEXT_INSN $0xbadc0de
+#define PREFIX_SIZE 0xba1d
diff --git a/executor/kvm_gen.cc b/executor/kvm_gen.cc
new file mode 100644
index 000000000..5290bd2e7
--- /dev/null
+++ b/executor/kvm_gen.cc
@@ -0,0 +1,33 @@
+// Copyright 2017 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+// +build
+
+#include <stdio.h>
+
+#define PRINT(x) \
+ extern const unsigned char x[], x##_end[]; \
+ print(#x, x, x##_end);
+
+void print(const char* name, const unsigned char* start, const unsigned char* end)
+{
+ printf("const char %s[] = \"", name);
+ for (const unsigned char* p = start; p < end; p++)
+ printf("\\x%02x", *p);
+ printf("\";\n");
+}
+
+int main()
+{
+ printf("// AUTOGENERATED FILE\n");
+ PRINT(kvm_asm16_cpl3);
+ PRINT(kvm_asm32_paged);
+ PRINT(kvm_asm32_vm86);
+ PRINT(kvm_asm32_paged_vm86);
+ PRINT(kvm_asm64_vm86);
+ PRINT(kvm_asm64_enable_long);
+ PRINT(kvm_asm64_init_vm);
+ PRINT(kvm_asm64_vm_exit);
+ PRINT(kvm_asm64_cpl3);
+ return 0;
+}
diff --git a/executor/test.go b/executor/test.go
new file mode 100644
index 000000000..09a1c130b
--- /dev/null
+++ b/executor/test.go
@@ -0,0 +1,13 @@
+// Copyright 2017 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+//go:generate bash -c "gcc kvm_gen.cc kvm.S -o kvm_gen && ./kvm_gen > kvm.S.h"
+
+package executor
+
+// int test_kvm();
+import "C"
+
+func testKVM() int {
+ return int(C.test_kvm())
+}
diff --git a/executor/test_kvm.cc b/executor/test_kvm.cc
new file mode 100644
index 000000000..ac696204b
--- /dev/null
+++ b/executor/test_kvm.cc
@@ -0,0 +1,222 @@
+// Copyright 2017 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+#include "syscalls.h"
+
+#define SYZ_EXECUTOR
+#include "common.h"
+
+#include <sys/utsname.h>
+
+static unsigned host_kernel_version();
+static void dump_cpu_state(int cpufd, char* vm_mem);
+
+static int test_one(int text_type, const char* text, int text_size, int flags, int reason, bool check_rax)
+{
+ printf("=== testing text %d, text size 0x%x, flags 0x%x\n", text_type, text_size, flags);
+ int kvmfd = open("/dev/kvm", O_RDWR);
+ if (kvmfd == -1) {
+ if (errno == ENOENT) {
+ printf("/dev/kvm is not present\n");
+ return -1;
+ }
+ if (errno == EPERM) {
+ printf("no permissions to open /dev/kvm\n");
+ return -1;
+ }
+ printf("failed to open /dev/kvm (%d)\n", errno);
+ return 1;
+ }
+ int vmfd = ioctl(kvmfd, KVM_CREATE_VM, 0);
+ if (vmfd == -1) {
+ printf("KVM_CREATE_VM failed (%d)\n", errno);
+ return 1;
+ }
+ int cpufd = ioctl(vmfd, KVM_CREATE_VCPU, 0);
+ if (cpufd == -1) {
+ printf("KVM_CREATE_VCPU failed (%d)\n", errno);
+ return 1;
+ }
+ int cpu_mem_size = ioctl(kvmfd, KVM_GET_VCPU_MMAP_SIZE, 0);
+ if (cpu_mem_size <= 0) {
+ printf("KVM_GET_VCPU_MMAP_SIZE failed (%d)\n", errno);
+ return 1;
+ }
+ struct kvm_run* cpu_mem = (struct kvm_run*)mmap(0, cpu_mem_size, PROT_READ | PROT_WRITE, MAP_SHARED, cpufd, 0);
+ if (cpu_mem == MAP_FAILED) {
+ printf("cpu mmap failed (%d)\n", errno);
+ return 1;
+ }
+ int vm_mem_size = 96 << 10;
+ void* vm_mem = mmap(0, vm_mem_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (vm_mem == MAP_FAILED) {
+ printf("mmap failed (%d)\n", errno);
+ return 1;
+ }
+ struct kvm_text kvm_text;
+ kvm_text.typ = text_type;
+ kvm_text.text = text;
+ kvm_text.size = text_size;
+ if (syz_kvm_setup_cpu(vmfd, cpufd, (uintptr_t)vm_mem, (uintptr_t)&kvm_text, 1, flags, 0, 0)) {
+ printf("syz_kvm_setup_cpu failed (%d)\n", errno);
+ return 1;
+ }
+ if (ioctl(cpufd, KVM_RUN, 0)) {
+ printf("KVM_RUN failed (%d)\n", errno);
+ return 1;
+ }
+ struct kvm_regs regs;
+ if (ioctl(cpufd, KVM_GET_REGS, &regs)) {
+ printf("KVM_GET_REGS failed (%d)\n", errno);
+ dump_cpu_state(cpufd, (char*)vm_mem);
+ return 1;
+ }
+ if (cpu_mem->exit_reason != reason) {
+ printf("KVM_RUN exit reason %d, expect %d\n", cpu_mem->exit_reason, reason);
+ if (cpu_mem->exit_reason == KVM_EXIT_FAIL_ENTRY)
+ printf("hardware exit reason 0x%llx\n", cpu_mem->fail_entry.hardware_entry_failure_reason);
+ dump_cpu_state(cpufd, (char*)vm_mem);
+ return 1;
+ }
+ if (check_rax && regs.rax != 0xbadc0de) {
+ printf("wrong result: rax=0x%llx\n", (long long)regs.rax);
+ dump_cpu_state(cpufd, (char*)vm_mem);
+ return 1;
+ }
+ munmap(vm_mem, vm_mem_size);
+ munmap(cpu_mem, cpu_mem_size);
+ close(cpufd);
+ close(vmfd);
+ close(kvmfd);
+ return 0;
+}
+
+extern "C" int test_kvm()
+{
+ int res;
+
+ unsigned ver = host_kernel_version();
+ printf("host kernel version %u\n", ver);
+
+ // TODO: test VM mode.
+ //const char text16_vm[] = "\x48\xc7\xc3\xde\xc0\xad\x0b\x90\x90\x48\xc7\xc0\xef\xcd\xab\x00\xf4";
+ //if (res = test_one(64, text16_vm, sizeof(text16_vm) - 1, KVM_SETUP_VM, KVM_EXIT_HLT, true))
+ // return res;
+
+ /// TODO: test code executed in interrupt handlers.
+ //const char text32_div0[] = "\x31\xc0\xf7\xf0";
+ //if (res = test_one(32, text32_div0, sizeof(text32_div0)-1, 0, KVM_EXIT_HLT, true))
+ // return res;
+
+ const char text16[] = "\x66\xb8\xde\xc0\xad\x0b";
+ if (res = test_one(16, text16, sizeof(text16) - 1, 0, KVM_EXIT_HLT, true))
+ return res;
+ if (res = test_one(16, text16, sizeof(text16) - 1, KVM_SETUP_PROTECTED, KVM_EXIT_HLT, true))
+ return res;
+ if (res = test_one(16, text16, sizeof(text16) - 1, KVM_SETUP_CPL3, KVM_EXIT_SHUTDOWN, true))
+ return res;
+ if (res = test_one(16, text16, sizeof(text16) - 1, KVM_SETUP_VIRT86, KVM_EXIT_SHUTDOWN, true))
+ return res;
+ if (res = test_one(16, text16, sizeof(text16) - 1, KVM_SETUP_VIRT86 | KVM_SETUP_PAGING, KVM_EXIT_SHUTDOWN, true))
+ return res;
+
+ const char text32[] = "\xb8\xde\xc0\xad\x0b";
+ if (res = test_one(32, text32, sizeof(text32) - 1, 0, KVM_EXIT_HLT, true))
+ return res;
+ if (res = test_one(32, text32, sizeof(text32) - 1, KVM_SETUP_PAGING, KVM_EXIT_HLT, true))
+ return res;
+ if (res = test_one(32, text32, sizeof(text32) - 1, KVM_SETUP_CPL3, KVM_EXIT_SHUTDOWN, true))
+ return res;
+
+ const char text64[] = "\x90\xb8\xde\xc0\xad\x0b";
+ if (res = test_one(64, text64, sizeof(text64) - 1, 0, KVM_EXIT_HLT, true))
+ return res;
+ if (res = test_one(64, text64, sizeof(text64) - 1, KVM_SETUP_PAGING, KVM_EXIT_HLT, true))
+ return res;
+ if (res = test_one(64, text64, sizeof(text64) - 1, KVM_SETUP_CPL3, KVM_EXIT_SHUTDOWN, true))
+ return res;
+
+ const char text64_sysenter[] = "\xb8\xde\xc0\xad\x0b\x0f\x34";
+ if (res = test_one(64, text64_sysenter, sizeof(text64_sysenter) - 1, KVM_SETUP_CPL3, KVM_EXIT_SHUTDOWN, true))
+ return res;
+
+ // Note: SMM does not work on 3.13 kernels.
+ if (ver >= 404) {
+ const char text16_smm[] = "\x66\xb8\xde\xc0\xad\x0b";
+ if (res = test_one(16, text16_smm, sizeof(text16_smm) - 1, KVM_SETUP_SMM, KVM_EXIT_HLT, true))
+ return res;
+ if (res = test_one(16, text16_smm, sizeof(text16_smm) - 1, KVM_SETUP_SMM | KVM_SETUP_PROTECTED, KVM_EXIT_HLT, true))
+ return res;
+
+ //const char text32_smm[] = "\xb8\xde\xc0\xad\x0b";
+ if (res = test_one(32, text16_smm, sizeof(text16_smm) - 1, KVM_SETUP_SMM, KVM_EXIT_HLT, true))
+ return res;
+
+ // Also ensure that we are actually in SMM.
+ // If we do MOV to RAX and then RSM, RAX will be restored to host value so RAX check will fail.
+ // So instead we execute just RSM, if we are in SMM we will get KVM_EXIT_HLT, otherwise KVM_EXIT_INTERNAL_ERROR.
+ const char text_rsm[] = "\x0f\xaa";
+ if (res = test_one(16, text_rsm, sizeof(text_rsm) - 1, KVM_SETUP_SMM, KVM_EXIT_HLT, false))
+ return res;
+ if (res = test_one(32, text_rsm, sizeof(text_rsm) - 1, KVM_SETUP_SMM, KVM_EXIT_HLT, false))
+ return res;
+ }
+
+ return 0;
+}
+
+static unsigned host_kernel_version()
+{
+ struct utsname name;
+ if (uname(&name)) {
+ printf("uname failed (%d)\n", errno);
+ doexit(1);
+ }
+ unsigned major = atoi(name.release);
+ unsigned minor = 0;
+ if (strchr(name.release, '.'))
+ minor = atoi(strchr(name.release, '.') + 1);
+ return major * 100 + minor;
+}
+
+static void dump_seg(const char* name, struct kvm_segment* seg)
+{
+ printf("%s: base=0x%llx limit=0x%x sel=0x%x type=%d p=%d dpl=%d, db=%d s=%d l=%d g=%d\n",
+ name, seg->base, seg->limit, seg->selector, seg->type, seg->present, seg->dpl, seg->db, seg->s, seg->l, seg->g);
+}
+
+static void dump_cpu_state(int cpufd, char* vm_mem)
+{
+ struct kvm_sregs sregs;
+ if (ioctl(cpufd, KVM_GET_SREGS, &sregs)) {
+ printf("KVM_GET_SREGS failed (%d)\n", errno);
+ return;
+ }
+ struct kvm_regs regs;
+ if (ioctl(cpufd, KVM_GET_REGS, &regs)) {
+ printf("KVM_GET_REGS failed (%d)\n", errno);
+ return;
+ }
+ printf("RIP=0x%llx RAX=0x%llx RDX=0x%llx RCX=0x%llx RBX=0x%llx CF=%d ZF=%d\n",
+ regs.rip, regs.rax, regs.rdx, regs.rcx, regs.rbx, !!(regs.rflags & (1 << 0)), !!(regs.rflags & (1 << 6)));
+ printf("CR0=0x%llx CR2=0x%llx CR4=0x%llx EFER=0x%llx\n",
+ sregs.cr0, sregs.cr2, sregs.cr4, sregs.efer);
+ dump_seg("CS", &sregs.cs);
+ dump_seg("SS", &sregs.ss);
+ dump_seg("DS", &sregs.ds);
+
+ if (false) {
+ printf("memory:\n");
+ for (int i = 0; i < 0x80; i++)
+ printf("0x%02x: 0x%02x\n", i, ((unsigned char*)vm_mem)[i]);
+ }
+
+ if (false) {
+ printf("vmcs:\n");
+ const int vmcs_size = 0x1000;
+ for (int i = 0; i < vmcs_size / 8; i += 4) {
+ printf("0x%04x: 0x%016llx 0x%016llx 0x%016llx 0x%016llx\n", i,
+ ((long long*)vm_mem)[i], ((long long*)vm_mem)[i + 1], ((long long*)vm_mem)[i + 2], ((long long*)vm_mem)[i + 3]);
+ }
+ }
+}
diff --git a/executor/test_test.go b/executor/test_test.go
new file mode 100644
index 000000000..86379b93b
--- /dev/null
+++ b/executor/test_test.go
@@ -0,0 +1,16 @@
+// Copyright 2016 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+package executor
+
+import "testing"
+
+func TestKVM(t *testing.T) {
+ switch res := testKVM(); {
+ case res < 0:
+ t.Skip()
+ case res > 0:
+ t.Fail()
+ default:
+ }
+}