map[SimplifiedCRepro:// autogenerated by syzkaller (https://github.com/google/syzkaller)
#define _GNU_SOURCE
#include <endian.h>
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#include <linux/kvm.h>
#ifndef __NR_userfaultfd
#define __NR_userfaultfd 323
#endif
#define noinline __attribute__((noinline))
#define always_inline __attribute__((always_inline)) inline
#define __no_stack_protector
#define __addrspace_guest
#define __optnone
#define GUEST_CODE __attribute__((section("guest"))) __no_stack_protector __addrspace_guest
extern char *__start_guest, *__stop_guest;
struct api_call_header {
uint64_t call;
uint64_t size;
};
struct api_call_1 {
struct api_call_header header;
uint64_t arg;
};
struct api_call_2 {
struct api_call_header header;
uint64_t args[2];
};
struct api_call_3 {
struct api_call_header header;
uint64_t args[3];
};
struct api_call_5 {
struct api_call_header header;
uint64_t args[5];
};
#define X86_ADDR_TEXT 0x0000
#define X86_ADDR_PD_IOAPIC 0x0000
#define X86_ADDR_GDT 0x1000
#define X86_ADDR_LDT 0x1800
#define X86_ADDR_PML4 0x2000
#define X86_ADDR_PDP 0x3000
#define X86_ADDR_PD 0x4000
#define X86_ADDR_STACK0 0x0f80
#define X86_ADDR_VAR_HLT 0x2800
#define X86_ADDR_VAR_SYSRET 0x2808
#define X86_ADDR_VAR_SYSEXIT 0x2810
#define X86_ADDR_VAR_IDT 0x3800
#define X86_ADDR_VAR_TSS64 0x3a00
#define X86_ADDR_VAR_TSS64_CPL3 0x3c00
#define X86_ADDR_VAR_TSS16 0x3d00
#define X86_ADDR_VAR_TSS16_2 0x3e00
#define X86_ADDR_VAR_TSS16_CPL3 0x3f00
#define X86_ADDR_VAR_TSS32 0x4800
#define X86_ADDR_VAR_TSS32_2 0x4a00
#define X86_ADDR_VAR_TSS32_CPL3 0x4c00
#define X86_ADDR_VAR_TSS32_VM86 0x4e00
#define X86_ADDR_VAR_VMXON_PTR 0x5f00
#define X86_ADDR_VAR_VMCS_PTR 0x5f08
#define X86_ADDR_VAR_VMEXIT_PTR 0x5f10
#define X86_ADDR_VAR_VMWRITE_FLD 0x5f18
#define X86_ADDR_VAR_VMWRITE_VAL 0x5f20
#define X86_ADDR_VAR_VMXON 0x6000
#define X86_ADDR_VAR_VMCS 0x7000
#define X86_ADDR_VAR_VMEXIT_CODE 0x9000
#define X86_ADDR_VAR_USER_CODE 0x9100
#define X86_ADDR_VAR_USER_CODE2 0x9120
#define X86_SYZOS_ADDR_ZERO 0x0
#define X86_SYZOS_ADDR_GDT 0x1000
#define X86_SYZOS_ADDR_PML4 0x2000
#define X86_SYZOS_ADDR_PDP 0x3000
#define X86_SYZOS_ADDR_VAR_IDT 0x25000
#define X86_SYZOS_ADDR_VAR_TSS 0x26000
#define X86_SYZOS_ADDR_BOOT_ARGS 0x2F000
#define X86_SYZOS_ADDR_SMRAM 0x30000
#define X86_SYZOS_ADDR_EXIT 0x40000
#define X86_SYZOS_ADDR_UEXIT (X86_SYZOS_ADDR_EXIT + 256)
#define X86_SYZOS_ADDR_DIRTY_PAGES 0x41000
#define X86_SYZOS_ADDR_USER_CODE 0x50000
#define SYZOS_ADDR_EXECUTOR_CODE 0x54000
#define X86_SYZOS_ADDR_SCRATCH_CODE 0x58000
#define X86_SYZOS_ADDR_STACK_BOTTOM 0x60000
#define X86_SYZOS_ADDR_STACK0 0x60f80
#define X86_SYZOS_PER_VCPU_REGIONS_BASE 0x400000
#define X86_SYZOS_L1_VCPU_REGION_SIZE 0x40000
#define X86_SYZOS_L1_VCPU_OFFSET_VM_ARCH_SPECIFIC 0x0000
#define X86_SYZOS_L1_VCPU_OFFSET_L2_VMS_AREA 0x1000
#define X86_SYZOS_ADDR_GLOBALS 0x17F000
#define X86_SYZOS_ADDR_PT_POOL 0x180000
#define X86_SYZOS_PT_POOL_SIZE 64
#define X86_SYZOS_L2_VM_REGION_SIZE 0x8000
#define X86_SYZOS_L2_VM_OFFSET_VMCS_VMCB 0x0000
#define X86_SYZOS_L2_VM_OFFSET_VM_STACK 0x1000
#define X86_SYZOS_L2_VM_OFFSET_VM_CODE 0x2000
#define X86_SYZOS_L2_VM_OFFSET_VM_PGTABLE 0x3000
#define X86_SYZOS_L2_VM_OFFSET_MSR_BITMAP 0x7000
#define X86_SYZOS_ADDR_UNUSED 0x1000000
#define X86_SYZOS_ADDR_IOAPIC 0xfec00000
#define X86_SYZOS_ADDR_VMCS_VMCB(cpu,vm) (X86_SYZOS_PER_VCPU_REGIONS_BASE + (cpu) * X86_SYZOS_L1_VCPU_REGION_SIZE + X86_SYZOS_L1_VCPU_OFFSET_L2_VMS_AREA + (vm) * X86_SYZOS_L2_VM_REGION_SIZE + X86_SYZOS_L2_VM_OFFSET_VMCS_VMCB)
#define X86_SYZOS_ADDR_VM_CODE(cpu,vm) (X86_SYZOS_PER_VCPU_REGIONS_BASE + (cpu) * X86_SYZOS_L1_VCPU_REGION_SIZE + X86_SYZOS_L1_VCPU_OFFSET_L2_VMS_AREA + (vm) * X86_SYZOS_L2_VM_REGION_SIZE + X86_SYZOS_L2_VM_OFFSET_VM_CODE)
#define X86_SYZOS_ADDR_VM_STACK(cpu,vm) (X86_SYZOS_PER_VCPU_REGIONS_BASE + (cpu) * X86_SYZOS_L1_VCPU_REGION_SIZE + X86_SYZOS_L1_VCPU_OFFSET_L2_VMS_AREA + (vm) * X86_SYZOS_L2_VM_REGION_SIZE + X86_SYZOS_L2_VM_OFFSET_VM_STACK)
#define X86_SYZOS_ADDR_VM_PGTABLE(cpu,vm) (X86_SYZOS_PER_VCPU_REGIONS_BASE + (cpu) * X86_SYZOS_L1_VCPU_REGION_SIZE + X86_SYZOS_L1_VCPU_OFFSET_L2_VMS_AREA + (vm) * X86_SYZOS_L2_VM_REGION_SIZE + X86_SYZOS_L2_VM_OFFSET_VM_PGTABLE)
#define X86_SYZOS_ADDR_MSR_BITMAP(cpu,vm) (X86_SYZOS_PER_VCPU_REGIONS_BASE + (cpu) * X86_SYZOS_L1_VCPU_REGION_SIZE + X86_SYZOS_L1_VCPU_OFFSET_L2_VMS_AREA + (vm) * X86_SYZOS_L2_VM_REGION_SIZE + X86_SYZOS_L2_VM_OFFSET_MSR_BITMAP)
#define X86_SYZOS_ADDR_VM_ARCH_SPECIFIC(cpu) (X86_SYZOS_PER_VCPU_REGIONS_BASE + (cpu) * X86_SYZOS_L1_VCPU_REGION_SIZE + X86_SYZOS_L1_VCPU_OFFSET_VM_ARCH_SPECIFIC)
#define X86_SYZOS_SEL_CODE 0x8
#define X86_SYZOS_SEL_DATA 0x10
#define X86_SYZOS_SEL_TSS64 0x18
#define X86_CR0_PE 1ULL
#define X86_CR0_MP (1ULL << 1)
#define X86_CR0_EM (1ULL << 2)
#define X86_CR0_TS (1ULL << 3)
#define X86_CR0_ET (1ULL << 4)
#define X86_CR0_NE (1ULL << 5)
#define X86_CR0_WP (1ULL << 16)
#define X86_CR0_AM (1ULL << 18)
#define X86_CR0_NW (1ULL << 29)
#define X86_CR0_CD (1ULL << 30)
#define X86_CR0_PG (1ULL << 31)
#define X86_CR4_VME 1ULL
#define X86_CR4_PVI (1ULL << 1)
#define X86_CR4_TSD (1ULL << 2)
#define X86_CR4_DE (1ULL << 3)
#define X86_CR4_PSE (1ULL << 4)
#define X86_CR4_PAE (1ULL << 5)
#define X86_CR4_MCE (1ULL << 6)
#define X86_CR4_PGE (1ULL << 7)
#define X86_CR4_PCE (1ULL << 8)
#define X86_CR4_OSFXSR (1ULL << 9)
#define X86_CR4_OSXMMEXCPT (1ULL << 10)
#define X86_CR4_UMIP (1ULL << 11)
#define X86_CR4_VMXE (1ULL << 13)
#define X86_CR4_SMXE (1ULL << 14)
#define X86_CR4_FSGSBASE (1ULL << 16)
#define X86_CR4_PCIDE (1ULL << 17)
#define X86_CR4_OSXSAVE (1ULL << 18)
#define X86_CR4_SMEP (1ULL << 20)
#define X86_CR4_SMAP (1ULL << 21)
#define X86_CR4_PKE (1ULL << 22)
#define X86_EFER_SCE 1ULL
#define X86_EFER_LME (1ULL << 8)
#define X86_EFER_LMA (1ULL << 10)
#define X86_EFER_NXE (1ULL << 11)
#define X86_EFER_SVME (1ULL << 12)
#define X86_EFER_LMSLE (1ULL << 13)
#define X86_EFER_FFXSR (1ULL << 14)
#define X86_EFER_TCE (1ULL << 15)
#define X86_PDE32_PRESENT 1UL
#define X86_PDE32_RW (1UL << 1)
#define X86_PDE32_USER (1UL << 2)
#define X86_PDE32_PS (1UL << 7)
#define X86_PDE64_PRESENT 1
#define X86_PDE64_RW (1ULL << 1)
#define X86_PDE64_USER (1ULL << 2)
#define X86_PDE64_ACCESSED (1ULL << 5)
#define X86_PDE64_DIRTY (1ULL << 6)
#define X86_PDE64_PS (1ULL << 7)
#define X86_PDE64_G (1ULL << 8)
#define EPT_MEMTYPE_WB (6ULL << 3)
#define EPT_ACCESSED (1ULL << 8)
#define EPT_DIRTY (1ULL << 9)
#define X86_SEL_LDT (1 << 3)
#define X86_SEL_CS16 (2 << 3)
#define X86_SEL_DS16 (3 << 3)
#define X86_SEL_CS16_CPL3 ((4 << 3) + 3)
#define X86_SEL_DS16_CPL3 ((5 << 3) + 3)
#define X86_SEL_CS32 (6 << 3)
#define X86_SEL_DS32 (7 << 3)
#define X86_SEL_CS32_CPL3 ((8 << 3) + 3)
#define X86_SEL_DS32_CPL3 ((9 << 3) + 3)
#define X86_SEL_CS64 (10 << 3)
#define X86_SEL_DS64 (11 << 3)
#define X86_SEL_CS64_CPL3 ((12 << 3) + 3)
#define X86_SEL_DS64_CPL3 ((13 << 3) + 3)
#define X86_SEL_CGATE16 (14 << 3)
#define X86_SEL_TGATE16 (15 << 3)
#define X86_SEL_CGATE32 (16 << 3)
#define X86_SEL_TGATE32 (17 << 3)
#define X86_SEL_CGATE64 (18 << 3)
#define X86_SEL_CGATE64_HI (19 << 3)
#define X86_SEL_TSS16 (20 << 3)
#define X86_SEL_TSS16_2 (21 << 3)
#define X86_SEL_TSS16_CPL3 ((22 << 3) + 3)
#define X86_SEL_TSS32 (23 << 3)
#define X86_SEL_TSS32_2 (24 << 3)
#define X86_SEL_TSS32_CPL3 ((25 << 3) + 3)
#define X86_SEL_TSS32_VM86 (26 << 3)
#define X86_SEL_TSS64 (27 << 3)
#define X86_SEL_TSS64_HI (28 << 3)
#define X86_SEL_TSS64_CPL3 ((29 << 3) + 3)
#define X86_SEL_TSS64_CPL3_HI (30 << 3)
#define X86_MSR_IA32_FEATURE_CONTROL 0x3a
#define X86_MSR_IA32_VMX_BASIC 0x480
#define X86_MSR_IA32_SMBASE 0x9e
#define X86_MSR_IA32_SYSENTER_CS 0x174
#define X86_MSR_IA32_SYSENTER_ESP 0x175
#define X86_MSR_IA32_SYSENTER_EIP 0x176
#define X86_MSR_IA32_CR_PAT 0x277
#define X86_MSR_CORE_PERF_GLOBAL_CTRL 0x38f
#define X86_MSR_IA32_VMX_TRUE_PINBASED_CTLS 0x48d
#define X86_MSR_IA32_VMX_TRUE_PROCBASED_CTLS 0x48e
#define X86_MSR_IA32_VMX_TRUE_EXIT_CTLS 0x48f
#define X86_MSR_IA32_VMX_TRUE_ENTRY_CTLS 0x490
#define X86_MSR_IA32_EFER 0xc0000080
#define X86_MSR_IA32_STAR 0xC0000081
#define X86_MSR_IA32_LSTAR 0xC0000082
#define X86_MSR_FS_BASE 0xc0000100
#define X86_MSR_GS_BASE 0xc0000101
#define X86_MSR_VM_HSAVE_PA 0xc0010117
#define X86_MSR_IA32_VMX_PROCBASED_CTLS2 0x48B
#define RFLAGS_1_BIT (1ULL << 1)
#define CPU_BASED_HLT_EXITING (1U << 7)
#define CPU_BASED_RDTSC_EXITING (1U << 12)
#define AR_TSS_AVAILABLE 0x0089
#define SVM_ATTR_LDTR_UNUSABLE 0x0000
#define VMX_AR_TSS_BUSY 0x008b
#define VMX_AR_TSS_AVAILABLE 0x0089
#define VMX_AR_LDTR_UNUSABLE 0x10000
#define VM_ENTRY_IA32E_MODE (1U << 9)
#define SECONDARY_EXEC_ENABLE_EPT (1U << 1)
#define SECONDARY_EXEC_ENABLE_RDTSCP (1U << 3)
#define VM_EXIT_HOST_ADDR_SPACE_SIZE (1U << 9)
#define CPU_BASED_ACTIVATE_SECONDARY_CONTROLS (1U << 31)
#define VMX_ACCESS_RIGHTS_P (1 << 7)
#define VMX_ACCESS_RIGHTS_S (1 << 4)
#define VMX_ACCESS_RIGHTS_TYPE_A (1 << 0)
#define VMX_ACCESS_RIGHTS_TYPE_RW (1 << 1)
#define VMX_ACCESS_RIGHTS_TYPE_E (1 << 3)
#define VMX_ACCESS_RIGHTS_G (1 << 15)
#define VMX_ACCESS_RIGHTS_DB (1 << 14)
#define VMX_ACCESS_RIGHTS_L (1 << 13)
#define VMX_AR_64BIT_DATA_STACK (VMX_ACCESS_RIGHTS_P | VMX_ACCESS_RIGHTS_S | VMX_ACCESS_RIGHTS_TYPE_RW | VMX_ACCESS_RIGHTS_TYPE_A | VMX_ACCESS_RIGHTS_G | VMX_ACCESS_RIGHTS_DB)
#define VMX_AR_64BIT_CODE (VMX_ACCESS_RIGHTS_P | VMX_ACCESS_RIGHTS_S | VMX_ACCESS_RIGHTS_TYPE_E | VMX_ACCESS_RIGHTS_TYPE_RW | VMX_ACCESS_RIGHTS_TYPE_A | VMX_ACCESS_RIGHTS_G | VMX_ACCESS_RIGHTS_L)
#define VMCS_VIRTUAL_PROCESSOR_ID 0x00000000
#define VMCS_POSTED_INTR_NV 0x00000002
#define VMCS_MSR_BITMAP 0x00002004
#define VMCS_VMREAD_BITMAP 0x00002006
#define VMCS_VMWRITE_BITMAP 0x00002008
#define VMCS_EPT_POINTER 0x0000201a
#define VMCS_LINK_POINTER 0x00002800
#define VMCS_PIN_BASED_VM_EXEC_CONTROL 0x00004000
#define VMCS_CPU_BASED_VM_EXEC_CONTROL 0x00004002
#define VMCS_EXCEPTION_BITMAP 0x00004004
#define VMCS_PAGE_FAULT_ERROR_CODE_MASK 0x00004006
#define VMCS_PAGE_FAULT_ERROR_CODE_MATCH 0x00004008
#define VMCS_CR3_TARGET_COUNT 0x0000400a
#define VMCS_VM_EXIT_CONTROLS 0x0000400c
#define VMCS_VM_EXIT_MSR_STORE_COUNT 0x0000400e
#define VMCS_VM_EXIT_MSR_LOAD_COUNT 0x00004010
#define VMCS_VM_ENTRY_CONTROLS 0x00004012
#define VMCS_VM_ENTRY_MSR_LOAD_COUNT 0x00004014
#define VMCS_VM_ENTRY_INTR_INFO_FIELD 0x00004016
#define VMCS_TPR_THRESHOLD 0x0000401c
#define VMCS_SECONDARY_VM_EXEC_CONTROL 0x0000401e
#define VMCS_VM_INSTRUCTION_ERROR 0x00004400
#define VMCS_VM_EXIT_REASON 0x00004402
#define VMCS_VMX_PREEMPTION_TIMER_VALUE 0x0000482e
#define VMCS_CR0_GUEST_HOST_MASK 0x00006000
#define VMCS_CR4_GUEST_HOST_MASK 0x00006002
#define VMCS_CR0_READ_SHADOW 0x00006004
#define VMCS_CR4_READ_SHADOW 0x00006006
#define VMCS_HOST_ES_SELECTOR 0x00000c00
#define VMCS_HOST_CS_SELECTOR 0x00000c02
#define VMCS_HOST_SS_SELECTOR 0x00000c04
#define VMCS_HOST_DS_SELECTOR 0x00000c06
#define VMCS_HOST_FS_SELECTOR 0x00000c08
#define VMCS_HOST_GS_SELECTOR 0x00000c0a
#define VMCS_HOST_TR_SELECTOR 0x00000c0c
#define VMCS_HOST_IA32_PAT 0x00002c00
#define VMCS_HOST_IA32_EFER 0x00002c02
#define VMCS_HOST_IA32_PERF_GLOBAL_CTRL 0x00002c04
#define VMCS_HOST_IA32_SYSENTER_CS 0x00004c00
#define VMCS_HOST_CR0 0x00006c00
#define VMCS_HOST_CR3 0x00006c02
#define VMCS_HOST_CR4 0x00006c04
#define VMCS_HOST_FS_BASE 0x00006c06
#define VMCS_HOST_GS_BASE 0x00006c08
#define VMCS_HOST_TR_BASE 0x00006c0a
#define VMCS_HOST_GDTR_BASE 0x00006c0c
#define VMCS_HOST_IDTR_BASE 0x00006c0e
#define VMCS_HOST_IA32_SYSENTER_ESP 0x00006c10
#define VMCS_HOST_IA32_SYSENTER_EIP 0x00006c12
#define VMCS_HOST_RSP 0x00006c14
#define VMCS_HOST_RIP 0x00006c16
#define VMCS_GUEST_INTR_STATUS 0x00000810
#define VMCS_GUEST_PML_INDEX 0x00000812
#define VMCS_GUEST_PHYSICAL_ADDRESS 0x00002400
#define VMCS_GUEST_IA32_DEBUGCTL 0x00002802
#define VMCS_GUEST_IA32_PAT 0x00002804
#define VMCS_GUEST_IA32_EFER 0x00002806
#define VMCS_GUEST_IA32_PERF_GLOBAL_CTRL 0x00002808
#define VMCS_GUEST_ES_SELECTOR 0x00000800
#define VMCS_GUEST_CS_SELECTOR 0x00000802
#define VMCS_GUEST_SS_SELECTOR 0x00000804
#define VMCS_GUEST_DS_SELECTOR 0x00000806
#define VMCS_GUEST_FS_SELECTOR 0x00000808
#define VMCS_GUEST_GS_SELECTOR 0x0000080a
#define VMCS_GUEST_LDTR_SELECTOR 0x0000080c
#define VMCS_GUEST_TR_SELECTOR 0x0000080e
#define VMCS_GUEST_ES_LIMIT 0x00004800
#define VMCS_GUEST_CS_LIMIT 0x00004802
#define VMCS_GUEST_SS_LIMIT 0x00004804
#define VMCS_GUEST_DS_LIMIT 0x00004806
#define VMCS_GUEST_FS_LIMIT 0x00004808
#define VMCS_GUEST_GS_LIMIT 0x0000480a
#define VMCS_GUEST_LDTR_LIMIT 0x0000480c
#define VMCS_GUEST_TR_LIMIT 0x0000480e
#define VMCS_GUEST_GDTR_LIMIT 0x00004810
#define VMCS_GUEST_IDTR_LIMIT 0x00004812
#define VMCS_GUEST_ES_ACCESS_RIGHTS 0x00004814
#define VMCS_GUEST_CS_ACCESS_RIGHTS 0x00004816
#define VMCS_GUEST_SS_ACCESS_RIGHTS 0x00004818
#define VMCS_GUEST_DS_ACCESS_RIGHTS 0x0000481a
#define VMCS_GUEST_FS_ACCESS_RIGHTS 0x0000481c
#define VMCS_GUEST_GS_ACCESS_RIGHTS 0x0000481e
#define VMCS_GUEST_LDTR_ACCESS_RIGHTS 0x00004820
#define VMCS_GUEST_TR_ACCESS_RIGHTS 0x00004822
#define VMCS_GUEST_ACTIVITY_STATE 0x00004824
#define VMCS_GUEST_INTERRUPTIBILITY_INFO 0x00004826
#define VMCS_GUEST_SYSENTER_CS 0x0000482a
#define VMCS_GUEST_CR0 0x00006800
#define VMCS_GUEST_CR3 0x00006802
#define VMCS_GUEST_CR4 0x00006804
#define VMCS_GUEST_ES_BASE 0x00006806
#define VMCS_GUEST_CS_BASE 0x00006808
#define VMCS_GUEST_SS_BASE 0x0000680a
#define VMCS_GUEST_DS_BASE 0x0000680c
#define VMCS_GUEST_FS_BASE 0x0000680e
#define VMCS_GUEST_GS_BASE 0x00006810
#define VMCS_GUEST_LDTR_BASE 0x00006812
#define VMCS_GUEST_TR_BASE 0x00006814
#define VMCS_GUEST_GDTR_BASE 0x00006816
#define VMCS_GUEST_IDTR_BASE 0x00006818
#define VMCS_GUEST_DR7 0x0000681a
#define VMCS_GUEST_RSP 0x0000681c
#define VMCS_GUEST_RIP 0x0000681e
#define VMCS_GUEST_RFLAGS 0x00006820
#define VMCS_GUEST_PENDING_DBG_EXCEPTIONS 0x00006822
#define VMCS_GUEST_SYSENTER_ESP 0x00006824
#define VMCS_GUEST_SYSENTER_EIP 0x00006826
#define VMCB_CTRL_INTERCEPT_VEC3 0x0c
#define VMCB_CTRL_INTERCEPT_VEC3_ALL (0xffffffff)
#define VMCB_CTRL_INTERCEPT_VEC4 0x10
#define VMCB_CTRL_INTERCEPT_VEC4_ALL (0x3ff)
#define VMCB_CTRL_ASID 0x058
#define VMCB_EXIT_CODE 0x070
#define VMCB_EXITINFO2 0x080
#define VMCB_CTRL_NP_ENABLE 0x090
#define VMCB_CTRL_NPT_ENABLE_BIT 0
#define VMCB_CTRL_N_CR3 0x0b0
#define VMCB_GUEST_ES_SEL 0x400
#define VMCB_GUEST_ES_ATTR 0x402
#define VMCB_GUEST_ES_LIM 0x404
#define VMCB_GUEST_ES_BASE 0x408
#define VMCB_GUEST_CS_SEL 0x410
#define VMCB_GUEST_CS_ATTR 0x412
#define VMCB_GUEST_CS_LIM 0x414
#define VMCB_GUEST_CS_BASE 0x418
#define VMCB_GUEST_SS_SEL 0x420
#define VMCB_GUEST_SS_ATTR 0x422
#define VMCB_GUEST_SS_LIM 0x424
#define VMCB_GUEST_SS_BASE 0x428
#define VMCB_GUEST_DS_SEL 0x430
#define VMCB_GUEST_DS_ATTR 0x432
#define VMCB_GUEST_DS_LIM 0x434
#define VMCB_GUEST_DS_BASE 0x438
#define VMCB_GUEST_FS_SEL 0x440
#define VMCB_GUEST_FS_ATTR 0x442
#define VMCB_GUEST_FS_LIM 0x444
#define VMCB_GUEST_FS_BASE 0x448
#define VMCB_GUEST_GS_SEL 0x450
#define VMCB_GUEST_GS_ATTR 0x452
#define VMCB_GUEST_GS_LIM 0x454
#define VMCB_GUEST_GS_BASE 0x458
#define VMCB_GUEST_IDTR_SEL 0x480
#define VMCB_GUEST_IDTR_ATTR 0x482
#define VMCB_GUEST_IDTR_LIM 0x484
#define VMCB_GUEST_IDTR_BASE 0x488
#define VMCB_GUEST_GDTR_SEL 0x460
#define VMCB_GUEST_GDTR_ATTR 0x462
#define VMCB_GUEST_GDTR_LIM 0x464
#define VMCB_GUEST_GDTR_BASE 0x468
#define VMCB_GUEST_LDTR_SEL 0x470
#define VMCB_GUEST_LDTR_ATTR 0x472
#define VMCB_GUEST_LDTR_LIM 0x474
#define VMCB_GUEST_LDTR_BASE 0x478
#define VMCB_GUEST_TR_SEL 0x490
#define VMCB_GUEST_TR_ATTR 0x492
#define VMCB_GUEST_TR_LIM 0x494
#define VMCB_GUEST_TR_BASE 0x498
#define VMCB_GUEST_EFER 0x4d0
#define VMCB_GUEST_CR4 0x548
#define VMCB_GUEST_CR3 0x550
#define VMCB_GUEST_CR0 0x558
#define VMCB_GUEST_DR7 0x560
#define VMCB_GUEST_DR6 0x568
#define VMCB_GUEST_RFLAGS 0x570
#define VMCB_GUEST_RIP 0x578
#define VMCB_GUEST_RSP 0x5d8
#define VMCB_GUEST_PAT 0x668
#define VMCB_GUEST_DEBUGCTL 0x670
#define VMCB_RAX 0x5f8
#define SVM_ATTR_G (1 << 15)
#define SVM_ATTR_DB (1 << 14)
#define SVM_ATTR_L (1 << 13)
#define SVM_ATTR_P (1 << 7)
#define SVM_ATTR_S (1 << 4)
#define SVM_ATTR_TYPE_A (1 << 0)
#define SVM_ATTR_TYPE_RW (1 << 1)
#define SVM_ATTR_TYPE_E (1 << 3)
#define SVM_ATTR_TSS_BUSY 0x008b
#define SVM_ATTR_64BIT_CODE (SVM_ATTR_P | SVM_ATTR_S | SVM_ATTR_TYPE_E | SVM_ATTR_TYPE_RW | SVM_ATTR_TYPE_A | SVM_ATTR_L | SVM_ATTR_G)
#define SVM_ATTR_64BIT_DATA (SVM_ATTR_P | SVM_ATTR_S | SVM_ATTR_TYPE_RW | SVM_ATTR_TYPE_A | SVM_ATTR_DB | SVM_ATTR_G)
#define X86_NEXT_INSN $0xbadc0de
#define X86_PREFIX_SIZE 0xba1d
#define KVM_MAX_VCPU 4
#define KVM_MAX_L2_VMS 4
#define KVM_PAGE_SIZE (1 << 12)
#define KVM_GUEST_PAGES 1024
#define KVM_GUEST_MEM_SIZE (KVM_GUEST_PAGES * KVM_PAGE_SIZE)
#define SZ_4K 0x00001000
#define SZ_64K 0x00010000
#define GENMASK_ULL(h,l) (((~0ULL) - (1ULL << (l)) + 1ULL) & (~0ULL >> (63 - (h))))
extern char* __start_guest;
static always_inline uintptr_t executor_fn_guest_addr(void* fn)
{
volatile uintptr_t start = (uintptr_t)&__start_guest;
volatile uintptr_t offset = SYZOS_ADDR_EXECUTOR_CODE;
return (uintptr_t)fn - start + offset;
}
typedef enum {
SYZOS_API_UEXIT = 0,
SYZOS_API_CODE = 10,
SYZOS_API_CPUID = 100,
SYZOS_API_WRMSR = 101,
SYZOS_API_RDMSR = 102,
SYZOS_API_WR_CRN = 103,
SYZOS_API_WR_DRN = 104,
SYZOS_API_IN_DX = 105,
SYZOS_API_OUT_DX = 106,
SYZOS_API_SET_IRQ_HANDLER = 200,
SYZOS_API_ENABLE_NESTED = 300,
SYZOS_API_NESTED_CREATE_VM = 301,
SYZOS_API_NESTED_LOAD_CODE = 302,
SYZOS_API_NESTED_VMLAUNCH = 303,
SYZOS_API_NESTED_VMRESUME = 304,
SYZOS_API_NESTED_LOAD_SYZOS = 310,
SYZOS_API_NESTED_INTEL_VMWRITE_MASK = 340,
SYZOS_API_NESTED_AMD_VMCB_WRITE_MASK = 380,
SYZOS_API_NESTED_AMD_INVLPGA = 381,
SYZOS_API_NESTED_AMD_STGI = 382,
SYZOS_API_NESTED_AMD_CLGI = 383,
SYZOS_API_NESTED_AMD_INJECT_EVENT = 384,
SYZOS_API_NESTED_AMD_SET_INTERCEPT = 385,
SYZOS_API_NESTED_AMD_VMLOAD = 386,
SYZOS_API_NESTED_AMD_VMSAVE = 387,
SYZOS_API_STOP,
} syzos_api_id;
struct api_call_uexit {
struct api_call_header header;
uint64_t exit_code;
};
struct api_call_code {
struct api_call_header header;
uint8_t insns[];
};
struct api_call_nested_load_code {
struct api_call_header header;
uint64_t vm_id;
uint8_t insns[];
};
struct api_call_nested_load_syzos {
struct api_call_header header;
uint64_t vm_id;
uint64_t unused_pages;
uint8_t program[];
};
struct api_call_cpuid {
struct api_call_header header;
uint32_t eax;
uint32_t ecx;
};
struct l2_guest_regs {
uint64_t rax, rbx, rcx, rdx, rsi, rdi, rbp;
uint64_t r8, r9, r10, r11, r12, r13, r14, r15;
};
#define MEM_REGION_FLAG_USER_CODE (1 << 0)
#define MEM_REGION_FLAG_DIRTY_LOG (1 << 1)
#define MEM_REGION_FLAG_READONLY (1 << 2)
#define MEM_REGION_FLAG_EXECUTOR_CODE (1 << 3)
#define MEM_REGION_FLAG_GPA0 (1 << 5)
#define MEM_REGION_FLAG_NO_HOST_MEM (1 << 6)
#define MEM_REGION_FLAG_REMAINING (1 << 7)
struct mem_region {
uint64_t gpa;
int pages;
uint32_t flags;
};
struct syzos_boot_args {
uint32_t region_count;
uint32_t reserved;
struct mem_region regions[];
};
struct syzos_globals {
uint64_t alloc_offset;
uint64_t total_size;
uint64_t text_sizes[KVM_MAX_VCPU];
struct l2_guest_regs l2_ctx[KVM_MAX_VCPU][KVM_MAX_L2_VMS];
uint64_t active_vm_id[KVM_MAX_VCPU];
};
GUEST_CODE static void guest_uexit(uint64_t exit_code);
GUEST_CODE static void nested_vm_exit_handler_intel(uint64_t exit_reason, struct l2_guest_regs* regs);
GUEST_CODE static void nested_vm_exit_handler_amd(uint64_t exit_reason, struct l2_guest_regs* regs);
GUEST_CODE static void guest_execute_code(uint8_t* insns, uint64_t size);
GUEST_CODE static void guest_handle_cpuid(uint32_t eax, uint32_t ecx);
GUEST_CODE static void guest_handle_wrmsr(uint64_t reg, uint64_t val);
GUEST_CODE static void guest_handle_rdmsr(uint64_t reg);
GUEST_CODE static void guest_handle_wr_crn(struct api_call_2* cmd);
GUEST_CODE static void guest_handle_wr_drn(struct api_call_2* cmd);
GUEST_CODE static void guest_handle_in_dx(struct api_call_2* cmd);
GUEST_CODE static void guest_handle_out_dx(struct api_call_3* cmd);
GUEST_CODE static void guest_handle_set_irq_handler(struct api_call_2* cmd);
GUEST_CODE static void guest_handle_enable_nested(struct api_call_1* cmd, uint64_t cpu_id);
GUEST_CODE static void guest_handle_nested_create_vm(struct api_call_1* cmd, uint64_t cpu_id);
GUEST_CODE static void guest_handle_nested_load_code(struct api_call_nested_load_code* cmd, uint64_t cpu_id);
GUEST_CODE static void guest_handle_nested_load_syzos(struct api_call_nested_load_syzos* cmd, uint64_t cpu_id);
GUEST_CODE static void guest_handle_nested_vmlaunch(struct api_call_1* cmd, uint64_t cpu_id);
GUEST_CODE static void guest_handle_nested_vmresume(struct api_call_1* cmd, uint64_t cpu_id);
GUEST_CODE static void guest_handle_nested_intel_vmwrite_mask(struct api_call_5* cmd, uint64_t cpu_id);
GUEST_CODE static void guest_handle_nested_amd_vmcb_write_mask(struct api_call_5* cmd, uint64_t cpu_id);
GUEST_CODE static void guest_handle_nested_amd_invlpga(struct api_call_2* cmd, uint64_t cpu_id);
GUEST_CODE static void guest_handle_nested_amd_stgi();
GUEST_CODE static void guest_handle_nested_amd_clgi();
GUEST_CODE static void guest_handle_nested_amd_inject_event(struct api_call_5* cmd, uint64_t cpu_id);
GUEST_CODE static void guest_handle_nested_amd_set_intercept(struct api_call_5* cmd, uint64_t cpu_id);
GUEST_CODE static void guest_handle_nested_amd_vmload(struct api_call_1* cmd, uint64_t cpu_id);
GUEST_CODE static void guest_handle_nested_amd_vmsave(struct api_call_1* cmd, uint64_t cpu_id);
typedef enum {
UEXIT_END = (uint64_t)-1,
UEXIT_IRQ = (uint64_t)-2,
UEXIT_ASSERT = (uint64_t)-3,
UEXIT_INVALID_MAIN = (uint64_t)-4,
} uexit_code;
typedef enum {
CPU_VENDOR_INTEL,
CPU_VENDOR_AMD,
} cpu_vendor_id;
__attribute__((naked)) GUEST_CODE static void dummy_null_handler()
{
asm("iretq");
}
__attribute__((naked)) GUEST_CODE static void uexit_irq_handler()
{
asm volatile(R"(
movq $-2, %rdi
call guest_uexit
iretq
)");
}
__attribute__((used))
GUEST_CODE static void
guest_main(uint64_t cpu)
{
volatile struct syzos_globals* globals = (volatile struct syzos_globals*)X86_SYZOS_ADDR_GLOBALS;
uint64_t size = globals->text_sizes[cpu];
uint64_t addr = X86_SYZOS_ADDR_USER_CODE + cpu * KVM_PAGE_SIZE;
while (size >= sizeof(struct api_call_header)) {
struct api_call_header* cmd = (struct api_call_header*)addr;
volatile uint64_t call = cmd->call;
if ((call >= SYZOS_API_STOP) || (cmd->size > size)) {
guest_uexit(UEXIT_INVALID_MAIN);
return;
}
if (call == SYZOS_API_UEXIT) {
struct api_call_uexit* ucmd = (struct api_call_uexit*)cmd;
guest_uexit(ucmd->exit_code);
} else if (call == SYZOS_API_CODE) {
struct api_call_code* ccmd = (struct api_call_code*)cmd;
guest_execute_code(ccmd->insns, cmd->size - sizeof(struct api_call_header));
} else if (call == SYZOS_API_CPUID) {
struct api_call_cpuid* ccmd = (struct api_call_cpuid*)cmd;
guest_handle_cpuid(ccmd->eax, ccmd->ecx);
} else if (call == SYZOS_API_WRMSR) {
struct api_call_2* ccmd = (struct api_call_2*)cmd;
guest_handle_wrmsr(ccmd->args[0], ccmd->args[1]);
} else if (call == SYZOS_API_RDMSR) {
struct api_call_1* ccmd = (struct api_call_1*)cmd;
guest_handle_rdmsr(ccmd->arg);
} else if (call == SYZOS_API_WR_CRN) {
guest_handle_wr_crn((struct api_call_2*)cmd);
} else if (call == SYZOS_API_WR_DRN) {
guest_handle_wr_drn((struct api_call_2*)cmd);
} else if (call == SYZOS_API_IN_DX) {
guest_handle_in_dx((struct api_call_2*)cmd);
} else if (call == SYZOS_API_OUT_DX) {
guest_handle_out_dx((struct api_call_3*)cmd);
} else if (call == SYZOS_API_SET_IRQ_HANDLER) {
guest_handle_set_irq_handler((struct api_call_2*)cmd);
} else if (call == SYZOS_API_ENABLE_NESTED) {
guest_handle_enable_nested((struct api_call_1*)cmd, cpu);
} else if (call == SYZOS_API_NESTED_CREATE_VM) {
guest_handle_nested_create_vm((struct api_call_1*)cmd, cpu);
} else if (call == SYZOS_API_NESTED_LOAD_CODE) {
guest_handle_nested_load_code((struct api_call_nested_load_code*)cmd, cpu);
} else if (call == SYZOS_API_NESTED_LOAD_SYZOS) {
guest_handle_nested_load_syzos((struct api_call_nested_load_syzos*)cmd, cpu);
} else if (call == SYZOS_API_NESTED_VMLAUNCH) {
guest_handle_nested_vmlaunch((struct api_call_1*)cmd, cpu);
} else if (call == SYZOS_API_NESTED_VMRESUME) {
guest_handle_nested_vmresume((struct api_call_1*)cmd, cpu);
} else if (call == SYZOS_API_NESTED_INTEL_VMWRITE_MASK) {
guest_handle_nested_intel_vmwrite_mask((struct api_call_5*)cmd, cpu);
} else if (call == SYZOS_API_NESTED_AMD_VMCB_WRITE_MASK) {
guest_handle_nested_amd_vmcb_write_mask((struct api_call_5*)cmd, cpu);
} else if (call == SYZOS_API_NESTED_AMD_INVLPGA) {
guest_handle_nested_amd_invlpga((struct api_call_2*)cmd, cpu);
} else if (call == SYZOS_API_NESTED_AMD_STGI) {
guest_handle_nested_amd_stgi();
} else if (call == SYZOS_API_NESTED_AMD_CLGI) {
guest_handle_nested_amd_clgi();
} else if (call == SYZOS_API_NESTED_AMD_INJECT_EVENT) {
guest_handle_nested_amd_inject_event((struct api_call_5*)cmd, cpu);
} else if (call == SYZOS_API_NESTED_AMD_SET_INTERCEPT) {
guest_handle_nested_amd_set_intercept((struct api_call_5*)cmd, cpu);
} else if (call == SYZOS_API_NESTED_AMD_VMLOAD) {
guest_handle_nested_amd_vmload((struct api_call_1*)cmd, cpu);
} else if (call == SYZOS_API_NESTED_AMD_VMSAVE) {
guest_handle_nested_amd_vmsave((struct api_call_1*)cmd, cpu);
}
addr += cmd->size;
size -= cmd->size;
};
guest_uexit(UEXIT_END);
}
GUEST_CODE static noinline void guest_execute_code(uint8_t* insns, uint64_t size)
{
volatile void (*fn)() = (volatile void (*)())insns;
fn();
}
__attribute__((used))
GUEST_CODE static noinline void
guest_uexit(uint64_t exit_code)
{
volatile uint64_t* ptr = (volatile uint64_t*)X86_SYZOS_ADDR_UEXIT;
asm volatile("movq %0, (%1)" ::"a"(exit_code), "r"(ptr) : "memory");
}
GUEST_CODE static noinline void guest_handle_cpuid(uint32_t eax, uint32_t ecx)
{
asm volatile(
"cpuid\n"
:
: "a"(eax), "c"(ecx)
: "rbx", "rdx");
}
GUEST_CODE static noinline void wrmsr(uint64_t reg, uint64_t val)
{
asm volatile(
"wrmsr"
:
: "c"(reg),
"a"((uint32_t)val),
"d"((uint32_t)(val >> 32))
: "memory");
}
GUEST_CODE static noinline void guest_handle_wrmsr(uint64_t reg, uint64_t val)
{
wrmsr(reg, val);
}
GUEST_CODE static noinline uint64_t rdmsr(uint64_t msr_id)
{
uint32_t low = 0, high = 0;
asm volatile("rdmsr" : "=a"(low), "=d"(high) : "c"(msr_id));
return ((uint64_t)high << 32) | low;
}
GUEST_CODE static noinline void guest_handle_rdmsr(uint64_t reg)
{
(void)rdmsr(reg);
}
GUEST_CODE static noinline void guest_handle_wr_crn(struct api_call_2* cmd)
{
uint64_t value = cmd->args[1];
volatile uint64_t reg = cmd->args[0];
if (reg == 0) {
asm volatile("movq %0, %%cr0" ::"r"(value) : "memory");
return;
}
if (reg == 2) {
asm volatile("movq %0, %%cr2" ::"r"(value) : "memory");
return;
}
if (reg == 3) {
asm volatile("movq %0, %%cr3" ::"r"(value) : "memory");
return;
}
if (reg == 4) {
asm volatile("movq %0, %%cr4" ::"r"(value) : "memory");
return;
}
if (reg == 8) {
asm volatile("movq %0, %%cr8" ::"r"(value) : "memory");
return;
}
}
GUEST_CODE static noinline void guest_handle_wr_drn(struct api_call_2* cmd)
{
uint64_t value = cmd->args[1];
volatile uint64_t reg = cmd->args[0];
if (reg == 0) {
asm volatile("movq %0, %%dr0" ::"r"(value) : "memory");
return;
}
if (reg == 1) {
asm volatile("movq %0, %%dr1" ::"r"(value) : "memory");
return;
}
if (reg == 2) {
asm volatile("movq %0, %%dr2" ::"r"(value) : "memory");
return;
}
if (reg == 3) {
asm volatile("movq %0, %%dr3" ::"r"(value) : "memory");
return;
}
if (reg == 4) {
asm volatile("movq %0, %%dr4" ::"r"(value) : "memory");
return;
}
if (reg == 5) {
asm volatile("movq %0, %%dr5" ::"r"(value) : "memory");
return;
}
if (reg == 6) {
asm volatile("movq %0, %%dr6" ::"r"(value) : "memory");
return;
}
if (reg == 7) {
asm volatile("movq %0, %%dr7" ::"r"(value) : "memory");
return;
}
}
GUEST_CODE static noinline void guest_handle_in_dx(struct api_call_2* cmd)
{
uint16_t port = cmd->args[0];
volatile int size = cmd->args[1];
if (size == 1) {
uint8_t unused;
asm volatile("inb %1, %0" : "=a"(unused) : "d"(port));
return;
}
if (size == 2) {
uint16_t unused;
asm volatile("inw %1, %0" : "=a"(unused) : "d"(port));
return;
}
if (size == 4) {
uint32_t unused;
asm volatile("inl %1, %0" : "=a"(unused) : "d"(port));
}
return;
}
GUEST_CODE static noinline void guest_handle_out_dx(struct api_call_3* cmd)
{
uint16_t port = cmd->args[0];
volatile int size = cmd->args[1];
uint32_t data = (uint32_t)cmd->args[2];
if (size == 1) {
asm volatile("outb %b0, %w1" ::"a"(data), "d"(port));
return;
}
if (size == 2) {
asm volatile("outw %w0, %w1" ::"a"(data), "d"(port));
return;
}
if (size == 4) {
asm volatile("outl %k0, %w1" ::"a"(data), "d"(port));
return;
}
}
struct idt_entry_64 {
uint16_t offset_low;
uint16_t selector;
uint8_t ist;
uint8_t type_attr;
uint16_t offset_mid;
uint32_t offset_high;
uint32_t reserved;
} __attribute__((packed));
GUEST_CODE static void set_idt_gate(uint8_t vector, uint64_t handler)
{
volatile struct idt_entry_64* idt =
(volatile struct idt_entry_64*)(X86_SYZOS_ADDR_VAR_IDT);
volatile struct idt_entry_64* idt_entry = &idt[vector];
idt_entry->offset_low = (uint16_t)handler;
idt_entry->offset_mid = (uint16_t)(handler >> 16);
idt_entry->offset_high = (uint32_t)(handler >> 32);
idt_entry->selector = X86_SYZOS_SEL_CODE;
idt_entry->type_attr = 0x8E;
idt_entry->ist = 0;
idt_entry->reserved = 0;
}
GUEST_CODE static noinline void guest_handle_set_irq_handler(struct api_call_2* cmd)
{
uint8_t vector = (uint8_t)cmd->args[0];
uint64_t type = cmd->args[1];
volatile uint64_t handler_addr = 0;
if (type == 1)
handler_addr = executor_fn_guest_addr(dummy_null_handler);
else if (type == 2)
handler_addr = executor_fn_guest_addr(uexit_irq_handler);
set_idt_gate(vector, handler_addr);
}
GUEST_CODE static cpu_vendor_id get_cpu_vendor(void)
{
uint32_t ebx, eax = 0;
asm volatile(
"cpuid"
: "+a"(eax), "=b"(ebx)
:
: "ecx", "edx");
if (ebx == 0x756e6547) {
return CPU_VENDOR_INTEL;
} else if (ebx == 0x68747541) {
return CPU_VENDOR_AMD;
} else {
guest_uexit(UEXIT_ASSERT);
return CPU_VENDOR_INTEL;
}
}
GUEST_CODE static inline uint64_t read_cr0(void)
{
uint64_t val;
asm volatile("mov %%cr0, %0" : "=r"(val));
return val;
}
GUEST_CODE static inline uint64_t read_cr3(void)
{
uint64_t val;
asm volatile("mov %%cr3, %0" : "=r"(val));
return val;
}
GUEST_CODE static inline uint64_t read_cr4(void)
{
uint64_t val;
asm volatile("mov %%cr4, %0" : "=r"(val));
return val;
}
GUEST_CODE static inline void write_cr4(uint64_t val)
{
asm volatile("mov %0, %%cr4" : : "r"(val));
}
GUEST_CODE static noinline void vmwrite(uint64_t field, uint64_t value)
{
uint8_t error = 0;
asm volatile("vmwrite %%rax, %%rbx; setna %0"
: "=q"(error)
: "a"(value), "b"(field)
: "cc", "memory");
if (error)
guest_uexit(UEXIT_ASSERT);
}
GUEST_CODE static noinline uint64_t vmread(uint64_t field)
{
uint64_t value;
asm volatile("vmread %%rbx, %%rax"
: "=a"(value)
: "b"(field)
: "cc");
return value;
}
GUEST_CODE static inline void nested_vmptrld(uint64_t cpu_id, uint64_t vm_id)
{
uint64_t vmcs_addr = X86_SYZOS_ADDR_VMCS_VMCB(cpu_id, vm_id);
uint8_t error = 0;
asm volatile("vmptrld %1; setna %0"
: "=q"(error)
: "m"(vmcs_addr)
: "memory", "cc");
if (error)
guest_uexit(0xE2BAD2);
}
GUEST_CODE static noinline void vmcb_write16(uint64_t vmcb, uint16_t offset, uint16_t val)
{
*((volatile uint16_t*)(vmcb + offset)) = val;
}
GUEST_CODE static noinline void vmcb_write32(uint64_t vmcb, uint16_t offset, uint32_t val)
{
*((volatile uint32_t*)(vmcb + offset)) = val;
}
GUEST_CODE static noinline uint32_t vmcb_read32(uint64_t vmcb, uint16_t offset)
{
return *((volatile uint32_t*)(vmcb + offset));
}
GUEST_CODE static noinline void vmcb_write64(uint64_t vmcb, uint16_t offset, uint64_t val)
{
*((volatile uint64_t*)(vmcb + offset)) = val;
}
GUEST_CODE static noinline uint64_t vmcb_read64(volatile uint8_t* vmcb, uint16_t offset)
{
return *((volatile uint64_t*)(vmcb + offset));
}
GUEST_CODE static void guest_memset(void* s, uint8_t c, int size)
{
volatile uint8_t* p = (volatile uint8_t*)s;
for (int i = 0; i < size; i++)
p[i] = c;
}
GUEST_CODE static void guest_memcpy(void* dst, void* src, int size)
{
volatile uint8_t* d = (volatile uint8_t*)dst;
volatile uint8_t* s = (volatile uint8_t*)src;
for (int i = 0; i < size; i++)
d[i] = s[i];
}
GUEST_CODE static noinline void
nested_enable_vmx_intel(uint64_t cpu_id)
{
uint64_t vmxon_addr = X86_SYZOS_ADDR_VM_ARCH_SPECIFIC(cpu_id);
uint64_t cr4 = read_cr4();
cr4 |= X86_CR4_VMXE;
write_cr4(cr4);
uint64_t feature_control = rdmsr(X86_MSR_IA32_FEATURE_CONTROL);
if ((feature_control & 1) == 0) {
feature_control |= 0b101;
asm volatile("wrmsr" : : "d"(0x0), "c"(X86_MSR_IA32_FEATURE_CONTROL), "A"(feature_control));
}
*(uint32_t*)vmxon_addr = rdmsr(X86_MSR_IA32_VMX_BASIC);
uint8_t error;
asm volatile("vmxon %1; setna %0"
: "=q"(error)
: "m"(vmxon_addr)
: "memory", "cc");
if (error) {
guest_uexit(0xE2BAD0);
return;
}
}
GUEST_CODE static noinline void
nested_enable_svm_amd(uint64_t cpu_id)
{
uint64_t hsave_addr = X86_SYZOS_ADDR_VM_ARCH_SPECIFIC(cpu_id);
uint64_t efer = rdmsr(X86_MSR_IA32_EFER);
efer |= X86_EFER_SVME;
wrmsr(X86_MSR_IA32_EFER, efer);
wrmsr(X86_MSR_VM_HSAVE_PA, hsave_addr);
}
GUEST_CODE static noinline void
guest_handle_enable_nested(struct api_call_1* cmd, uint64_t cpu_id)
{
if (get_cpu_vendor() == CPU_VENDOR_INTEL) {
nested_enable_vmx_intel(cpu_id);
} else {
nested_enable_svm_amd(cpu_id);
}
}
GUEST_CODE static uint64_t get_unused_memory_size()
{
volatile struct syzos_boot_args* args = (volatile struct syzos_boot_args*)X86_SYZOS_ADDR_BOOT_ARGS;
for (uint32_t i = 0; i < args->region_count; i++) {
if (args->regions[i].gpa == X86_SYZOS_ADDR_UNUSED)
return args->regions[i].pages * KVM_PAGE_SIZE;
}
return 0;
}
GUEST_CODE static uint64_t guest_alloc_page()
{
volatile struct syzos_globals* globals = (volatile struct syzos_globals*)X86_SYZOS_ADDR_GLOBALS;
if (globals->total_size == 0) {
uint64_t size = get_unused_memory_size();
__sync_val_compare_and_swap(&globals->total_size, 0, size);
}
uint64_t offset = __sync_fetch_and_add(&globals->alloc_offset, KVM_PAGE_SIZE);
if (offset >= globals->total_size)
guest_uexit(UEXIT_ASSERT);
uint64_t ptr = X86_SYZOS_ADDR_UNUSED + offset;
guest_memset((void*)ptr, 0, KVM_PAGE_SIZE);
return ptr;
}
GUEST_CODE static void l2_map_page(uint64_t cpu_id, uint64_t vm_id, uint64_t gpa, uint64_t host_pa, uint64_t flags)
{
uint64_t pml4_addr = X86_SYZOS_ADDR_VM_PGTABLE(cpu_id, vm_id);
volatile uint64_t* pml4 = (volatile uint64_t*)pml4_addr;
uint64_t pml4_idx = (gpa >> 39) & 0x1FF;
if (!(pml4[pml4_idx] & X86_PDE64_PRESENT)) {
uint64_t page = guest_alloc_page();
pml4[pml4_idx] = page | X86_PDE64_PRESENT | X86_PDE64_RW | X86_PDE64_USER;
}
volatile uint64_t* pdpt = (volatile uint64_t*)(pml4[pml4_idx] & ~0xFFF);
uint64_t pdpt_idx = (gpa >> 30) & 0x1FF;
if (!(pdpt[pdpt_idx] & X86_PDE64_PRESENT)) {
uint64_t page = guest_alloc_page();
pdpt[pdpt_idx] = page | X86_PDE64_PRESENT | X86_PDE64_RW | X86_PDE64_USER;
}
volatile uint64_t* pd = (volatile uint64_t*)(pdpt[pdpt_idx] & ~0xFFF);
uint64_t pd_idx = (gpa >> 21) & 0x1FF;
if (!(pd[pd_idx] & X86_PDE64_PRESENT)) {
uint64_t page = guest_alloc_page();
pd[pd_idx] = page | X86_PDE64_PRESENT | X86_PDE64_RW | X86_PDE64_USER;
}
volatile uint64_t* pt = (volatile uint64_t*)(pd[pd_idx] & ~0xFFF);
uint64_t pt_idx = (gpa >> 12) & 0x1FF;
if (!(pt[pt_idx] & X86_PDE64_PRESENT))
pt[pt_idx] = (host_pa & ~0xFFF) | flags;
}
GUEST_CODE static noinline void setup_l2_page_tables(cpu_vendor_id vendor, uint64_t cpu_id, uint64_t vm_id, uint64_t unused_pages)
{
uint64_t flags = X86_PDE64_PRESENT | X86_PDE64_RW | X86_PDE64_USER;
if (vendor == CPU_VENDOR_INTEL) {
flags |= EPT_MEMTYPE_WB | EPT_ACCESSED | EPT_DIRTY;
} else {
flags |= X86_PDE64_ACCESSED | X86_PDE64_DIRTY;
}
volatile struct syzos_boot_args* args = (volatile struct syzos_boot_args*)X86_SYZOS_ADDR_BOOT_ARGS;
for (uint32_t i = 0; i < args->region_count; i++) {
struct mem_region r;
r.gpa = args->regions[i].gpa;
r.pages = args->regions[i].pages;
r.flags = args->regions[i].flags;
if (r.flags & MEM_REGION_FLAG_NO_HOST_MEM)
continue;
if (r.flags & MEM_REGION_FLAG_REMAINING) {
r.pages = (unused_pages < 16) ? 16 : unused_pages;
}
for (int p = 0; p < r.pages; p++) {
uint64_t gpa = r.gpa + (p * KVM_PAGE_SIZE);
uint64_t backing;
if (r.gpa == X86_SYZOS_ADDR_USER_CODE && p == 0) {
backing = X86_SYZOS_ADDR_VM_CODE(cpu_id, vm_id);
} else if (r.gpa == X86_SYZOS_ADDR_STACK_BOTTOM) {
backing = X86_SYZOS_ADDR_VM_STACK(cpu_id, vm_id);
} else {
backing = gpa;
}
l2_map_page(cpu_id, vm_id, gpa, backing, flags);
}
}
}
GUEST_CODE static noinline void init_vmcs_control_fields(uint64_t cpu_id, uint64_t vm_id)
{
uint64_t vmx_msr = rdmsr(X86_MSR_IA32_VMX_TRUE_PINBASED_CTLS);
vmwrite(VMCS_PIN_BASED_VM_EXEC_CONTROL, (uint32_t)vmx_msr);
vmx_msr = (uint32_t)rdmsr(X86_MSR_IA32_VMX_PROCBASED_CTLS2);
vmx_msr |= SECONDARY_EXEC_ENABLE_EPT | SECONDARY_EXEC_ENABLE_RDTSCP;
vmwrite(VMCS_SECONDARY_VM_EXEC_CONTROL, vmx_msr);
vmx_msr = rdmsr(X86_MSR_IA32_VMX_TRUE_PROCBASED_CTLS);
vmx_msr |= CPU_BASED_ACTIVATE_SECONDARY_CONTROLS;
vmx_msr |= CPU_BASED_HLT_EXITING | CPU_BASED_RDTSC_EXITING;
vmwrite(VMCS_CPU_BASED_VM_EXEC_CONTROL, (uint32_t)vmx_msr);
vmx_msr = rdmsr(X86_MSR_IA32_VMX_TRUE_EXIT_CTLS);
vmwrite(VMCS_VM_EXIT_CONTROLS, (uint32_t)vmx_msr | VM_EXIT_HOST_ADDR_SPACE_SIZE);
vmx_msr = rdmsr(X86_MSR_IA32_VMX_TRUE_ENTRY_CTLS);
vmwrite(VMCS_VM_ENTRY_CONTROLS, (uint32_t)vmx_msr | VM_ENTRY_IA32E_MODE);
uint64_t eptp = (X86_SYZOS_ADDR_VM_PGTABLE(cpu_id, vm_id) & ~0xFFF) | (6 << 0) | (3 << 3);
vmwrite(VMCS_EPT_POINTER, eptp);
vmwrite(VMCS_CR0_GUEST_HOST_MASK, 0);
vmwrite(VMCS_CR4_GUEST_HOST_MASK, 0);
vmwrite(VMCS_CR0_READ_SHADOW, read_cr0());
vmwrite(VMCS_CR4_READ_SHADOW, read_cr4());
vmwrite(VMCS_MSR_BITMAP, 0);
vmwrite(VMCS_VMREAD_BITMAP, 0);
vmwrite(VMCS_VMWRITE_BITMAP, 0);
vmwrite(VMCS_EXCEPTION_BITMAP, (1 << 6));
vmwrite(VMCS_VIRTUAL_PROCESSOR_ID, 0);
vmwrite(VMCS_POSTED_INTR_NV, 0);
vmwrite(VMCS_PAGE_FAULT_ERROR_CODE_MASK, 0);
vmwrite(VMCS_PAGE_FAULT_ERROR_CODE_MATCH, -1);
vmwrite(VMCS_CR3_TARGET_COUNT, 0);
vmwrite(VMCS_VM_EXIT_MSR_STORE_COUNT, 0);
vmwrite(VMCS_VM_EXIT_MSR_LOAD_COUNT, 0);
vmwrite(VMCS_VM_ENTRY_MSR_LOAD_COUNT, 0);
vmwrite(VMCS_VM_ENTRY_INTR_INFO_FIELD, 0);
vmwrite(VMCS_TPR_THRESHOLD, 0);
}
typedef enum {
SYZOS_NESTED_EXIT_REASON_HLT = 1,
SYZOS_NESTED_EXIT_REASON_INVD = 2,
SYZOS_NESTED_EXIT_REASON_CPUID = 3,
SYZOS_NESTED_EXIT_REASON_RDTSC = 4,
SYZOS_NESTED_EXIT_REASON_RDTSCP = 5,
SYZOS_NESTED_EXIT_REASON_EPT_VIOLATION = 6,
SYZOS_NESTED_EXIT_REASON_UNKNOWN = 0xFF,
} syz_nested_exit_reason;
GUEST_CODE static void handle_nested_uexit(uint64_t exit_code)
{
uint64_t level = (exit_code >> 56) + 1;
exit_code = (exit_code & 0x00FFFFFFFFFFFFFFULL) | (level << 56);
guest_uexit(exit_code);
}
GUEST_CODE static void guest_uexit_l2(uint64_t exit_reason, syz_nested_exit_reason mapped_reason,
cpu_vendor_id vendor)
{
if (mapped_reason != SYZOS_NESTED_EXIT_REASON_UNKNOWN) {
guest_uexit(0xe2e20000 | mapped_reason);
} else if (vendor == CPU_VENDOR_INTEL) {
guest_uexit(0xe2110000 | exit_reason);
} else {
guest_uexit(0xe2aa0000 | exit_reason);
}
}
#define EXIT_REASON_CPUID 0xa
#define EXIT_REASON_HLT 0xc
#define EXIT_REASON_INVD 0xd
#define EXIT_REASON_EPT_VIOLATION 0x30
#define EXIT_REASON_RDTSC 0x10
#define EXIT_REASON_RDTSCP 0x33
GUEST_CODE static syz_nested_exit_reason map_intel_exit_reason(uint64_t basic_reason)
{
volatile uint64_t reason = basic_reason;
if (reason == EXIT_REASON_HLT)
return SYZOS_NESTED_EXIT_REASON_HLT;
if (reason == EXIT_REASON_INVD)
return SYZOS_NESTED_EXIT_REASON_INVD;
if (reason == EXIT_REASON_CPUID)
return SYZOS_NESTED_EXIT_REASON_CPUID;
if (reason == EXIT_REASON_RDTSC)
return SYZOS_NESTED_EXIT_REASON_RDTSC;
if (reason == EXIT_REASON_RDTSCP)
return SYZOS_NESTED_EXIT_REASON_RDTSCP;
if (reason == EXIT_REASON_EPT_VIOLATION)
return SYZOS_NESTED_EXIT_REASON_EPT_VIOLATION;
return SYZOS_NESTED_EXIT_REASON_UNKNOWN;
}
GUEST_CODE static void advance_l2_rip_intel(uint64_t basic_reason)
{
volatile uint64_t reason = basic_reason;
uint64_t rip = vmread(VMCS_GUEST_RIP);
if ((reason == EXIT_REASON_INVD) || (reason == EXIT_REASON_CPUID) ||
(reason == EXIT_REASON_RDTSC)) {
rip += 2;
} else if (reason == EXIT_REASON_RDTSCP) {
rip += 3;
}
vmwrite(VMCS_GUEST_RIP, rip);
}
__attribute__((used))
GUEST_CODE static void
nested_vm_exit_handler_intel(uint64_t exit_reason, struct l2_guest_regs* regs)
{
volatile struct syzos_globals* globals = (volatile struct syzos_globals*)X86_SYZOS_ADDR_GLOBALS;
uint64_t cpu_id = *(uint64_t*)((char*)regs + sizeof(struct l2_guest_regs) + 7 * 8);
uint64_t vm_id = globals->active_vm_id[cpu_id];
guest_memcpy((void*)&globals->l2_ctx[cpu_id][vm_id], regs, sizeof(struct l2_guest_regs));
uint64_t basic_reason = exit_reason & 0xFFFF;
if (basic_reason == EXIT_REASON_EPT_VIOLATION) {
uint64_t gpa = vmread(VMCS_GUEST_PHYSICAL_ADDRESS);
if ((gpa & ~0xFFF) == X86_SYZOS_ADDR_EXIT) {
handle_nested_uexit(regs->rax);
vmwrite(VMCS_GUEST_RIP, vmread(VMCS_GUEST_RIP) + 3);
return;
}
}
syz_nested_exit_reason mapped_reason = map_intel_exit_reason(basic_reason);
guest_uexit_l2(exit_reason, mapped_reason, CPU_VENDOR_INTEL);
advance_l2_rip_intel(basic_reason);
}
extern char after_vmentry_label;
__attribute__((naked)) GUEST_CODE static void nested_vm_exit_handler_intel_asm(void)
{
asm volatile(R"... [truncated large byte array] ..."
: : [l2_regs_size] "i"(sizeof(struct l2_guest_regs)),
[vm_exit_reason] "i"(VMCS_VM_EXIT_REASON) : "memory", "cc", "rbx", "rdi", "rsi");
}
#define VMEXIT_RDTSC 0x6e
#define VMEXIT_CPUID 0x72
#define VMEXIT_INVD 0x76
#define VMEXIT_HLT 0x78
#define VMEXIT_NPF 0x400
#define VMEXIT_RDTSCP 0x87
GUEST_CODE static syz_nested_exit_reason map_amd_exit_reason(uint64_t basic_reason)
{
volatile uint64_t reason = basic_reason;
if (reason == VMEXIT_HLT)
return SYZOS_NESTED_EXIT_REASON_HLT;
if (reason == VMEXIT_INVD)
return SYZOS_NESTED_EXIT_REASON_INVD;
if (reason == VMEXIT_CPUID)
return SYZOS_NESTED_EXIT_REASON_CPUID;
if (reason == VMEXIT_RDTSC)
return SYZOS_NESTED_EXIT_REASON_RDTSC;
if (reason == VMEXIT_RDTSCP)
return SYZOS_NESTED_EXIT_REASON_RDTSCP;
if (reason == VMEXIT_NPF)
return SYZOS_NESTED_EXIT_REASON_EPT_VIOLATION;
return SYZOS_NESTED_EXIT_REASON_UNKNOWN;
}
GUEST_CODE static void advance_l2_rip_amd(uint64_t basic_reason, uint64_t cpu_id, uint64_t vm_id)
{
volatile uint64_t reason = basic_reason;
uint64_t vmcb_addr = X86_SYZOS_ADDR_VMCS_VMCB(cpu_id, vm_id);
uint64_t rip = vmcb_read64((volatile uint8_t*)vmcb_addr, VMCB_GUEST_RIP);
if ((reason == VMEXIT_INVD) || (reason == VMEXIT_CPUID) ||
(reason == VMEXIT_RDTSC)) {
rip += 2;
} else if (reason == VMEXIT_RDTSCP) {
rip += 3;
}
vmcb_write64(vmcb_addr, VMCB_GUEST_RIP, rip);
}
__attribute__((used)) GUEST_CODE static void
nested_vm_exit_handler_amd(uint64_t exit_reason, struct l2_guest_regs* regs)
{
volatile struct syzos_globals* globals = (volatile struct syzos_globals*)X86_SYZOS_ADDR_GLOBALS;
uint64_t cpu_id = *(uint64_t*)((char*)regs + sizeof(struct l2_guest_regs) + 8 * 8);
uint64_t vm_id = globals->active_vm_id[cpu_id];
guest_memcpy((void*)&globals->l2_ctx[cpu_id][vm_id], regs, sizeof(struct l2_guest_regs));
volatile uint64_t basic_reason = exit_reason & 0xFFFF;
if (basic_reason == VMEXIT_NPF) {
uint64_t vmcb_addr = X86_SYZOS_ADDR_VMCS_VMCB(cpu_id, vm_id);
uint64_t fault_gpa = vmcb_read64((volatile uint8_t*)vmcb_addr, VMCB_EXITINFO2);
if ((fault_gpa & ~0xFFF) == X86_SYZOS_ADDR_EXIT) {
handle_nested_uexit(regs->rax);
uint64_t rip = vmcb_read64((volatile uint8_t*)vmcb_addr, VMCB_GUEST_RIP);
vmcb_write64(vmcb_addr, VMCB_GUEST_RIP, rip + 3);
return;
}
}
syz_nested_exit_reason mapped_reason = map_amd_exit_reason(basic_reason);
guest_uexit_l2(exit_reason, mapped_reason, CPU_VENDOR_AMD);
advance_l2_rip_amd(basic_reason, cpu_id, vm_id);
}
GUEST_CODE static noinline void init_vmcs_host_state(void)
{
vmwrite(VMCS_HOST_CS_SELECTOR, X86_SYZOS_SEL_CODE);
vmwrite(VMCS_HOST_DS_SELECTOR, X86_SYZOS_SEL_DATA);
vmwrite(VMCS_HOST_ES_SELECTOR, X86_SYZOS_SEL_DATA);
vmwrite(VMCS_HOST_SS_SELECTOR, X86_SYZOS_SEL_DATA);
vmwrite(VMCS_HOST_FS_SELECTOR, X86_SYZOS_SEL_DATA);
vmwrite(VMCS_HOST_GS_SELECTOR, X86_SYZOS_SEL_DATA);
vmwrite(VMCS_HOST_TR_SELECTOR, X86_SYZOS_SEL_TSS64);
vmwrite(VMCS_HOST_TR_BASE, X86_SYZOS_ADDR_VAR_TSS);
vmwrite(VMCS_HOST_GDTR_BASE, X86_SYZOS_ADDR_GDT);
vmwrite(VMCS_HOST_IDTR_BASE, X86_SYZOS_ADDR_VAR_IDT);
vmwrite(VMCS_HOST_FS_BASE, rdmsr(X86_MSR_FS_BASE));
vmwrite(VMCS_HOST_GS_BASE, rdmsr(X86_MSR_GS_BASE));
vmwrite(VMCS_HOST_RIP, (uintptr_t)nested_vm_exit_handler_intel_asm);
vmwrite(VMCS_HOST_CR0, read_cr0());
vmwrite(VMCS_HOST_CR3, read_cr3());
vmwrite(VMCS_HOST_CR4, read_cr4());
vmwrite(VMCS_HOST_IA32_PAT, rdmsr(X86_MSR_IA32_CR_PAT));
vmwrite(VMCS_HOST_IA32_EFER, rdmsr(X86_MSR_IA32_EFER));
vmwrite(VMCS_HOST_IA32_PERF_GLOBAL_CTRL, rdmsr(X86_MSR_CORE_PERF_GLOBAL_CTRL));
vmwrite(VMCS_HOST_IA32_SYSENTER_CS, rdmsr(X86_MSR_IA32_SYSENTER_CS));
vmwrite(VMCS_HOST_IA32_SYSENTER_ESP, rdmsr(X86_MSR_IA32_SYSENTER_ESP));
vmwrite(VMCS_HOST_IA32_SYSENTER_EIP, rdmsr(X86_MSR_IA32_SYSENTER_EIP));
}
#define COPY_VMCS_FIELD(GUEST_FIELD,HOST_FIELD) vmwrite(GUEST_FIELD, vmread(HOST_FIELD))
#define SETUP_L2_SEGMENT(SEG,SELECTOR,BASE,LIMIT,AR) vmwrite(VMCS_GUEST_ ##SEG ##_SELECTOR, SELECTOR); vmwrite(VMCS_GUEST_ ##SEG ##_BASE, BASE); vmwrite(VMCS_GUEST_ ##SEG ##_LIMIT, LIMIT); vmwrite(VMCS_GUEST_ ##SEG ##_ACCESS_RIGHTS, AR);
GUEST_CODE static noinline void init_vmcs_guest_state(uint64_t cpu_id, uint64_t vm_id)
{
uint64_t l2_code_addr = X86_SYZOS_ADDR_VM_CODE(cpu_id, vm_id);
uint64_t l2_stack_addr = X86_SYZOS_ADDR_VM_STACK(cpu_id, vm_id);
SETUP_L2_SEGMENT(CS, vmread(VMCS_HOST_CS_SELECTOR), 0, 0xFFFFFFFF, VMX_AR_64BIT_CODE);
SETUP_L2_SEGMENT(DS, vmread(VMCS_HOST_DS_SELECTOR), 0, 0xFFFFFFFF, VMX_AR_64BIT_DATA_STACK);
SETUP_L2_SEGMENT(ES, vmread(VMCS_HOST_ES_SELECTOR), 0, 0xFFFFFFFF, VMX_AR_64BIT_DATA_STACK);
SETUP_L2_SEGMENT(SS, vmread(VMCS_HOST_SS_SELECTOR), 0, 0xFFFFFFFF, VMX_AR_64BIT_DATA_STACK);
SETUP_L2_SEGMENT(FS, vmread(VMCS_HOST_FS_SELECTOR), vmread(VMCS_HOST_FS_BASE), 0xFFFFFFFF, VMX_AR_64BIT_DATA_STACK);
SETUP_L2_SEGMENT(GS, vmread(VMCS_HOST_GS_SELECTOR), vmread(VMCS_HOST_GS_BASE), 0xFFFFFFFF, VMX_AR_64BIT_DATA_STACK);
SETUP_L2_SEGMENT(TR, vmread(VMCS_HOST_TR_SELECTOR), vmread(VMCS_HOST_TR_BASE), 0x67, VMX_AR_TSS_BUSY);
SETUP_L2_SEGMENT(LDTR, 0, 0, 0, VMX_AR_LDTR_UNUSABLE);
vmwrite(VMCS_GUEST_CR0, vmread(VMCS_HOST_CR0));
vmwrite(VMCS_GUEST_CR3, vmread(VMCS_HOST_CR3));
vmwrite(VMCS_GUEST_CR4, vmread(VMCS_HOST_CR4));
vmwrite(VMCS_GUEST_RIP, l2_code_addr);
vmwrite(VMCS_GUEST_RSP, l2_stack_addr + KVM_PAGE_SIZE - 8);
vmwrite(VMCS_GUEST_RFLAGS, RFLAGS_1_BIT);
vmwrite(VMCS_GUEST_DR7, 0x400);
COPY_VMCS_FIELD(VMCS_GUEST_IA32_EFER, VMCS_HOST_IA32_EFER);
COPY_VMCS_FIELD(VMCS_GUEST_IA32_PAT, VMCS_HOST_IA32_PAT);
COPY_VMCS_FIELD(VMCS_GUEST_IA32_PERF_GLOBAL_CTRL, VMCS_HOST_IA32_PERF_GLOBAL_CTRL);
COPY_VMCS_FIELD(VMCS_GUEST_SYSENTER_CS, VMCS_HOST_IA32_SYSENTER_CS);
COPY_VMCS_FIELD(VMCS_GUEST_SYSENTER_ESP, VMCS_HOST_IA32_SYSENTER_ESP);
COPY_VMCS_FIELD(VMCS_GUEST_SYSENTER_EIP, VMCS_HOST_IA32_SYSENTER_EIP);
vmwrite(VMCS_GUEST_IA32_DEBUGCTL, 0);
vmwrite(VMCS_GUEST_GDTR_BASE, vmread(VMCS_HOST_GDTR_BASE));
vmwrite(VMCS_GUEST_GDTR_LIMIT, 0xffff);
vmwrite(VMCS_GUEST_IDTR_BASE, vmread(VMCS_HOST_IDTR_BASE));
vmwrite(VMCS_GUEST_IDTR_LIMIT, 0xffff);
vmwrite(VMCS_LINK_POINTER, 0xffffffffffffffff);
vmwrite(VMCS_GUEST_ACTIVITY_STATE, 0);
vmwrite(VMCS_GUEST_INTERRUPTIBILITY_INFO, 0);
vmwrite(VMCS_GUEST_PENDING_DBG_EXCEPTIONS, 0);
vmwrite(VMCS_VMX_PREEMPTION_TIMER_VALUE, 0);
vmwrite(VMCS_GUEST_INTR_STATUS, 0);
vmwrite(VMCS_GUEST_PML_INDEX, 0);
}
GUEST_CODE static noinline void
nested_create_vm_intel(struct api_call_1* cmd, uint64_t cpu_id)
{
uint64_t vm_id = cmd->arg;
uint64_t vmcs_addr = X86_SYZOS_ADDR_VMCS_VMCB(cpu_id, vm_id);
uint8_t error = 0;
uint64_t l2_pml4_addr = X86_SYZOS_ADDR_VM_PGTABLE(cpu_id, vm_id);
uint64_t l2_msr_bitmap = X86_SYZOS_ADDR_MSR_BITMAP(cpu_id, vm_id);
*(uint32_t*)vmcs_addr = rdmsr(X86_MSR_IA32_VMX_BASIC);
asm volatile("vmclear %1; setna %0"
: "=q"(error)
: "m"(vmcs_addr)
: "memory", "cc");
if (error) {
guest_uexit(0xE2BAD1);
return;
}
nested_vmptrld(cpu_id, vm_id);
guest_memset((void*)l2_pml4_addr, 0, KVM_PAGE_SIZE);
guest_memset((void*)l2_msr_bitmap, 0, KVM_PAGE_SIZE);
setup_l2_page_tables(CPU_VENDOR_INTEL, cpu_id, vm_id, 0);
init_vmcs_control_fields(cpu_id, vm_id);
init_vmcs_host_state();
init_vmcs_guest_state(cpu_id, vm_id);
}
#define SETUP_L2_SEGMENT_SVM(VMBC_PTR,SEG_NAME,SELECTOR,BASE,LIMIT,ATTR) vmcb_write16(VMBC_PTR, VMCB_GUEST_ ##SEG_NAME ##_SEL, SELECTOR); vmcb_write16(VMBC_PTR, VMCB_GUEST_ ##SEG_NAME ##_ATTR, ATTR); vmcb_write32(VMBC_PTR, VMCB_GUEST_ ##SEG_NAME ##_LIM, LIMIT); vmcb_write64(VMBC_PTR, VMCB_GUEST_ ##SEG_NAME ##_BASE, BASE);
GUEST_CODE static noinline void init_vmcb_guest_state(uint64_t cpu_id, uint64_t vm_id)
{
uint64_t vmcb_addr = X86_SYZOS_ADDR_VMCS_VMCB(cpu_id, vm_id);
uint64_t l2_code_addr = X86_SYZOS_ADDR_VM_CODE(cpu_id, vm_id);
uint64_t l2_stack_addr = X86_SYZOS_ADDR_VM_STACK(cpu_id, vm_id);
uint64_t npt_pml4_addr = X86_SYZOS_ADDR_VM_PGTABLE(cpu_id, vm_id);
SETUP_L2_SEGMENT_SVM(vmcb_addr, CS, X86_SYZOS_SEL_CODE, 0, 0xFFFFFFFF, SVM_ATTR_64BIT_CODE);
SETUP_L2_SEGMENT_SVM(vmcb_addr, DS, X86_SYZOS_SEL_DATA, 0, 0xFFFFFFFF, SVM_ATTR_64BIT_DATA);
SETUP_L2_SEGMENT_SVM(vmcb_addr, ES, X86_SYZOS_SEL_DATA, 0, 0xFFFFFFFF, SVM_ATTR_64BIT_DATA);
SETUP_L2_SEGMENT_SVM(vmcb_addr, SS, X86_SYZOS_SEL_DATA, 0, 0xFFFFFFFF, SVM_ATTR_64BIT_DATA);
SETUP_L2_SEGMENT_SVM(vmcb_addr, FS, X86_SYZOS_SEL_DATA, 0, 0xFFFFFFFF, SVM_ATTR_64BIT_DATA);
SETUP_L2_SEGMENT_SVM(vmcb_addr, GS, X86_SYZOS_SEL_DATA, 0, 0xFFFFFFFF, SVM_ATTR_64BIT_DATA);
SETUP_L2_SEGMENT_SVM(vmcb_addr, TR, X86_SYZOS_SEL_TSS64, X86_SYZOS_ADDR_VAR_TSS, 0x67, SVM_ATTR_TSS_BUSY);
SETUP_L2_SEGMENT_SVM(vmcb_addr, LDTR, 0, 0, 0, SVM_ATTR_LDTR_UNUSABLE);
vmcb_write64(vmcb_addr, VMCB_GUEST_CR0, read_cr0() | X86_CR0_WP);
vmcb_write64(vmcb_addr, VMCB_GUEST_CR3, read_cr3());
vmcb_write64(vmcb_addr, VMCB_GUEST_CR4, read_cr4());
vmcb_write64(vmcb_addr, VMCB_GUEST_RIP, l2_code_addr);
vmcb_write64(vmcb_addr, VMCB_GUEST_RSP, l2_stack_addr + KVM_PAGE_SIZE - 8);
vmcb_write64(vmcb_addr, VMCB_GUEST_RFLAGS, RFLAGS_1_BIT);
vmcb_write64(vmcb_addr, VMCB_GUEST_EFER, X86_EFER_LME | X86_EFER_LMA | X86_EFER_SVME);
vmcb_write64(vmcb_addr, VMCB_RAX, 0);
struct {
uint16_t limit;
uint64_t base;
} __attribute__((packed)) gdtr, idtr;
asm volatile("sgdt %0" : "=m"(gdtr));
asm volatile("sidt %0" : "=m"(idtr));
vmcb_write64(vmcb_addr, VMCB_GUEST_GDTR_BASE, gdtr.base);
vmcb_write32(vmcb_addr, VMCB_GUEST_GDTR_LIM, gdtr.limit);
vmcb_write64(vmcb_addr, VMCB_GUEST_IDTR_BASE, idtr.base);
vmcb_write32(vmcb_addr, VMCB_GUEST_IDTR_LIM, idtr.limit);
vmcb_write32(vmcb_addr, VMCB_CTRL_INTERCEPT_VEC3, VMCB_CTRL_INTERCEPT_VEC3_ALL);
vmcb_write32(vmcb_addr, VMCB_CTRL_INTERCEPT_VEC4, VMCB_CTRL_INTERCEPT_VEC4_ALL);
vmcb_write64(vmcb_addr, VMCB_CTRL_NP_ENABLE, (1 << VMCB_CTRL_NPT_ENABLE_BIT));
uint64_t npt_pointer = (npt_pml4_addr & ~0xFFF);
vmcb_write64(vmcb_addr, VMCB_CTRL_N_CR3, npt_pointer);
vmcb_write32(vmcb_addr, VMCB_CTRL_ASID, 1);
}
GUEST_CODE static noinline void
nested_create_vm_amd(struct api_call_1* cmd, uint64_t cpu_id)
{
uint64_t vm_id = cmd->arg;
uint64_t vmcb_addr = X86_SYZOS_ADDR_VMCS_VMCB(cpu_id, vm_id);
uint64_t l2_pml4_addr = X86_SYZOS_ADDR_VM_PGTABLE(cpu_id, vm_id);
uint64_t l2_msr_bitmap = X86_SYZOS_ADDR_MSR_BITMAP(cpu_id, vm_id);
guest_memset((void*)vmcb_addr, 0, KVM_PAGE_SIZE);
guest_memset((void*)X86_SYZOS_ADDR_VM_ARCH_SPECIFIC(cpu_id), 0, KVM_PAGE_SIZE);
guest_memset((void*)l2_pml4_addr, 0, KVM_PAGE_SIZE);
guest_memset((void*)l2_msr_bitmap, 0, KVM_PAGE_SIZE);
setup_l2_page_tables(CPU_VENDOR_AMD, cpu_id, vm_id, 0);
init_vmcb_guest_state(cpu_id, vm_id);
}
GUEST_CODE static noinline void
guest_handle_nested_create_vm(struct api_call_1* cmd, uint64_t cpu_id)
{
if (get_cpu_vendor() == CPU_VENDOR_INTEL) {
nested_create_vm_intel(cmd, cpu_id);
} else {
nested_create_vm_amd(cmd, cpu_id);
}
}
GUEST_CODE static uint64_t l2_gpa_to_pa(uint64_t cpu_id, uint64_t vm_id, uint64_t gpa)
{
uint64_t pml4_addr = X86_SYZOS_ADDR_VM_PGTABLE(cpu_id, vm_id);
volatile uint64_t* pml4 = (volatile uint64_t*)pml4_addr;
uint64_t pml4_idx = (gpa >> 39) & 0x1FF;
if (!(pml4[pml4_idx] & X86_PDE64_PRESENT))
return 0;
volatile uint64_t* pdpt = (volatile uint64_t*)(pml4[pml4_idx] & ~0xFFF);
uint64_t pdpt_idx = (gpa >> 30) & 0x1FF;
if (!(pdpt[pdpt_idx] & X86_PDE64_PRESENT))
return 0;
volatile uint64_t* pd = (volatile uint64_t*)(pdpt[pdpt_idx] & ~0xFFF);
uint64_t pd_idx = (gpa >> 21) & 0x1FF;
if (!(pd[pd_idx] & X86_PDE64_PRESENT))
return 0;
volatile uint64_t* pt = (volatile uint64_t*)(pd[pd_idx] & ~0xFFF);
uint64_t pt_idx = (gpa >> 12) & 0x1FF;
if (!(pt[pt_idx] & X86_PDE64_PRESENT))
return 0;
return (pt[pt_idx] & ~0xFFF) + (gpa & 0xFFF);
}
GUEST_CODE static noinline void
guest_handle_nested_load_code(struct api_call_nested_load_code* cmd, uint64_t cpu_id)
{
uint64_t vm_id = cmd->vm_id;
uint64_t l2_code_backing = l2_gpa_to_pa(cpu_id, vm_id, X86_SYZOS_ADDR_USER_CODE);
if (!l2_code_backing) {
guest_uexit(0xE2BAD4);
return;
}
uint64_t l2_code_size = cmd->header.size - sizeof(struct api_call_header) - sizeof(uint64_t);
if (l2_code_size > KVM_PAGE_SIZE)
l2_code_size = KVM_PAGE_SIZE;
guest_memcpy((void*)l2_code_backing, (void*)cmd->insns,
l2_code_size);
if (get_cpu_vendor() == CPU_VENDOR_INTEL) {
nested_vmptrld(cpu_id, vm_id);
vmwrite(VMCS_GUEST_RIP, X86_SYZOS_ADDR_USER_CODE);
vmwrite(VMCS_GUEST_RSP, X86_SYZOS_ADDR_STACK_BOTTOM + KVM_PAGE_SIZE - 8);
} else {
vmcb_write64(X86_SYZOS_ADDR_VMCS_VMCB(cpu_id, vm_id), VMCB_GUEST_RIP, X86_SYZOS_ADDR_USER_CODE);
vmcb_write64(X86_SYZOS_ADDR_VMCS_VMCB(cpu_id, vm_id), VMCB_GUEST_RSP, X86_SYZOS_ADDR_STACK_BOTTOM + KVM_PAGE_SIZE - 8);
}
}
GUEST_CODE static noinline void
guest_handle_nested_load_syzos(struct api_call_nested_load_syzos* cmd, uint64_t cpu_id)
{
uint64_t vm_id = cmd->vm_id;
uint64_t prog_size = cmd->header.size - __builtin_offsetof(struct api_call_nested_load_syzos, program);
uint64_t l2_code_backing = X86_SYZOS_ADDR_VM_CODE(cpu_id, vm_id);
volatile struct syzos_globals* globals = (volatile struct syzos_globals*)X86_SYZOS_ADDR_GLOBALS;
if (prog_size > KVM_PAGE_SIZE)
prog_size = KVM_PAGE_SIZE;
guest_memcpy((void*)l2_code_backing, (void*)cmd->program, prog_size);
uint64_t globals_pa = l2_gpa_to_pa(cpu_id, vm_id, X86_SYZOS_ADDR_GLOBALS);
if (!globals_pa) {
guest_uexit(0xE2BAD3);
return;
}
volatile struct syzos_globals* l2_globals = (volatile struct syzos_globals*)globals_pa;
for (int i = 0; i < KVM_MAX_VCPU; i++) {
l2_globals->text_sizes[i] = prog_size;
globals->l2_ctx[i][vm_id].rdi = i;
globals->l2_ctx[i][vm_id].rax = 0;
}
uint64_t entry_rip = executor_fn_guest_addr(guest_main);
if (get_cpu_vendor() == CPU_VENDOR_INTEL) {
nested_vmptrld(cpu_id, vm_id);
vmwrite(VMCS_GUEST_RIP, entry_rip);
vmwrite(VMCS_GUEST_RSP, X86_SYZOS_ADDR_STACK_BOTTOM + KVM_PAGE_SIZE - 8);
} else {
uint64_t vmcb = X86_SYZOS_ADDR_VMCS_VMCB(cpu_id, vm_id);
vmcb_write64(vmcb, VMCB_GUEST_RIP, entry_rip);
vmcb_write64(vmcb, VMCB_GUEST_RSP, X86_SYZOS_ADDR_STACK_BOTTOM + KVM_PAGE_SIZE - 8);
}
}
GUEST_CODE static noinline void
guest_handle_nested_vmentry_intel(uint64_t vm_id, uint64_t cpu_id, bool is_launch)
{
volatile struct syzos_globals* globals = (volatile struct syzos_globals*)X86_SYZOS_ADDR_GLOBALS;
struct l2_guest_regs* l2_regs = (struct l2_guest_regs*)&globals->l2_ctx[cpu_id][vm_id];
uint64_t vmx_error_code = 0;
uint64_t fail_flag = 0;
nested_vmptrld(cpu_id, vm_id);
globals->active_vm_id[cpu_id] = vm_id;
asm volatile(R"... [truncated large byte array] ..."
: [ret] "=&r"(fail_flag)
: [launch] "r"((uint64_t)is_launch),
[host_rsp_field] "i"(VMCS_HOST_RSP),
[cpu_id] "r"(cpu_id),
[l2_regs] "r"(l2_regs)
: "cc", "memory", "rax", "rcx", "rdx", "rsi", "rdi", "r8", "r9", "r10", "r11");
if (fail_flag) {
vmx_error_code = vmread(VMCS_VM_INSTRUCTION_ERROR);
guest_uexit(0xE2E10000 | (uint32_t)vmx_error_code);
return;
}
}
GUEST_CODE static noinline void
guest_run_amd_vm(uint64_t cpu_id, uint64_t vm_id)
{
uint64_t vmcb_addr = X86_SYZOS_ADDR_VMCS_VMCB(cpu_id, vm_id);
volatile struct syzos_globals* globals = (volatile struct syzos_globals*)X86_SYZOS_ADDR_GLOBALS;
globals->active_vm_id[cpu_id] = vm_id;
struct l2_guest_regs* l2_regs = (struct l2_guest_regs*)&globals->l2_ctx[cpu_id][vm_id];
uint8_t fail_flag = 0;
asm volatile(R"... [truncated large byte array] ..."
: [fail_flag] "=m"(fail_flag)
: [cpu_id] "r"(cpu_id), [vmcb_addr] "r"(vmcb_addr), [l2_regs] "r"(l2_regs),
[l2_regs_size] "i"(sizeof(struct l2_guest_regs))
: "cc", "memory", "rax", "rcx", "rdx", "rsi", "rdi", "r8", "r9", "r10", "r11");
if (fail_flag) {
guest_uexit(0xE2E10000 | 0xFFFF);
return;
}
}
GUEST_CODE static noinline void
guest_handle_nested_vmlaunch(struct api_call_1* cmd, uint64_t cpu_id)
{
uint64_t vm_id = cmd->arg;
if (get_cpu_vendor() == CPU_VENDOR_INTEL) {
guest_handle_nested_vmentry_intel(vm_id, cpu_id, true);
} else {
guest_run_amd_vm(cpu_id, vm_id);
}
}
GUEST_CODE static noinline void
guest_handle_nested_vmresume(struct api_call_1* cmd, uint64_t cpu_id)
{
uint64_t vm_id = cmd->arg;
if (get_cpu_vendor() == CPU_VENDOR_INTEL) {
guest_handle_nested_vmentry_intel(vm_id, cpu_id, false);
} else {
guest_run_amd_vm(cpu_id, vm_id);
}
}
GUEST_CODE static noinline void
guest_handle_nested_intel_vmwrite_mask(struct api_call_5* cmd, uint64_t cpu_id)
{
if (get_cpu_vendor() != CPU_VENDOR_INTEL)
return;
uint64_t vm_id = cmd->args[0];
nested_vmptrld(cpu_id, vm_id);
uint64_t field = cmd->args[1];
uint64_t set_mask = cmd->args[2];
uint64_t unset_mask = cmd->args[3];
uint64_t flip_mask = cmd->args[4];
uint64_t current_value = vmread(field);
uint64_t new_value = (current_value & ~unset_mask) | set_mask;
new_value ^= flip_mask;
vmwrite(field, new_value);
}
GUEST_CODE static noinline void
guest_handle_nested_amd_vmcb_write_mask(struct api_call_5* cmd, uint64_t cpu_id)
{
if (get_cpu_vendor() != CPU_VENDOR_AMD)
return;
uint64_t vm_id = cmd->args[0];
uint64_t vmcb_addr = X86_SYZOS_ADDR_VMCS_VMCB(cpu_id, vm_id);
uint64_t offset = cmd->args[1];
uint64_t set_mask = cmd->args[2];
uint64_t unset_mask = cmd->args[3];
uint64_t flip_mask = cmd->args[4];
uint64_t current_value = vmcb_read64((volatile uint8_t*)vmcb_addr, offset);
uint64_t new_value = (current_value & ~unset_mask) | set_mask;
new_value ^= flip_mask;
vmcb_write64(vmcb_addr, offset, new_value);
}
GUEST_CODE static noinline void
guest_handle_nested_amd_invlpga(struct api_call_2* cmd, uint64_t cpu_id)
{
if (get_cpu_vendor() != CPU_VENDOR_AMD)
return;
uint64_t linear_addr = cmd->args[0];
uint32_t asid = (uint32_t)cmd->args[1];
asm volatile("invlpga" : : "a"(linear_addr), "c"(asid) : "memory");
}
GUEST_CODE static noinline void
guest_handle_nested_amd_stgi()
{
if (get_cpu_vendor() != CPU_VENDOR_AMD)
return;
asm volatile("stgi" ::: "memory");
}
GUEST_CODE static noinline void
guest_handle_nested_amd_clgi()
{
if (get_cpu_vendor() != CPU_VENDOR_AMD)
return;
asm volatile("clgi" ::: "memory");
}
GUEST_CODE static noinline void
guest_handle_nested_amd_inject_event(struct api_call_5* cmd, uint64_t cpu_id)
{
if (get_cpu_vendor() != CPU_VENDOR_AMD)
return;
uint64_t vm_id = cmd->args[0];
uint64_t vmcb_addr = X86_SYZOS_ADDR_VMCS_VMCB(cpu_id, vm_id);
uint64_t vector = cmd->args[1] & 0xFF;
uint64_t type = cmd->args[2] & 0x7;
uint64_t error_code = cmd->args[3] & 0xFFFFFFFF;
uint64_t flags = cmd->args[4];
uint64_t event_inj = vector;
event_inj |= (type << 8);
if (flags & 2)
event_inj |= (1ULL << 11);
if (flags & 1)
event_inj |= (1ULL << 31);
event_inj |= (error_code << 32);
vmcb_write64(vmcb_addr, 0x60, event_inj);
}
GUEST_CODE static noinline void
guest_handle_nested_amd_set_intercept(struct api_call_5* cmd, uint64_t cpu_id)
{
if (get_cpu_vendor() != CPU_VENDOR_AMD)
return;
uint64_t vm_id = cmd->args[0];
uint64_t vmcb_addr = X86_SYZOS_ADDR_VMCS_VMCB(cpu_id, vm_id);
uint64_t offset = cmd->args[1];
uint64_t bit_mask = cmd->args[2];
uint64_t action = cmd->args[3];
uint32_t current = vmcb_read32(vmcb_addr, (uint16_t)offset);
if (action == 1)
current |= (uint32_t)bit_mask;
else
current &= ~((uint32_t)bit_mask);
vmcb_write32(vmcb_addr, (uint16_t)offset, current);
}
GUEST_CODE static noinline void
guest_handle_nested_amd_vmload(struct api_call_1* cmd, uint64_t cpu_id)
{
if (get_cpu_vendor() != CPU_VENDOR_AMD)
return;
uint64_t vm_id = cmd->arg;
uint64_t vmcb_pa = X86_SYZOS_ADDR_VMCS_VMCB(cpu_id, vm_id);
asm volatile("vmload %%rax" ::"a"(vmcb_pa) : "memory");
}
GUEST_CODE static noinline void
guest_handle_nested_amd_vmsave(struct api_call_1* cmd, uint64_t cpu_id)
{
if (get_cpu_vendor() != CPU_VENDOR_AMD)
return;
uint64_t vm_id = cmd->arg;
uint64_t vmcb_pa = X86_SYZOS_ADDR_VMCS_VMCB(cpu_id, vm_id);
asm volatile("vmsave %%rax" ::"a"(vmcb_pa) : "memory");
}
const char kvm_asm16_cpl3[] = "... [truncated large byte array] ...";
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_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[] = "... [truncated large byte array] ...";
const char kvm_asm64_vm_exit[] = "... [truncated large byte array] ...";
const char kvm_asm64_cpl3[] = "... [truncated large byte array] ...";
#define KVM_SMI _IO(KVMIO, 0xb7)
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;
uint16_t reserved3;
uint16_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;
dt[index] = sd;
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;
dt[index + 1] = 0;
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;
struct kvm_msr_entry* entries = msrs->entries;
msrs->nmsrs = 5;
entries[0].index = X86_MSR_IA32_SYSENTER_CS;
entries[0].data = sel_cs;
entries[1].index = X86_MSR_IA32_SYSENTER_ESP;
entries[1].data = X86_ADDR_STACK0;
entries[2].index = X86_MSR_IA32_SYSENTER_EIP;
entries[2].data = X86_ADDR_VAR_SYSEXIT;
entries[3].index = X86_MSR_IA32_STAR;
entries[3].data = ((uint64_t)sel_cs << 32) | ((uint64_t)sel_cs_cpl3 << 48);
entries[4].index = X86_MSR_IA32_LSTAR;
entries[4].data = X86_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 + X86_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:
gate.type = 6;
gate.base = X86_SEL_CS16;
break;
case 1:
gate.type = 7;
gate.base = X86_SEL_CS16;
break;
case 2:
gate.type = 3;
gate.base = X86_SEL_TGATE16;
break;
case 3:
gate.type = 14;
gate.base = X86_SEL_CS32;
break;
case 4:
gate.type = 15;
gate.base = X86_SEL_CS32;
break;
case 5:
gate.type = 11;
gate.base = X86_SEL_TGATE32;
break;
}
gate.limit = guest_mem + X86_ADDR_VAR_USER_CODE2;
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 + X86_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;
gate.base = X86_SEL_CS64;
gate.limit = guest_mem + X86_ADDR_VAR_USER_CODE2;
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);
}
}
static const struct mem_region syzos_mem_regions[] = {
{X86_SYZOS_ADDR_ZERO, 5, MEM_REGION_FLAG_GPA0},
{X86_SYZOS_ADDR_VAR_IDT, 10, 0},
{X86_SYZOS_ADDR_BOOT_ARGS, 1, 0},
{X86_SYZOS_ADDR_PT_POOL, X86_SYZOS_PT_POOL_SIZE, 0},
{X86_SYZOS_ADDR_GLOBALS, 1, 0},
{X86_SYZOS_ADDR_SMRAM, 10, 0},
{X86_SYZOS_ADDR_EXIT, 1, MEM_REGION_FLAG_NO_HOST_MEM},
{X86_SYZOS_ADDR_DIRTY_PAGES, 2, MEM_REGION_FLAG_DIRTY_LOG},
{X86_SYZOS_ADDR_USER_CODE, KVM_MAX_VCPU, MEM_REGION_FLAG_READONLY | MEM_REGION_FLAG_USER_CODE},
{SYZOS_ADDR_EXECUTOR_CODE, 4, MEM_REGION_FLAG_READONLY | MEM_REGION_FLAG_EXECUTOR_CODE},
{X86_SYZOS_ADDR_SCRATCH_CODE, 1, 0},
{X86_SYZOS_ADDR_STACK_BOTTOM, 1, 0},
{X86_SYZOS_PER_VCPU_REGIONS_BASE, (KVM_MAX_VCPU * X86_SYZOS_L1_VCPU_REGION_SIZE) / KVM_PAGE_SIZE, 0},
{X86_SYZOS_ADDR_IOAPIC, 1, 0},
{X86_SYZOS_ADDR_UNUSED, 0, MEM_REGION_FLAG_REMAINING},
};
#define SYZOS_REGION_COUNT (sizeof(syzos_mem_regions) / sizeof(syzos_mem_regions[0]))
struct kvm_syz_vm {
int vmfd;
int next_cpu_id;
void* host_mem;
size_t total_pages;
void* user_text;
void* gpa0_mem;
void* pt_pool_mem;
void* globals_mem;
void* region_base[SYZOS_REGION_COUNT];
};
static inline void* gpa_to_hva(struct kvm_syz_vm* vm, uint64_t gpa)
{
for (size_t i = 0; i < SYZOS_REGION_COUNT; i++) {
const struct mem_region* r = &syzos_mem_regions[i];
if (r->flags & MEM_REGION_FLAG_NO_HOST_MEM)
continue;
if (r->gpa == X86_SYZOS_ADDR_UNUSED)
break;
size_t region_size = r->pages * KVM_PAGE_SIZE;
if (gpa >= r->gpa && gpa < r->gpa + region_size)
return (void*)((char*)vm->region_base[i] + (gpa - r->gpa));
}
return NULL;
}
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)
static volatile long syz_kvm_setup_cpu(volatile long a0, volatile long a1, volatile long a2, volatile long a3, volatile long a4, volatile long a5, volatile long a6, volatile long 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;
(void)text_count;
int text_type = text_array_ptr[0].typ;
const void* text = text_array_ptr[0].text;
uintptr_t text_size = text_array_ptr[0].size;
for (uintptr_t i = 0; i < guest_mem_size / page_size; i++) {
struct kvm_userspace_memory_region memreg;
memreg.slot = i;
memreg.flags = 0;
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);
}
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(®s, 0, sizeof(regs));
regs.rip = guest_mem + X86_ADDR_TEXT;
regs.rsp = X86_ADDR_STACK0;
sregs.gdt.base = guest_mem + X86_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;
memset(&seg_ldt, 0, sizeof(seg_ldt));
seg_ldt.selector = X86_SEL_LDT;
seg_ldt.type = 2;
seg_ldt.base = guest_mem + X86_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;
sregs.ldt = seg_ldt;
uint64_t* ldt = (uint64_t*)(host_mem + sregs.ldt.base);
struct kvm_segment seg_cs16;
memset(&seg_cs16, 0, sizeof(seg_cs16));
seg_cs16.selector = X86_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;
struct kvm_segment seg_ds16 = seg_cs16;
seg_ds16.selector = X86_SEL_DS16;
seg_ds16.type = 3;
struct kvm_segment seg_cs16_cpl3 = seg_cs16;
seg_cs16_cpl3.selector = X86_SEL_CS16_CPL3;
seg_cs16_cpl3.dpl = 3;
struct kvm_segment seg_ds16_cpl3 = seg_ds16;
seg_ds16_cpl3.selector = X86_SEL_DS16_CPL3;
seg_ds16_cpl3.dpl = 3;
struct kvm_segment seg_cs32 = seg_cs16;
seg_cs32.selector = X86_SEL_CS32;
seg_cs32.db = 1;
struct kvm_segment seg_ds32 = seg_ds16;
seg_ds32.selector = X86_SEL_DS32;
seg_ds32.db = 1;
struct kvm_segment seg_cs32_cpl3 = seg_cs32;
seg_cs32_cpl3.selector = X86_SEL_CS32_CPL3;
seg_cs32_cpl3.dpl = 3;
struct kvm_segment seg_ds32_cpl3 = seg_ds32;
seg_ds32_cpl3.selector = X86_SEL_DS32_CPL3;
seg_ds32_cpl3.dpl = 3;
struct kvm_segment seg_cs64 = seg_cs16;
seg_cs64.selector = X86_SEL_CS64;
seg_cs64.l = 1;
struct kvm_segment seg_ds64 = seg_ds32;
seg_ds64.selector = X86_SEL_DS64;
struct kvm_segment seg_cs64_cpl3 = seg_cs64;
seg_cs64_cpl3.selector = X86_SEL_CS64_CPL3;
seg_cs64_cpl3.dpl = 3;
struct kvm_segment seg_ds64_cpl3 = seg_ds64;
seg_ds64_cpl3.selector = X86_SEL_DS64_CPL3;
seg_ds64_cpl3.dpl = 3;
struct kvm_segment seg_tss32;
memset(&seg_tss32, 0, sizeof(seg_tss32));
seg_tss32.selector = X86_SEL_TSS32;
seg_tss32.type = 9;
seg_tss32.base = X86_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;
struct kvm_segment seg_tss32_2 = seg_tss32;
seg_tss32_2.selector = X86_SEL_TSS32_2;
seg_tss32_2.base = X86_ADDR_VAR_TSS32_2;
struct kvm_segment seg_tss32_cpl3 = seg_tss32;
seg_tss32_cpl3.selector = X86_SEL_TSS32_CPL3;
seg_tss32_cpl3.base = X86_ADDR_VAR_TSS32_CPL3;
struct kvm_segment seg_tss32_vm86 = seg_tss32;
seg_tss32_vm86.selector = X86_SEL_TSS32_VM86;
seg_tss32_vm86.base = X86_ADDR_VAR_TSS32_VM86;
struct kvm_segment seg_tss16 = seg_tss32;
seg_tss16.selector = X86_SEL_TSS16;
seg_tss16.base = X86_ADDR_VAR_TSS16;
seg_tss16.limit = 0xff;
seg_tss16.type = 1;
struct kvm_segment seg_tss16_2 = seg_tss16;
seg_tss16_2.selector = X86_SEL_TSS16_2;
seg_tss16_2.base = X86_ADDR_VAR_TSS16_2;
seg_tss16_2.dpl = 0;
struct kvm_segment seg_tss16_cpl3 = seg_tss16;
seg_tss16_cpl3.selector = X86_SEL_TSS16_CPL3;
seg_tss16_cpl3.base = X86_ADDR_VAR_TSS16_CPL3;
seg_tss16_cpl3.dpl = 3;
struct kvm_segment seg_tss64 = seg_tss32;
seg_tss64.selector = X86_SEL_TSS64;
seg_tss64.base = X86_ADDR_VAR_TSS64;
seg_tss64.limit = 0x1ff;
struct kvm_segment seg_tss64_cpl3 = seg_tss64;
seg_tss64_cpl3.selector = X86_SEL_TSS64_CPL3;
seg_tss64_cpl3.base = X86_ADDR_VAR_TSS64_CPL3;
seg_tss64_cpl3.dpl = 3;
struct kvm_segment seg_cgate16;
memset(&seg_cgate16, 0, sizeof(seg_cgate16));
seg_cgate16.selector = X86_SEL_CGATE16;
seg_cgate16.type = 4;
seg_cgate16.base = X86_SEL_CS16 | (2 << 16);
seg_cgate16.limit = X86_ADDR_VAR_USER_CODE2;
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;
struct kvm_segment seg_tgate16 = seg_cgate16;
seg_tgate16.selector = X86_SEL_TGATE16;
seg_tgate16.type = 3;
seg_cgate16.base = X86_SEL_TSS16_2;
seg_tgate16.limit = 0;
struct kvm_segment seg_cgate32 = seg_cgate16;
seg_cgate32.selector = X86_SEL_CGATE32;
seg_cgate32.type = 12;
seg_cgate32.base = X86_SEL_CS32 | (2 << 16);
struct kvm_segment seg_tgate32 = seg_cgate32;
seg_tgate32.selector = X86_SEL_TGATE32;
seg_tgate32.type = 11;
seg_tgate32.base = X86_SEL_TSS32_2;
seg_tgate32.limit = 0;
struct kvm_segment seg_cgate64 = seg_cgate16;
seg_cgate64.selector = X86_SEL_CGATE64;
seg_cgate64.type = 12;
seg_cgate64.base = X86_SEL_CS64;
int kvmfd = open("/dev/kvm", O_RDWR);
char buf[sizeof(struct 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 = 0;
int text_prefix_size = 0;
char* host_text = host_mem + X86_ADDR_TEXT;
if (text_type == 8) {
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 |= X86_CR0_PE;
} else {
sregs.cs.selector = 0;
sregs.cs.base = 0;
}
*(host_mem + X86_ADDR_TEXT) = 0xf4;
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 |= X86_CR0_PE;
sregs.efer |= X86_EFER_SCE;
setup_syscall_msrs(cpufd, X86_SEL_CS32, X86_SEL_CS32_CPL3);
setup_32bit_idt(&sregs, host_mem, guest_mem);
if (flags & KVM_SETUP_PAGING) {
uint64_t pd_addr = guest_mem + X86_ADDR_PD;
uint64_t* pd = (uint64_t*)(host_mem + X86_ADDR_PD);
pd[0] = X86_PDE32_PRESENT | X86_PDE32_RW | X86_PDE32_USER | X86_PDE32_PS;
sregs.cr3 = pd_addr;
sregs.cr4 |= X86_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 {
sregs.cs.selector = 0;
sregs.cs.base = 0;
}
} else if (text_type == 16) {
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.cr0 |= X86_CR0_PE;
sregs.cs = seg_cs16;
sregs.ds = sregs.es = sregs.fs = sregs.gs = sregs.ss = seg_ds16;
}
} else if (text_type == 32) {
sregs.cr0 |= X86_CR0_PE;
sregs.efer |= X86_EFER_SCE;
setup_syscall_msrs(cpufd, X86_SEL_CS32, X86_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;
*(host_mem + X86_ADDR_TEXT) = 0xf4;
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 + X86_ADDR_PD;
uint64_t* pd = (uint64_t*)(host_mem + X86_ADDR_PD);
pd[0] = X86_PDE32_PRESENT | X86_PDE32_RW | X86_PDE32_USER | X86_PDE32_PS;
sregs.cr3 = pd_addr;
sregs.cr4 |= X86_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 |= X86_EFER_LME | X86_EFER_SCE;
sregs.cr0 |= X86_CR0_PE;
setup_syscall_msrs(cpufd, X86_SEL_CS64, X86_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 + X86_ADDR_PML4;
uint64_t* pml4 = (uint64_t*)(host_mem + X86_ADDR_PML4);
uint64_t pdpt_addr = guest_mem + X86_ADDR_PDP;
uint64_t* pdpt = (uint64_t*)(host_mem + X86_ADDR_PDP);
uint64_t pd_addr = guest_mem + X86_ADDR_PD;
uint64_t* pd = (uint64_t*)(host_mem + X86_ADDR_PD);
pml4[0] = X86_PDE64_PRESENT | X86_PDE64_RW | X86_PDE64_USER | pdpt_addr;
pdpt[0] = X86_PDE64_PRESENT | X86_PDE64_RW | X86_PDE64_USER | pd_addr;
pd[0] = X86_PDE64_PRESENT | X86_PDE64_RW | X86_PDE64_USER | X86_PDE64_PS;
sregs.cr3 = pml4_addr;
sregs.cr4 |= X86_CR4_PAE;
if (flags & KVM_SETUP_VM) {
sregs.cr0 |= X86_CR0_NE;
*((uint64_t*)(host_mem + X86_ADDR_VAR_VMXON_PTR)) = X86_ADDR_VAR_VMXON;
*((uint64_t*)(host_mem + X86_ADDR_VAR_VMCS_PTR)) = X86_ADDR_VAR_VMCS;
memcpy(host_mem + X86_ADDR_VAR_VMEXIT_CODE, kvm_asm64_vm_exit, sizeof(kvm_asm64_vm_exit) - 1);
*((uint64_t*)(host_mem + X86_ADDR_VAR_VMEXIT_PTR)) = X86_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;
}
}
struct tss16 tss16;
memset(&tss16, 0, sizeof(tss16));
tss16.ss0 = tss16.ss1 = tss16.ss2 = X86_SEL_DS16;
tss16.sp0 = tss16.sp1 = tss16.sp2 = X86_ADDR_STACK0;
tss16.ip = X86_ADDR_VAR_USER_CODE2;
tss16.flags = (1 << 1);
tss16.cs = X86_SEL_CS16;
tss16.es = tss16.ds = tss16.ss = X86_SEL_DS16;
tss16.ldt = X86_SEL_LDT;
struct tss16* tss16_addr = (struct tss16*)(host_mem + seg_tss16_2.base);
memcpy(tss16_addr, &tss16, sizeof(tss16));
memset(&tss16, 0, sizeof(tss16));
tss16.ss0 = tss16.ss1 = tss16.ss2 = X86_SEL_DS16;
tss16.sp0 = tss16.sp1 = tss16.sp2 = X86_ADDR_STACK0;
tss16.ip = X86_ADDR_VAR_USER_CODE2;
tss16.flags = (1 << 1);
tss16.cs = X86_SEL_CS16_CPL3;
tss16.es = tss16.ds = tss16.ss = X86_SEL_DS16_CPL3;
tss16.ldt = X86_SEL_LDT;
struct tss16* tss16_cpl3_addr = (struct tss16*)(host_mem + seg_tss16_cpl3.base);
memcpy(tss16_cpl3_addr, &tss16, sizeof(tss16));
struct tss32 tss32;
memset(&tss32, 0, sizeof(tss32));
tss32.ss0 = tss32.ss1 = tss32.ss2 = X86_SEL_DS32;
tss32.sp0 = tss32.sp1 = tss32.sp2 = X86_ADDR_STACK0;
tss32.ip = X86_ADDR_VAR_USER_CODE;
tss32.flags = (1 << 1) | (1 << 17);
tss32.ldt = X86_SEL_LDT;
tss32.cr3 = sregs.cr3;
tss32.io_bitmap = offsetof(struct tss32, io_bitmap);
struct tss32* tss32_addr = (struct tss32*)(host_mem + seg_tss32_vm86.base);
memcpy(tss32_addr, &tss32, sizeof(tss32));
memset(&tss32, 0, sizeof(tss32));
tss32.ss0 = tss32.ss1 = tss32.ss2 = X86_SEL_DS32;
tss32.sp0 = tss32.sp1 = tss32.sp2 = X86_ADDR_STACK0;
tss32.ip = X86_ADDR_VAR_USER_CODE;
tss32.flags = (1 << 1);
tss32.cr3 = sregs.cr3;
tss32.es = tss32.ds = tss32.ss = tss32.gs = tss32.fs = X86_SEL_DS32;
tss32.cs = X86_SEL_CS32;
tss32.ldt = X86_SEL_LDT;
tss32.cr3 = sregs.cr3;
tss32.io_bitmap = offsetof(struct tss32, io_bitmap);
struct tss32* tss32_cpl3_addr = (struct tss32*)(host_mem + seg_tss32_2.base);
memcpy(tss32_cpl3_addr, &tss32, sizeof(tss32));
struct tss64 tss64;
memset(&tss64, 0, sizeof(tss64));
tss64.rsp[0] = X86_ADDR_STACK0;
tss64.rsp[1] = X86_ADDR_STACK0;
tss64.rsp[2] = X86_ADDR_STACK0;
tss64.io_bitmap = offsetof(struct tss64, io_bitmap);
struct tss64* tss64_addr = (struct tss64*)(host_mem + seg_tss64.base);
memcpy(tss64_addr, &tss64, sizeof(tss64));
memset(&tss64, 0, sizeof(tss64));
tss64.rsp[0] = X86_ADDR_STACK0;
tss64.rsp[1] = X86_ADDR_STACK0;
tss64.rsp[2] = X86_ADDR_STACK0;
tss64.io_bitmap = offsetof(struct tss64, io_bitmap);
struct tss64* tss64_cpl3_addr = (struct tss64*)(host_mem + seg_tss64_cpl3.base);
memcpy(tss64_cpl3_addr, &tss64, sizeof(tss64));
if (text_size > 1000)
text_size = 1000;
if (text_prefix) {
memcpy(host_text, text_prefix, text_prefix_size);
void* patch = memmem(host_text, text_prefix_size, "\xde\xc0\xad\x0b", 4);
if (patch)
*((uint32_t*)patch) = guest_mem + X86_ADDR_TEXT + ((char*)patch - host_text) + 6;
uint16_t magic = X86_PREFIX_SIZE;
patch = memmem(host_text, text_prefix_size, &magic, sizeof(magic));
if (patch)
*((uint16_t*)patch) = guest_mem + X86_ADDR_TEXT + text_prefix_size;
}
memcpy((void*)(host_text + text_prefix_size), text, text_size);
*(host_text + text_prefix_size + text_size) = 0xf4;
memcpy(host_mem + X86_ADDR_VAR_USER_CODE, text, text_size);
*(host_mem + X86_ADDR_VAR_USER_CODE + text_size) = 0xf4;
*(host_mem + X86_ADDR_VAR_HLT) = 0xf4;
memcpy(host_mem + X86_ADDR_VAR_SYSRET, "\x0f\x07\xf4", 3);
memcpy(host_mem + X86_ADDR_VAR_SYSEXIT, "\x0f\x35\xf4", 3);
*(uint64_t*)(host_mem + X86_ADDR_VAR_VMWRITE_FLD) = 0;
*(uint64_t*)(host_mem + X86_ADDR_VAR_VMWRITE_VAL) = 0;
if (opt_count > 2)
opt_count = 2;
for (uintptr_t i = 0; i < opt_count; i++) {
uint64_t typ = opt_array_ptr[i].typ;
uint64_t val = opt_array_ptr[i].val;
switch (typ % 9) {
case 0:
sregs.cr0 ^= val & (X86_CR0_MP | X86_CR0_EM | X86_CR0_ET | X86_CR0_NE | X86_CR0_WP | X86_CR0_AM | X86_CR0_NW | X86_CR0_CD);
break;
case 1:
sregs.cr4 ^= val & (X86_CR4_VME | X86_CR4_PVI | X86_CR4_TSD | X86_CR4_DE | X86_CR4_MCE | X86_CR4_PGE | X86_CR4_PCE |
X86_CR4_OSFXSR | X86_CR4_OSXMMEXCPT | X86_CR4_UMIP | X86_CR4_VMXE | X86_CR4_SMXE | X86_CR4_FSGSBASE | X86_CR4_PCIDE |
X86_CR4_OSXSAVE | X86_CR4_SMEP | X86_CR4_SMAP | X86_CR4_PKE);
break;
case 2:
sregs.efer ^= val & (X86_EFER_SCE | X86_EFER_NXE | X86_EFER_SVME | X86_EFER_LMSLE | X86_EFER_FFXSR | X86_EFER_TCE);
break;
case 3:
val &= ((1 << 8) | (1 << 9) | (1 << 10) | (1 << 12) | (1 << 13) | (1 << 14) |
(1 << 15) | (1 << 18) | (1 << 19) | (1 << 20) | (1 << 21));
regs.rflags ^= val;
tss16_addr->flags ^= val;
tss16_cpl3_addr->flags ^= val;
tss32_addr->flags ^= val;
tss32_cpl3_addr->flags ^= val;
break;
case 4:
seg_cs16.type = val & 0xf;
seg_cs32.type = val & 0xf;
seg_cs64.type = val & 0xf;
break;
case 5:
seg_cs16_cpl3.type = val & 0xf;
seg_cs32_cpl3.type = val & 0xf;
seg_cs64_cpl3.type = val & 0xf;
break;
case 6:
seg_ds16.type = val & 0xf;
seg_ds32.type = val & 0xf;
seg_ds64.type = val & 0xf;
break;
case 7:
seg_ds16_cpl3.type = val & 0xf;
seg_ds32_cpl3.type = val & 0xf;
seg_ds64_cpl3.type = val & 0xf;
break;
case 8:
*(uint64_t*)(host_mem + X86_ADDR_VAR_VMWRITE_FLD) = (val & 0xffff);
*(uint64_t*)(host_mem + X86_ADDR_VAR_VMWRITE_VAL) = (val >> 16);
break;
default:
exit(1);
}
}
regs.rflags |= 2;
fill_segment_descriptor(gdt, ldt, &seg_ldt);
fill_segment_descriptor(gdt, ldt, &seg_cs16);
fill_segment_descriptor(gdt, ldt, &seg_ds16);
fill_segment_descriptor(gdt, ldt, &seg_cs16_cpl3);
fill_segment_descriptor(gdt, ldt, &seg_ds16_cpl3);
fill_segment_descriptor(gdt, ldt, &seg_cs32);
fill_segment_descriptor(gdt, ldt, &seg_ds32);
fill_segment_descriptor(gdt, ldt, &seg_cs32_cpl3);
fill_segment_descriptor(gdt, ldt, &seg_ds32_cpl3);
fill_segment_descriptor(gdt, ldt, &seg_cs64);
fill_segment_descriptor(gdt, ldt, &seg_ds64);
fill_segment_descriptor(gdt, ldt, &seg_cs64_cpl3);
fill_segment_descriptor(gdt, ldt, &seg_ds64_cpl3);
fill_segment_descriptor(gdt, ldt, &seg_tss32);
fill_segment_descriptor(gdt, ldt, &seg_tss32_2);
fill_segment_descriptor(gdt, ldt, &seg_tss32_cpl3);
fill_segment_descriptor(gdt, ldt, &seg_tss32_vm86);
fill_segment_descriptor(gdt, ldt, &seg_tss16);
fill_segment_descriptor(gdt, ldt, &seg_tss16_2);
fill_segment_descriptor(gdt, ldt, &seg_tss16_cpl3);
fill_segment_descriptor_dword(gdt, ldt, &seg_tss64);
fill_segment_descriptor_dword(gdt, ldt, &seg_tss64_cpl3);
fill_segment_descriptor(gdt, ldt, &seg_cgate16);
fill_segment_descriptor(gdt, ldt, &seg_tgate16);
fill_segment_descriptor(gdt, ldt, &seg_cgate32);
fill_segment_descriptor(gdt, ldt, &seg_tgate32);
fill_segment_descriptor_dword(gdt, ldt, &seg_cgate64);
if (ioctl(cpufd, KVM_SET_SREGS, &sregs))
return -1;
if (ioctl(cpufd, KVM_SET_REGS, ®s))
return -1;
return 0;
}
uint64_t r[4] = {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff};
int main(void)
{
syscall(__NR_mmap, /*addr=*/0x1ffffffff000ul, /*len=*/0x1000ul, /*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
syscall(__NR_mmap, /*addr=*/0x200000000000ul, /*len=*/0x1000000ul, /*prot=PROT_WRITE|PROT_READ|PROT_EXEC*/7ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
syscall(__NR_mmap, /*addr=*/0x200001000000ul, /*len=*/0x1000ul, /*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
const char* reason;
(void)reason;
intptr_t res = 0;
if (write(1, "executing program\n", sizeof("executing program\n") - 1)) {}
// openat$kvm arguments: [
// fd: const = 0xffffffffffffff9c (8 bytes)
// file: ptr[in, buffer] {
// buffer: {2f 64 65 76 2f 6b 76 6d 00} (length 0x9)
// }
// flags: open_flags = 0x0 (4 bytes)
// mode: const = 0x0 (2 bytes)
// ]
// returns fd_kvm
memcpy((void*)0x200000000100, "/dev/kvm\000", 9);
res = syscall(__NR_openat, /*fd=*/0xffffffffffffff9cul, /*file=*/0x200000000100ul, /*flags=*/0, /*mode=*/0);
if (res != -1)
r[0] = res;
// ioctl$KVM_CREATE_VM arguments: [
// fd: fd_kvm (resource)
// cmd: const = 0xae01 (4 bytes)
// type: intptr = 0x0 (8 bytes)
// ]
// returns fd_kvmvm
res = syscall(__NR_ioctl, /*fd=*/r[0], /*cmd=*/0xae01, /*type=*/0ul);
if (res != -1)
r[1] = res;
// userfaultfd arguments: [
// flags: userfaultfd_flags = 0x1 (8 bytes)
// ]
// returns fd_uffd
res = syscall(__NR_userfaultfd, /*flags=UFFD_USER_MODE_ONLY*/1ul);
if (res != -1)
r[2] = res;
// ioctl$UFFDIO_API arguments: [
// fd: fd_uffd (resource)
// cmd: const = 0xc018aa3f (4 bytes)
// arg: ptr[in, uffdio_api] {
// uffdio_api {
// api: const = 0xaa (8 bytes)
// featur: uffdio_features = 0x0 (8 bytes)
// ioctls: const = 0x0 (8 bytes)
// }
// }
// ]
*(uint64_t*)0x2000000000c0 = 0xaa;
*(uint64_t*)0x2000000000c8 = 0;
*(uint64_t*)0x2000000000d0 = 0;
syscall(__NR_ioctl, /*fd=*/r[2], /*cmd=*/0xc018aa3f, /*arg=*/0x2000000000c0ul);
// ioctl$UFFDIO_REGISTER arguments: [
// fd: fd_uffd (resource)
// cmd: const = 0xc020aa00 (4 bytes)
// arg: ptr[in, uffdio_register] {
// uffdio_register {
// range: uffdio_range {
// start: VMA[0xc00000]
// len: len = 0xc00000 (8 bytes)
// }
// mode: uffdio_register_mode = 0x1 (8 bytes)
// ioctls: const = 0x0 (8 bytes)
// }
// }
// ]
*(uint64_t*)0x200000000100 = 0x2000000e2000;
*(uint64_t*)0x200000000108 = 0xc00000;
*(uint64_t*)0x200000000110 = 1;
*(uint64_t*)0x200000000118 = 0;
syscall(__NR_ioctl, /*fd=*/r[2], /*cmd=*/0xc020aa00, /*arg=*/0x200000000100ul);
// ioctl$KVM_CREATE_VCPU arguments: [
// fd: fd_kvmvm (resource)
// cmd: const = 0xae41 (4 bytes)
// id: intptr = 0x0 (8 bytes)
// ]
// returns fd_kvmcpu
res = syscall(__NR_ioctl, /*fd=*/r[1], /*cmd=*/0xae41, /*id=*/0ul);
if (res != -1)
r[3] = res;
// syz_kvm_setup_cpu$x86 arguments: [
// fd: fd_kvmvm (resource)
// cpufd: fd_kvmcpu (resource)
// usermem: VMA[0x18000]
// text: ptr[in, array[kvm_text_x86]] {
// array[kvm_text_x86] {
// union kvm_text_x86 {
// text32: kvm_text_x86_32 {
// typ: const = 0x20 (8 bytes)
// text: nil
// size: len = 0x0 (8 bytes)
// }
// }
// }
// }
// ntext: len = 0x1 (8 bytes)
// flags: kvm_setup_flags = 0xfffffdfd (8 bytes)
// opts: nil
// nopt: len = 0x0 (8 bytes)
// ]
*(uint64_t*)0x200000000140 = 0x20;
*(uint64_t*)0x200000000148 = 0;
*(uint64_t*)0x200000000150 = 0;
syz_kvm_setup_cpu(/*fd=*/-1, /*cpufd=*/r[3], /*usermem=*/0x200000b7a000, /*text=*/0x200000000140, /*ntext=*/1, /*flags=KVM_SETUP_VM|KVM_SETUP_SMM|KVM_SETUP_VIRT86|KVM_SETUP_CPL3|KVM_SETUP_PROTECTED|KVM_SETUP_PAGING|0xfffffd80*/0xfffffdfd, /*opts=*/0, /*nopt=*/0);
return 0;
}
]