// https://syzkaller.appspot.com/bug?id=d219a27084c471908203a8873860a1d16a36f0b8 // autogenerated by syzkaller (https://github.com/google/syzkaller) #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef __NR_ioctl #define __NR_ioctl 29 #endif #ifndef __NR_mmap #define __NR_mmap 222 #endif #ifndef __NR_openat #define __NR_openat 56 #endif static unsigned long long procid; static void sleep_ms(uint64_t ms) { usleep(ms * 1000); } static uint64_t current_time_ms(void) { struct timespec ts; if (clock_gettime(CLOCK_MONOTONIC, &ts)) exit(1); return (uint64_t)ts.tv_sec * 1000 + (uint64_t)ts.tv_nsec / 1000000; } static void thread_start(void* (*fn)(void*), void* arg) { pthread_t th; pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setstacksize(&attr, 128 << 10); int i = 0; for (; i < 100; i++) { if (pthread_create(&th, &attr, fn, arg) == 0) { pthread_attr_destroy(&attr); return; } if (errno == EAGAIN) { usleep(50); continue; } break; } exit(1); } typedef struct { int state; } event_t; static void event_init(event_t* ev) { ev->state = 0; } static void event_reset(event_t* ev) { ev->state = 0; } static void event_set(event_t* ev) { if (ev->state) exit(1); __atomic_store_n(&ev->state, 1, __ATOMIC_RELEASE); syscall(SYS_futex, &ev->state, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, 1000000); } static void event_wait(event_t* ev) { while (!__atomic_load_n(&ev->state, __ATOMIC_ACQUIRE)) syscall(SYS_futex, &ev->state, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, 0, 0); } static int event_isset(event_t* ev) { return __atomic_load_n(&ev->state, __ATOMIC_ACQUIRE); } static int event_timedwait(event_t* ev, uint64_t timeout) { uint64_t start = current_time_ms(); uint64_t now = start; for (;;) { uint64_t remain = timeout - (now - start); struct timespec ts; ts.tv_sec = remain / 1000; ts.tv_nsec = (remain % 1000) * 1000 * 1000; syscall(SYS_futex, &ev->state, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, 0, &ts); if (__atomic_load_n(&ev->state, __ATOMIC_ACQUIRE)) return 1; now = current_time_ms(); if (now - start > timeout) return 0; } } static bool write_file(const char* file, const char* what, ...) { char buf[1024]; va_list args; va_start(args, what); vsnprintf(buf, sizeof(buf), what, args); va_end(args); buf[sizeof(buf) - 1] = 0; int len = strlen(buf); int fd = open(file, O_WRONLY | O_CLOEXEC); if (fd == -1) return false; if (write(fd, buf, len) != len) { int err = errno; close(fd); errno = err; return false; } close(fd); return true; } #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_ADDR_SMRAM 0x30000 #define X86_ADDR_EXIT 0x40000 #define X86_ADDR_UEXIT (X86_ADDR_EXIT + 256) #define X86_ADDR_DIRTY_PAGES 0x41000 #define X86_ADDR_USER_CODE 0x50000 #define X86_ADDR_EXECUTOR_CODE 0x54000 #define X86_ADDR_SCRATCH_CODE 0x58000 #define X86_ADDR_UNUSED 0x200000 #define X86_ADDR_IOAPIC 0xfec00000 #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 << 8) #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 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_STAR 0xC0000081 #define X86_MSR_IA32_LSTAR 0xC0000082 #define X86_MSR_IA32_VMX_PROCBASED_CTLS2 0x48B #define X86_NEXT_INSN $0xbadc0de #define X86_PREFIX_SIZE 0xba1d #define KVM_MAX_VCPU 4 #define KVM_PAGE_SIZE (1 << 12) #define KVM_GUEST_MEM_SIZE (1024 * KVM_PAGE_SIZE) #define SZ_4K 0x00001000 #define SZ_64K 0x00010000 #define GENMASK_ULL(h, l) \ (((~0ULL) - (1ULL << (l)) + 1ULL) & (~0ULL >> (63 - (h)))) #define ARM64_ADDR_GICD_BASE 0x08000000 #define ARM64_ADDR_GITS_BASE 0x08080000 #define ARM64_ADDR_GICR_BASE 0x080a0000 #define ARM64_ADDR_ITS_TABLES 0xc0000000 #define ARM64_ADDR_EXIT 0xdddd0000 #define ARM64_ADDR_UEXIT (ARM64_ADDR_EXIT + 256) #define ARM64_ADDR_DIRTY_PAGES 0xdddd1000 #define ARM64_ADDR_USER_CODE 0xeeee0000 #define ARM64_ADDR_EXECUTOR_CODE 0xeeee8000 #define ARM64_ADDR_SCRATCH_CODE 0xeeef0000 #define ARM64_ADDR_EL1_STACK_BOTTOM 0xffff1000 #define ITS_MAX_DEVICES 16 #define ARM64_ADDR_ITS_DEVICE_TABLE (ARM64_ADDR_ITS_TABLES) #define ARM64_ADDR_ITS_COLL_TABLE (ARM64_ADDR_ITS_DEVICE_TABLE + SZ_64K) #define ARM64_ADDR_ITS_CMDQ_BASE (ARM64_ADDR_ITS_COLL_TABLE + SZ_64K) #define ARM64_ADDR_ITS_ITT_TABLES (ARM64_ADDR_ITS_CMDQ_BASE + SZ_64K) #define ARM64_ADDR_ITS_PROP_TABLE \ (ARM64_ADDR_ITS_ITT_TABLES + SZ_64K * ITS_MAX_DEVICES) #define ARM64_ADDR_ITS_PEND_TABLES (ARM64_ADDR_ITS_PROP_TABLE + SZ_64K) #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_ADDR_SMRAM 0x30000 #define X86_ADDR_EXIT 0x40000 #define X86_ADDR_UEXIT (X86_ADDR_EXIT + 256) #define X86_ADDR_DIRTY_PAGES 0x41000 #define X86_ADDR_USER_CODE 0x50000 #define X86_ADDR_EXECUTOR_CODE 0x54000 #define X86_ADDR_SCRATCH_CODE 0x58000 #define X86_ADDR_UNUSED 0x200000 #define X86_ADDR_IOAPIC 0xfec00000 #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 << 8) #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 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_STAR 0xC0000081 #define X86_MSR_IA32_LSTAR 0xC0000082 #define X86_MSR_IA32_VMX_PROCBASED_CTLS2 0x48B #define X86_NEXT_INSN $0xbadc0de #define X86_PREFIX_SIZE 0xba1d #define KVM_MAX_VCPU 4 #define KVM_PAGE_SIZE (1 << 12) #define KVM_GUEST_MEM_SIZE (1024 * KVM_PAGE_SIZE) #define SZ_4K 0x00001000 #define SZ_64K 0x00010000 #define GENMASK_ULL(h, l) \ (((~0ULL) - (1ULL << (l)) + 1ULL) & (~0ULL >> (63 - (h)))) #define ARM64_ADDR_GICD_BASE 0x08000000 #define ARM64_ADDR_GITS_BASE 0x08080000 #define ARM64_ADDR_GICR_BASE 0x080a0000 #define ARM64_ADDR_ITS_TABLES 0xc0000000 #define ARM64_ADDR_EXIT 0xdddd0000 #define ARM64_ADDR_UEXIT (ARM64_ADDR_EXIT + 256) #define ARM64_ADDR_DIRTY_PAGES 0xdddd1000 #define ARM64_ADDR_USER_CODE 0xeeee0000 #define ARM64_ADDR_EXECUTOR_CODE 0xeeee8000 #define ARM64_ADDR_SCRATCH_CODE 0xeeef0000 #define ARM64_ADDR_EL1_STACK_BOTTOM 0xffff1000 #define ITS_MAX_DEVICES 16 #define ARM64_ADDR_ITS_DEVICE_TABLE (ARM64_ADDR_ITS_TABLES) #define ARM64_ADDR_ITS_COLL_TABLE (ARM64_ADDR_ITS_DEVICE_TABLE + SZ_64K) #define ARM64_ADDR_ITS_CMDQ_BASE (ARM64_ADDR_ITS_COLL_TABLE + SZ_64K) #define ARM64_ADDR_ITS_ITT_TABLES (ARM64_ADDR_ITS_CMDQ_BASE + SZ_64K) #define ARM64_ADDR_ITS_PROP_TABLE \ (ARM64_ADDR_ITS_ITT_TABLES + SZ_64K * ITS_MAX_DEVICES) #define ARM64_ADDR_ITS_PEND_TABLES (ARM64_ADDR_ITS_PROP_TABLE + SZ_64K) #define GUEST_CODE __attribute__((section("guest"))) #define noinline __attribute__((noinline)) extern char *__start_guest, *__stop_guest; typedef enum { SYZOS_API_UEXIT, SYZOS_API_CODE, SYZOS_API_MSR, SYZOS_API_SMC, SYZOS_API_HVC, SYZOS_API_IRQ_SETUP, SYZOS_API_MEMWRITE, SYZOS_API_ITS_SETUP, SYZOS_API_ITS_SEND_CMD, SYZOS_API_MRS, SYZOS_API_STOP, } syzos_api_id; struct api_call_header { uint64_t call; uint64_t size; }; struct api_call_uexit { struct api_call_header header; uint64_t exit_code; }; 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_code { struct api_call_header header; uint32_t insns[]; }; struct api_call_smccc { struct api_call_header header; uint32_t func_id; uint64_t params[5]; }; struct api_call_irq_setup { struct api_call_header header; uint32_t nr_cpus; uint32_t nr_spis; }; struct api_call_memwrite { struct api_call_header header; uint64_t base_addr; uint64_t offset; uint64_t value; uint64_t len; }; struct api_call_its_send_cmd { struct api_call_header header; uint8_t type; uint8_t valid; uint32_t cpuid; uint32_t devid; uint32_t eventid; uint32_t intid; uint32_t cpuid2; }; static void guest_uexit(uint64_t exit_code); static void guest_execute_code(uint32_t* insns, uint64_t size); static void guest_handle_mrs(uint64_t reg); static void guest_handle_msr(uint64_t reg, uint64_t val); static void guest_handle_smc(struct api_call_smccc* cmd); static void guest_handle_hvc(struct api_call_smccc* cmd); static void guest_handle_irq_setup(struct api_call_irq_setup* cmd); static void guest_handle_memwrite(struct api_call_memwrite* cmd); static void guest_handle_its_setup(struct api_call_3* cmd); static void guest_handle_its_send_cmd(struct api_call_its_send_cmd* cmd); typedef enum { UEXIT_END = (uint64_t)-1, UEXIT_IRQ = (uint64_t)-2, UEXIT_ASSERT = (uint64_t)-3, } uexit_code; __attribute__((used)) GUEST_CODE static void guest_main(uint64_t size, uint64_t cpu) { uint64_t addr = ARM64_ADDR_USER_CODE + cpu * 0x1000; while (size >= sizeof(struct api_call_header)) { struct api_call_header* cmd = (struct api_call_header*)addr; if (cmd->call >= SYZOS_API_STOP) return; if (cmd->size > size) return; switch (cmd->call) { case SYZOS_API_UEXIT: { struct api_call_uexit* ucmd = (struct api_call_uexit*)cmd; guest_uexit(ucmd->exit_code); break; } case 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)); break; } case SYZOS_API_MRS: { struct api_call_1* ccmd = (struct api_call_1*)cmd; guest_handle_mrs(ccmd->arg); break; } case SYZOS_API_MSR: { struct api_call_2* ccmd = (struct api_call_2*)cmd; guest_handle_msr(ccmd->args[0], ccmd->args[1]); break; } case SYZOS_API_SMC: { guest_handle_smc((struct api_call_smccc*)cmd); break; } case SYZOS_API_HVC: { guest_handle_hvc((struct api_call_smccc*)cmd); break; } case SYZOS_API_IRQ_SETUP: { guest_handle_irq_setup((struct api_call_irq_setup*)cmd); break; } case SYZOS_API_MEMWRITE: { guest_handle_memwrite((struct api_call_memwrite*)cmd); break; } case SYZOS_API_ITS_SETUP: { guest_handle_its_setup((struct api_call_3*)cmd); break; } case SYZOS_API_ITS_SEND_CMD: { guest_handle_its_send_cmd((struct api_call_its_send_cmd*)cmd); break; } } addr += cmd->size; size -= cmd->size; }; guest_uexit((uint64_t)-1); } GUEST_CODE static noinline void guest_execute_code(uint32_t* insns, uint64_t size) { volatile void (*fn)() = (volatile void (*)())insns; fn(); } GUEST_CODE static noinline void guest_uexit(uint64_t exit_code) { volatile uint64_t* ptr = (volatile uint64_t*)ARM64_ADDR_UEXIT; *ptr = exit_code; } #define MSR_REG_OPCODE 0xd5100000 #define MRS_REG_OPCODE 0xd5300000 GUEST_CODE static uint32_t reg_to_msr(uint64_t reg) { return MSR_REG_OPCODE | ((reg & 0xffff) << 5); } GUEST_CODE static uint32_t reg_to_mrs(uint64_t reg) { return MRS_REG_OPCODE | ((reg & 0xffff) << 5); } GUEST_CODE static uint32_t get_cpu_id() { uint64_t val = 0; asm volatile("mrs %0, tpidr_el1" : "=r"(val)); return (uint32_t)val; } #define MAX_CACHE_LINE_SIZE 256 GUEST_CODE static noinline void guest_handle_mrs(uint64_t reg) { uint32_t mrs = reg_to_mrs(reg); uint32_t cpu_id = get_cpu_id(); uint32_t* insn = (uint32_t*)((uint64_t)ARM64_ADDR_SCRATCH_CODE + cpu_id * MAX_CACHE_LINE_SIZE); insn[0] = mrs; insn[1] = 0xd65f03c0; asm("blr %[pc]\n" : : [pc] "r"(insn) : "x0", "x30"); } GUEST_CODE static noinline void guest_handle_msr(uint64_t reg, uint64_t val) { uint32_t msr = reg_to_msr(reg); uint32_t cpu_id = get_cpu_id(); uint32_t* insn = (uint32_t*)((uint64_t)ARM64_ADDR_SCRATCH_CODE + cpu_id * MAX_CACHE_LINE_SIZE); insn[0] = msr; insn[1] = 0xd65f03c0; asm("mov x0, %[val]\nblr %[pc]\n" : : [val] "r"(val), [pc] "r"(insn) : "x0", "x30", "memory"); } GUEST_CODE static noinline void guest_handle_smc(struct api_call_smccc* cmd) { asm volatile( "mov x0, %[func_id]\n" "mov x1, %[arg1]\n" "mov x2, %[arg2]\n" "mov x3, %[arg3]\n" "mov x4, %[arg4]\n" "mov x5, %[arg5]\n" "smc #0\n" : : [func_id] "r"((uint64_t)cmd->func_id), [arg1] "r"(cmd->params[0]), [arg2] "r"(cmd->params[1]), [arg3] "r"(cmd->params[2]), [arg4] "r"(cmd->params[3]), [arg5] "r"(cmd->params[4]) : "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "memory"); } GUEST_CODE static noinline void guest_handle_hvc(struct api_call_smccc* cmd) { asm volatile( "mov x0, %[func_id]\n" "mov x1, %[arg1]\n" "mov x2, %[arg2]\n" "mov x3, %[arg3]\n" "mov x4, %[arg4]\n" "mov x5, %[arg5]\n" "hvc #0\n" : : [func_id] "r"((uint64_t)cmd->func_id), [arg1] "r"(cmd->params[0]), [arg2] "r"(cmd->params[1]), [arg3] "r"(cmd->params[2]), [arg4] "r"(cmd->params[3]), [arg5] "r"(cmd->params[4]) : "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "memory"); } #define GICD_CTLR 0x0000 #define GICD_IGROUPR 0x0080 #define GICD_ISENABLER 0x0100 #define GICD_ICENABLER 0x0180 #define GICD_ICACTIVER 0x0380 #define GICD_IPRIORITYR 0x0400 #define GICD_INT_DEF_PRI_X4 0xa0a0a0a0 #define GICD_CTLR_ARE_NS (1U << 4) #define GICD_CTLR_ENABLE_G1A (1U << 1) #define GICD_CTLR_ENABLE_G1 (1U << 0) #define GICD_CTLR_RWP (1U << 31) #define GICR_CTLR GICD_CTLR #define GICR_WAKER 0x0014 #define GICR_PROPBASER 0x0070 #define GICR_PENDBASER 0x0078 #define GICR_CTLR_ENABLE_LPIS (1UL << 0) #define GICR_CTLR_RWP (1UL << 3) #define GICR_IGROUPR0 GICD_IGROUPR #define GICR_ICENABLER0 GICD_ICENABLER #define GICR_ICACTIVER0 GICD_ICACTIVER #define GICR_IPRIORITYR0 GICD_IPRIORITYR #define ICC_SRE_EL1_SRE (1U << 0) #define ICC_PMR_DEF_PRIO 0xff #define ICC_IGRPEN1_EL1_ENABLE (1U << 0) #define GICR_WAKER_ProcessorSleep (1U << 1) #define GICR_WAKER_ChildrenAsleep (1U << 2) #define ICC_SRE_EL1 "S3_0_C12_C12_5" #define ICC_PMR_EL1 "S3_0_C4_C6_0" #define ICC_IGRPEN1_EL1 "S3_0_C12_C12_7" #define ICC_IAR0_EL1 "S3_0_C12_C8_0" #define ICC_IAR1_EL1 "S3_0_C12_C12_0" #define ICC_EOIR0_EL1 "S3_0_C12_C8_1" #define ICC_EOIR1_EL1 "S3_0_C12_C12_1" #define ICC_DIR_EL1 "S3_0_C12_C11_1" GUEST_CODE static __always_inline void __raw_writel(uint32_t val, uint64_t addr) { asm volatile("str %w0, [%1]" : : "rZ"(val), "r"(addr)); } GUEST_CODE static __always_inline void __raw_writeq(uint64_t val, uint64_t addr) { asm volatile("str %x0, [%1]" : : "rZ"(val), "r"(addr)); } GUEST_CODE static __always_inline uint32_t __raw_readl(uint64_t addr) { uint32_t val; asm volatile("ldr %w0, [%1]" : "=r"(val) : "r"(addr)); return val; } GUEST_CODE static __always_inline uint64_t __raw_readq(uint64_t addr) { uint64_t val; asm volatile("ldr %x0, [%1]" : "=r"(val) : "r"(addr)); return val; } #define dmb() asm volatile("dmb sy" : : : "memory") #define writel(v, c) \ ({ \ dmb(); \ __raw_writel(v, c); \ }) #define readl(c) \ ({ \ uint32_t __v = __raw_readl(c); \ dmb(); \ __v; \ }) #define writeq(v, c) \ ({ \ dmb(); \ __raw_writeq(v, c); \ }) #define readq(c) \ ({ \ uint64_t __v = __raw_readq(c); \ dmb(); \ __v; \ }) #define GUEST_ASSERT(val) \ do { \ if (!(val)) \ guest_uexit(UEXIT_ASSERT); \ } while (0) GUEST_CODE static uint64_t read_cntvct(void) { uint64_t val; asm volatile("mrs %0, cntvct_el0" : "=r"(val)); return val; } GUEST_CODE static void guest_udelay(uint32_t us) { uint64_t ticks_per_second = 0; asm volatile("mrs %0, cntfrq_el0" : "=r"(ticks_per_second)); uint64_t start = read_cntvct(); uint64_t target = start + (us * ticks_per_second) / 1000000; while (read_cntvct() < target) { } } GUEST_CODE static void spin_while_readl(uint64_t reg, uint32_t mask) { volatile unsigned int count = 100000; while (readl(reg) & mask) { GUEST_ASSERT(count--); guest_udelay(10); } } GUEST_CODE static void gicd_wait_for_rwp() { spin_while_readl(ARM64_ADDR_GICD_BASE + GICD_CTLR, GICD_CTLR_RWP); } GUEST_CODE static uint64_t gicr_base_cpu(uint32_t cpu) { return ARM64_ADDR_GICR_BASE + cpu * SZ_64K * 2; } GUEST_CODE static uint64_t sgi_base_cpu(uint32_t cpu) { return gicr_base_cpu(cpu) + SZ_64K; } GUEST_CODE static void gicr_wait_for_rwp(uint32_t cpu) { spin_while_readl(gicr_base_cpu(cpu) + GICR_CTLR, GICR_CTLR_RWP); } GUEST_CODE static void gicv3_dist_init(int nr_spis) { writel(0, ARM64_ADDR_GICD_BASE + GICD_CTLR); gicd_wait_for_rwp(); for (int i = 32; i < nr_spis + 32; i += 32) { writel(~0, ARM64_ADDR_GICD_BASE + GICD_IGROUPR + i / 8); writel(~0, ARM64_ADDR_GICD_BASE + GICD_ICACTIVER + i / 8); writel(~0, ARM64_ADDR_GICD_BASE + GICD_ICENABLER + i / 8); } for (int i = 32; i < nr_spis + 32; i += 4) { writel(GICD_INT_DEF_PRI_X4, ARM64_ADDR_GICD_BASE + GICD_IPRIORITYR + i); } gicd_wait_for_rwp(); writel(GICD_CTLR_ARE_NS | GICD_CTLR_ENABLE_G1A | GICD_CTLR_ENABLE_G1, ARM64_ADDR_GICD_BASE + GICD_CTLR); gicd_wait_for_rwp(); } GUEST_CODE static void gicv3_enable_redist(uint32_t cpu) { uint64_t redist_base_cpu = gicr_base_cpu(cpu); uint32_t val = readl(redist_base_cpu + GICR_WAKER); val &= ~GICR_WAKER_ProcessorSleep; writel(val, ARM64_ADDR_GICR_BASE + GICR_WAKER); spin_while_readl(ARM64_ADDR_GICR_BASE + GICR_WAKER, GICR_WAKER_ChildrenAsleep); } GUEST_CODE static void gicv3_cpu_init(uint32_t cpu) { uint64_t sgi_base = sgi_base_cpu(cpu); gicv3_enable_redist(cpu); writel(~0, sgi_base + GICR_IGROUPR0); writel(~0, sgi_base + GICR_ICACTIVER0); writel(~0, sgi_base + GICR_ICENABLER0); for (int i = 0; i < 32; i += 4) { writel(GICD_INT_DEF_PRI_X4, sgi_base + GICR_IPRIORITYR0 + i); } gicr_wait_for_rwp(cpu); uint64_t icc_sre_el1 = 0; asm volatile("mrs %0, " ICC_SRE_EL1 : : "r"(icc_sre_el1)); icc_sre_el1 |= ICC_SRE_EL1_SRE; asm volatile("msr " ICC_SRE_EL1 ", %0" : : "r"(icc_sre_el1)); uint64_t value = ICC_PMR_DEF_PRIO; asm volatile("msr " ICC_PMR_EL1 ", %0" : : "r"(value)); value = ICC_IGRPEN1_EL1_ENABLE; asm volatile("msr " ICC_IGRPEN1_EL1 ", %0" : : "r"(value)); } #define VGICV3_MIN_SPI 32 #define VGICV3_MAX_SPI 1019 GUEST_CODE static void gicv3_irq_enable(uint32_t intid) { uint32_t cpu = get_cpu_id(); writel(1 << (intid % 32), ARM64_ADDR_GICD_BASE + GICD_ISENABLER + (intid / 32) * 4); if ((intid >= VGICV3_MIN_SPI) && (intid <= VGICV3_MAX_SPI)) gicd_wait_for_rwp(); else gicr_wait_for_rwp(cpu); } GUEST_CODE static noinline void guest_handle_irq_setup(struct api_call_irq_setup* cmd) { int nr_spis = cmd->nr_spis; if ((nr_spis > VGICV3_MAX_SPI - VGICV3_MIN_SPI) || (nr_spis < 0)) nr_spis = 32; int nr_cpus = cmd->nr_cpus; gicv3_dist_init(nr_spis); for (int i = 0; i < nr_cpus; i++) gicv3_cpu_init(i); for (int i = 0; i < nr_spis; i++) gicv3_irq_enable(VGICV3_MIN_SPI + i); asm(R"( adr x1, guest_vector_table msr vbar_el1, x1 msr daifclr, #0b1111 )" : : : "x1"); } GUEST_CODE static noinline void guest_handle_memwrite(struct api_call_memwrite* cmd) { uint64_t dest = cmd->base_addr + cmd->offset; switch (cmd->len) { case 1: { volatile uint8_t* p = (uint8_t*)dest; *p = (uint8_t)cmd->value; break; } case 2: { volatile uint16_t* p = (uint16_t*)dest; *p = (uint16_t)cmd->value; break; } case 4: { volatile uint32_t* p = (uint32_t*)dest; *p = (uint32_t)cmd->value; break; } case 8: default: { volatile uint64_t* p = (uint64_t*)dest; *p = (uint64_t)cmd->value; break; } } } GUEST_CODE static void guest_prepare_its(int nr_cpus, int nr_devices, int nr_events); GUEST_CODE static noinline void guest_handle_its_setup(struct api_call_3* cmd) { guest_prepare_its(cmd->args[0], cmd->args[1], cmd->args[2]); } struct ex_regs { uint64_t regs[31]; uint64_t sp; uint64_t pc; uint64_t pstate; }; __attribute__((used)) GUEST_CODE static void one_irq_handler_fn() { asm volatile( R"(.global one_irq_handler one_irq_handler: # Allocate 34 * uint64_t for struct ex_regs. add sp, sp, #-16 * 17 # Store registers x0-x29 on the stack. stp x0, x1, [sp, #16 * 0] stp x2, x3, [sp, #16 * 1] stp x4, x5, [sp, #16 * 2] stp x6, x7, [sp, #16 * 3] stp x8, x9, [sp, #16 * 4] stp x10, x11, [sp, #16 * 5] stp x12, x13, [sp, #16 * 6] stp x14, x15, [sp, #16 * 7] stp x16, x17, [sp, #16 * 8] stp x18, x19, [sp, #16 * 9] stp x20, x21, [sp, #16 * 10] stp x22, x23, [sp, #16 * 11] stp x24, x25, [sp, #16 * 12] stp x26, x27, [sp, #16 * 13] stp x28, x29, [sp, #16 * 14] add x1, sp, #16 * 17 # Store x30 and SP (before allocating ex_regs). stp x30, x1, [sp, #16 * 15] # ELR_EL1 holds the PC to return to. mrs x1, elr_el1 # SPSR_EL1 is the saved PSTATE. mrs x2, spsr_el1 # Also store them to ex_regs. stp x1, x2, [sp, #16 * 16] # Call guest_irq_handler(ex_regs). mov x0, sp bl guest_irq_handler # Restore ELR_EL1 and SPSR_EL1. ldp x1, x2, [sp, #16 * 16] msr elr_el1, x1 msr spsr_el1, x2 # Restore the GP registers x0-x30 (ignoring SP). ldp x30, xzr, [sp, #16 * 15] ldp x28, x29, [sp, #16 * 14] ldp x26, x27, [sp, #16 * 13] ldp x24, x25, [sp, #16 * 12] ldp x22, x23, [sp, #16 * 11] ldp x20, x21, [sp, #16 * 10] ldp x18, x19, [sp, #16 * 9] ldp x16, x17, [sp, #16 * 8] ldp x14, x15, [sp, #16 * 7] ldp x12, x13, [sp, #16 * 6] ldp x10, x11, [sp, #16 * 5] ldp x8, x9, [sp, #16 * 4] ldp x6, x7, [sp, #16 * 3] ldp x4, x5, [sp, #16 * 2] ldp x2, x3, [sp, #16 * 1] ldp x0, x1, [sp, #16 * 0] add sp, sp, #16 * 17 # Use ERET to exit from an exception. eret)" : : : "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "memory"); } __attribute__((used)) GUEST_CODE static void guest_irq_handler(struct ex_regs* regs) { uint64_t iar0, iar1, irq_num = 0; bool is_group0 = false; asm volatile("mrs %0, " ICC_IAR0_EL1 : "=r"(iar0)); asm volatile("mrs %0, " ICC_IAR1_EL1 : "=r"(iar1)); if ((iar0 < 1020) || (iar0 > 1023)) { irq_num = iar0; is_group0 = true; } else if ((iar1 < 1020) || (iar1 > 1023)) { irq_num = iar1; } else { return; } guest_uexit(UEXIT_IRQ); if (is_group0) { asm volatile("msr " ICC_EOIR0_EL1 ", %0" : : "r"(irq_num)); } else { asm volatile("msr " ICC_EOIR1_EL1 ", %0" : : "r"(irq_num)); } asm volatile("msr " ICC_DIR_EL1 ", %0" : : "r"(irq_num)); } #define IRQ_ENTRY \ ".balign 0x80\n" \ "b one_irq_handler\n" #define IRQ_ENTRY_DUMMY \ ".balign 0x80\n" \ "eret\n" __attribute__((used)) GUEST_CODE static void guest_vector_table_fn() { asm volatile( ".global guest_vector_table\n" ".balign 2048\n" "guest_vector_table:\n" IRQ_ENTRY_DUMMY IRQ_ENTRY_DUMMY IRQ_ENTRY_DUMMY IRQ_ENTRY_DUMMY IRQ_ENTRY_DUMMY IRQ_ENTRY IRQ_ENTRY_DUMMY IRQ_ENTRY_DUMMY IRQ_ENTRY_DUMMY IRQ_ENTRY_DUMMY IRQ_ENTRY_DUMMY IRQ_ENTRY_DUMMY IRQ_ENTRY_DUMMY IRQ_ENTRY_DUMMY IRQ_ENTRY_DUMMY IRQ_ENTRY_DUMMY); } #define GITS_CTLR 0x0000 #define GITS_CBASER 0x0080 #define GITS_CWRITER 0x0088 #define GITS_CREADR 0x0090 #define GITS_BASER 0x0100 #define GITS_CTLR_ENABLE (1U << 0) #define GIC_BASER_InnerShareable 1ULL #define GIC_PAGE_SIZE_64K 2ULL #define GITS_BASER_PAGE_SIZE_SHIFT (8) #define __GITS_BASER_PSZ(sz) (GIC_PAGE_SIZE_##sz << GITS_BASER_PAGE_SIZE_SHIFT) #define GITS_BASER_PAGE_SIZE_64K __GITS_BASER_PSZ(64K) #define GIC_BASER_CACHE_RaWaWb 7ULL #define GITS_BASER_INNER_CACHEABILITY_SHIFT (59) #define GITS_BASER_RaWaWb GIC_BASER_CACHEABILITY(GITS_BASER, INNER, RaWaWb) #define GITS_CBASER_INNER_CACHEABILITY_SHIFT (59) #define GITS_CBASER_RaWaWb GIC_BASER_CACHEABILITY(GITS_CBASER, INNER, RaWaWb) #define GICR_PROPBASER_SHAREABILITY_SHIFT (10) #define GICR_PROPBASER_INNER_CACHEABILITY_SHIFT (7) #define GICR_PROPBASER_RaWaWb \ GIC_BASER_CACHEABILITY(GICR_PROPBASER, INNER, RaWaWb) #define GICR_PROPBASER_IDBITS_MASK (0x1f) #define GIC_BASER_CACHEABILITY(reg, inner_outer, type) \ (GIC_BASER_CACHE_##type << reg##_##inner_outer##_CACHEABILITY_SHIFT) #define GITS_BASER_SHAREABILITY_SHIFT (10) #define GITS_CBASER_SHAREABILITY_SHIFT (10) #define GIC_BASER_SHAREABILITY(reg, type) \ (GIC_BASER_##type << reg##_SHAREABILITY_SHIFT) #define GITS_BASER_InnerShareable \ GIC_BASER_SHAREABILITY(GITS_BASER, InnerShareable) #define GITS_CBASER_InnerShareable \ GIC_BASER_SHAREABILITY(GITS_CBASER, InnerShareable) #define GICR_PROPBASER_InnerShareable \ GIC_BASER_SHAREABILITY(GICR_PROPBASER, InnerShareable) #define GICR_PENDBASER_InnerShareable \ GIC_BASER_SHAREABILITY(GICR_PENDBASER, InnerShareable) #define GICR_PENDBASER_SHAREABILITY_SHIFT (10) #define GICR_PENDBASER_INNER_CACHEABILITY_SHIFT (7) #define GICR_PENDBASER_RaWaWb \ GIC_BASER_CACHEABILITY(GICR_PENDBASER, INNER, RaWaWb) #define GITS_BASER_TYPE_NONE 0 #define GITS_BASER_TYPE_DEVICE 1 #define GITS_BASER_TYPE_VCPU 2 #define GITS_BASER_TYPE_RESERVED3 3 #define GITS_BASER_TYPE_COLLECTION 4 #define GITS_BASER_TYPE_RESERVED5 5 #define GITS_BASER_TYPE_RESERVED6 6 #define GITS_BASER_TYPE_RESERVED7 7 #define GITS_BASER_TYPE_SHIFT (56) #define GITS_BASER_TYPE(r) (((r) >> GITS_BASER_TYPE_SHIFT) & 7) #define GITS_BASER_NR_REGS 8 #define GITS_BASER_VALID (1ULL << 63) #define GITS_CBASER_VALID (1ULL << 63) GUEST_CODE static uint64_t its_read_u64(unsigned long offset) { return readq(ARM64_ADDR_GITS_BASE + offset); } GUEST_CODE static void its_write_u64(unsigned long offset, uint64_t val) { writeq(val, ARM64_ADDR_GITS_BASE + offset); } GUEST_CODE static uint32_t its_read_u32(unsigned long offset) { return readl(ARM64_ADDR_GITS_BASE + offset); } GUEST_CODE static void its_write_u32(unsigned long offset, uint32_t val) { writel(val, ARM64_ADDR_GITS_BASE + offset); } struct its_cmd_block { uint64_t raw_cmd[4]; }; GUEST_CODE static noinline void guest_memcpy(void* dst, void* src, size_t size) { volatile char* pdst = (char*)dst; volatile char* psrc = (char*)src; for (size_t i = 0; i < size; i++) pdst[i] = psrc[i]; } GUEST_CODE static noinline void its_send_cmd(uint64_t cmdq_base, struct its_cmd_block* cmd) { uint64_t cwriter = its_read_u64(GITS_CWRITER); struct its_cmd_block* dst = (struct its_cmd_block*)(cmdq_base + cwriter); uint64_t cbaser = its_read_u64(GITS_CBASER); size_t cmdq_size = ((cbaser & 0xFF) + 1) * SZ_4K; guest_memcpy(dst, cmd, sizeof(*cmd)); dmb(); uint64_t next = (cwriter + sizeof(*cmd)) % cmdq_size; its_write_u64(GITS_CWRITER, next); } GUEST_CODE static unsigned long its_find_baser(unsigned int type) { for (int i = 0; i < GITS_BASER_NR_REGS; i++) { uint64_t baser; unsigned long offset = GITS_BASER + (i * sizeof(baser)); baser = its_read_u64(offset); if (GITS_BASER_TYPE(baser) == type) return offset; } GUEST_ASSERT(0); return -1; } GUEST_CODE static void its_install_table(unsigned int type, uint64_t base, size_t size) { unsigned long offset = its_find_baser(type); uint64_t baser = ((size / SZ_64K) - 1) | GITS_BASER_PAGE_SIZE_64K | GITS_BASER_InnerShareable | base | GITS_BASER_RaWaWb | GITS_BASER_VALID; its_write_u64(offset, baser); } GUEST_CODE static void its_install_cmdq(uint64_t base, size_t size) { uint64_t cbaser = ((size / SZ_4K) - 1) | GITS_CBASER_InnerShareable | base | GITS_CBASER_RaWaWb | GITS_CBASER_VALID; its_write_u64(GITS_CBASER, cbaser); } GUEST_CODE static void its_init(uint64_t coll_tbl, uint64_t device_tbl, uint64_t cmdq) { its_install_table(GITS_BASER_TYPE_COLLECTION, coll_tbl, SZ_64K); its_install_table(GITS_BASER_TYPE_DEVICE, device_tbl, SZ_64K); its_install_cmdq(cmdq, SZ_64K); uint32_t ctlr = its_read_u32(GITS_CTLR); ctlr |= GITS_CTLR_ENABLE; its_write_u32(GITS_CTLR, ctlr); } #define GIC_LPI_OFFSET 8192 #define GITS_CMD_MAPD 0x08 #define GITS_CMD_MAPC 0x09 #define GITS_CMD_MAPTI 0x0a #define GITS_CMD_MAPI 0x0b #define GITS_CMD_MOVI 0x01 #define GITS_CMD_DISCARD 0x0f #define GITS_CMD_INV 0x0c #define GITS_CMD_MOVALL 0x0e #define GITS_CMD_INVALL 0x0d #define GITS_CMD_INT 0x03 #define GITS_CMD_CLEAR 0x04 #define GITS_CMD_SYNC 0x05 GUEST_CODE static noinline void its_mask_encode(uint64_t* raw_cmd, uint64_t val, int h, int l) { uint64_t mask = GENMASK_ULL(h, l); *raw_cmd &= ~mask; *raw_cmd |= (val << l) & mask; } GUEST_CODE static void its_encode_cmd(struct its_cmd_block* cmd, uint8_t cmd_nr) { its_mask_encode(&cmd->raw_cmd[0], cmd_nr, 7, 0); } GUEST_CODE static void its_encode_devid(struct its_cmd_block* cmd, uint32_t devid) { its_mask_encode(&cmd->raw_cmd[0], devid, 63, 32); } GUEST_CODE static void its_encode_event_id(struct its_cmd_block* cmd, uint32_t id) { its_mask_encode(&cmd->raw_cmd[1], id, 31, 0); } GUEST_CODE static void its_encode_phys_id(struct its_cmd_block* cmd, uint32_t phys_id) { its_mask_encode(&cmd->raw_cmd[1], phys_id, 63, 32); } GUEST_CODE static void its_encode_size(struct its_cmd_block* cmd, uint8_t size) { its_mask_encode(&cmd->raw_cmd[1], size, 4, 0); } GUEST_CODE static void its_encode_itt(struct its_cmd_block* cmd, uint64_t itt_addr) { its_mask_encode(&cmd->raw_cmd[2], itt_addr >> 8, 51, 8); } GUEST_CODE static void its_encode_valid(struct its_cmd_block* cmd, int valid) { its_mask_encode(&cmd->raw_cmd[2], !!valid, 63, 63); } GUEST_CODE static void its_encode_target(struct its_cmd_block* cmd, uint64_t target_addr) { its_mask_encode(&cmd->raw_cmd[2], target_addr >> 16, 51, 16); } GUEST_CODE static void its_encode_target2(struct its_cmd_block* cmd, uint64_t target_addr) { its_mask_encode(&cmd->raw_cmd[3], target_addr >> 16, 51, 16); } GUEST_CODE static void its_encode_collection(struct its_cmd_block* cmd, uint16_t col) { its_mask_encode(&cmd->raw_cmd[2], col, 15, 0); } GUEST_CODE static noinline void guest_memzero(void* ptr, size_t size) { volatile char* p = (char*)ptr; for (size_t i = 0; i < size; i++) p[i] = 0; } GUEST_CODE static void its_send_mapd_cmd(uint64_t cmdq_base, uint32_t device_id, uint64_t itt_base, size_t num_idbits, bool valid) { struct its_cmd_block cmd; guest_memzero(&cmd, sizeof(cmd)); its_encode_cmd(&cmd, GITS_CMD_MAPD); its_encode_devid(&cmd, device_id); its_encode_size(&cmd, num_idbits - 1); its_encode_itt(&cmd, itt_base); its_encode_valid(&cmd, valid); its_send_cmd(cmdq_base, &cmd); } GUEST_CODE static void its_send_mapc_cmd(uint64_t cmdq_base, uint32_t vcpu_id, uint32_t collection_id, bool valid) { struct its_cmd_block cmd; guest_memzero(&cmd, sizeof(cmd)); its_encode_cmd(&cmd, GITS_CMD_MAPC); its_encode_collection(&cmd, collection_id); its_encode_target(&cmd, vcpu_id); its_encode_valid(&cmd, valid); its_send_cmd(cmdq_base, &cmd); } GUEST_CODE static void its_send_mapti_cmd(uint64_t cmdq_base, uint32_t device_id, uint32_t event_id, uint32_t collection_id, uint32_t intid) { struct its_cmd_block cmd; guest_memzero(&cmd, sizeof(cmd)); its_encode_cmd(&cmd, GITS_CMD_MAPTI); its_encode_devid(&cmd, device_id); its_encode_event_id(&cmd, event_id); its_encode_phys_id(&cmd, intid); its_encode_collection(&cmd, collection_id); its_send_cmd(cmdq_base, &cmd); } GUEST_CODE static void its_send_devid_eventid_icid_cmd(uint64_t cmdq_base, uint8_t cmd_nr, uint32_t device_id, uint32_t event_id, uint32_t intid) { struct its_cmd_block cmd; guest_memzero(&cmd, sizeof(cmd)); its_encode_cmd(&cmd, cmd_nr); its_encode_devid(&cmd, device_id); its_encode_event_id(&cmd, event_id); its_encode_phys_id(&cmd, intid); its_send_cmd(cmdq_base, &cmd); } GUEST_CODE static void its_send_devid_eventid_cmd(uint64_t cmdq_base, uint8_t cmd_nr, uint32_t device_id, uint32_t event_id) { struct its_cmd_block cmd; guest_memzero(&cmd, sizeof(cmd)); its_encode_cmd(&cmd, cmd_nr); its_encode_devid(&cmd, device_id); its_encode_event_id(&cmd, event_id); its_send_cmd(cmdq_base, &cmd); } GUEST_CODE static void its_send_movall_cmd(uint64_t cmdq_base, uint32_t vcpu_id, uint32_t vcpu_id2) { struct its_cmd_block cmd; guest_memzero(&cmd, sizeof(cmd)); its_encode_cmd(&cmd, GITS_CMD_MOVALL); its_encode_target(&cmd, vcpu_id); its_encode_target2(&cmd, vcpu_id2); its_send_cmd(cmdq_base, &cmd); } GUEST_CODE static void its_send_invall_cmd(uint64_t cmdq_base, uint32_t collection_id) { struct its_cmd_block cmd; guest_memzero(&cmd, sizeof(cmd)); its_encode_cmd(&cmd, GITS_CMD_INVALL); its_encode_collection(&cmd, collection_id); its_send_cmd(cmdq_base, &cmd); } #define SYZOS_NUM_IDBITS 16 GUEST_CODE static void its_send_sync_cmd(uint64_t cmdq_base, uint32_t vcpu_id) { struct its_cmd_block cmd; guest_memzero(&cmd, sizeof(cmd)); its_encode_cmd(&cmd, GITS_CMD_SYNC); its_encode_target(&cmd, vcpu_id); its_send_cmd(cmdq_base, &cmd); } GUEST_CODE static noinline void guest_handle_its_send_cmd(struct api_call_its_send_cmd* cmd) { volatile uint8_t type = cmd->type; if (type == GITS_CMD_MAPD) { uint64_t itt_base = ARM64_ADDR_ITS_ITT_TABLES + cmd->devid * SZ_64K; its_send_mapd_cmd(ARM64_ADDR_ITS_CMDQ_BASE, cmd->devid, itt_base, SYZOS_NUM_IDBITS, cmd->valid); return; } if (type == GITS_CMD_MAPC) { its_send_mapc_cmd(ARM64_ADDR_ITS_CMDQ_BASE, cmd->cpuid, cmd->cpuid, cmd->valid); return; } if (type == GITS_CMD_MAPTI) { its_send_mapti_cmd(ARM64_ADDR_ITS_CMDQ_BASE, cmd->devid, cmd->eventid, cmd->cpuid, cmd->intid); return; } if (type == GITS_CMD_MAPI || type == GITS_CMD_MOVI) { its_send_devid_eventid_icid_cmd(ARM64_ADDR_ITS_CMDQ_BASE, type, cmd->devid, cmd->eventid, cmd->intid); return; } if (type == GITS_CMD_MOVALL) { its_send_movall_cmd(ARM64_ADDR_ITS_CMDQ_BASE, cmd->cpuid, cmd->cpuid2); return; } if (type == GITS_CMD_INVALL) { its_send_invall_cmd(ARM64_ADDR_ITS_CMDQ_BASE, cmd->cpuid); return; } if (type == GITS_CMD_INT || type == GITS_CMD_INV || type == GITS_CMD_DISCARD || type == GITS_CMD_CLEAR) { its_send_devid_eventid_cmd(ARM64_ADDR_ITS_CMDQ_BASE, type, cmd->devid, cmd->eventid); return; } if (type == GITS_CMD_SYNC) { its_send_sync_cmd(ARM64_ADDR_ITS_CMDQ_BASE, cmd->cpuid); return; } } GUEST_CODE static noinline void guest_setup_its_mappings(uint64_t cmdq_base, uint64_t itt_tables, uint32_t nr_events, uint32_t nr_devices, uint32_t nr_cpus) { if ((nr_events < 1) || (nr_devices < 1) || (nr_cpus < 1)) return; uint32_t coll_id, device_id, event_id, intid = GIC_LPI_OFFSET; for (coll_id = 0; coll_id < nr_cpus; coll_id++) { its_send_mapc_cmd(cmdq_base, coll_id, coll_id, true); } coll_id = 0; for (device_id = 0; device_id < nr_devices; device_id++) { uint64_t itt_base = itt_tables + (device_id * SZ_64K); its_send_mapd_cmd(cmdq_base, device_id, itt_base, SYZOS_NUM_IDBITS, true); for (event_id = 0; event_id < nr_events; event_id++) { its_send_mapti_cmd(cmdq_base, device_id, event_id, coll_id, intid++); coll_id = (coll_id + 1) % nr_cpus; } } } GUEST_CODE static void guest_invalidate_all_rdists(uint64_t cmdq_base, int nr_cpus) { for (int i = 0; i < nr_cpus; i++) its_send_invall_cmd(cmdq_base, i); } void gic_rdist_enable_lpis(uint64_t cfg_table, size_t cfg_table_size, uint64_t pend_table) { uint64_t rdist_base = gicr_base_cpu(get_cpu_id()); uint64_t val = (cfg_table | GICR_PROPBASER_InnerShareable | GICR_PROPBASER_RaWaWb | ((SYZOS_NUM_IDBITS - 1) & GICR_PROPBASER_IDBITS_MASK)); writeq(val, rdist_base + GICR_PROPBASER); val = (pend_table | GICR_PENDBASER_InnerShareable | GICR_PENDBASER_RaWaWb); writeq(val, rdist_base + GICR_PENDBASER); uint64_t ctlr = readl(rdist_base + GICR_CTLR); ctlr |= GICR_CTLR_ENABLE_LPIS; writel(ctlr, rdist_base + GICR_CTLR); } #define LPI_PROP_DEFAULT_PRIO 0xa0 #define LPI_PROP_GROUP1 (1 << 1) #define LPI_PROP_ENABLED (1 << 0) GUEST_CODE static noinline void configure_lpis(uint64_t prop_table, int nr_devices, int nr_events) { int nr_lpis = nr_devices * nr_events; volatile uint8_t* tbl = (uint8_t*)prop_table; for (int i = 0; i < nr_lpis; i++) { tbl[i] = LPI_PROP_DEFAULT_PRIO | LPI_PROP_GROUP1 | LPI_PROP_ENABLED; } } GUEST_CODE static void guest_prepare_its(int nr_cpus, int nr_devices, int nr_events) { configure_lpis(ARM64_ADDR_ITS_PROP_TABLE, nr_devices, nr_events); gic_rdist_enable_lpis(ARM64_ADDR_ITS_PROP_TABLE, SZ_64K, ARM64_ADDR_ITS_PEND_TABLES); its_init(ARM64_ADDR_ITS_COLL_TABLE, ARM64_ADDR_ITS_DEVICE_TABLE, ARM64_ADDR_ITS_CMDQ_BASE); guest_setup_its_mappings(ARM64_ADDR_ITS_CMDQ_BASE, ARM64_ADDR_ITS_ITT_TABLES, nr_events, nr_devices, nr_cpus); guest_invalidate_all_rdists(ARM64_ADDR_ITS_CMDQ_BASE, nr_cpus); } #define KVM_ARM64_REGS_X0 0x6030000000100000UL #define KVM_ARM64_REGS_X1 0x6030000000100002UL #define KVM_ARM64_REGS_PC 0x6030000000100040UL #define KVM_ARM64_REGS_SP_EL1 0x6030000000100044UL #define KVM_ARM64_REGS_TPIDR_EL1 0x603000000013c684 struct kvm_text { uintptr_t typ; const void* text; uintptr_t size; }; struct kvm_opt { uint64_t typ; uint64_t val; }; struct addr_size { void* addr; size_t size; }; static struct addr_size alloc_guest_mem(struct addr_size* free, size_t size) { struct addr_size ret = {.addr = NULL, .size = 0}; if (free->size < size) return ret; ret.addr = free->addr; ret.size = size; free->addr = (void*)((char*)free->addr + size); free->size -= size; return ret; } static void vm_set_user_memory_region(int vmfd, uint32_t slot, uint32_t flags, uint64_t guest_phys_addr, uint64_t memory_size, uint64_t userspace_addr) { struct kvm_userspace_memory_region memreg; memreg.slot = slot; memreg.flags = flags; memreg.guest_phys_addr = guest_phys_addr; memreg.memory_size = memory_size; memreg.userspace_addr = userspace_addr; ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, &memreg); } #define ADRP_OPCODE 0x90000000 #define ADRP_OPCODE_MASK 0x9f000000 static void validate_guest_code(void* mem, size_t size) { uint32_t* insns = (uint32_t*)mem; for (size_t i = 0; i < size / 4; i++) { if ((insns[i] & ADRP_OPCODE_MASK) == ADRP_OPCODE) exit(1); } } static void install_syzos_code(void* host_mem, size_t mem_size) { size_t size = (char*)&__stop_guest - (char*)&__start_guest; if (size > mem_size) exit(1); memcpy(host_mem, &__start_guest, size); validate_guest_code(host_mem, size); } static void setup_vm(int vmfd, void* host_mem, void** text_slot) { struct addr_size allocator = {.addr = host_mem, .size = KVM_GUEST_MEM_SIZE}; int slot = 0; struct addr_size host_text = alloc_guest_mem(&allocator, 4 * KVM_PAGE_SIZE); install_syzos_code(host_text.addr, host_text.size); vm_set_user_memory_region(vmfd, slot++, KVM_MEM_READONLY, ARM64_ADDR_EXECUTOR_CODE, host_text.size, (uintptr_t)host_text.addr); struct addr_size next = alloc_guest_mem(&allocator, 2 * KVM_PAGE_SIZE); vm_set_user_memory_region(vmfd, slot++, KVM_MEM_LOG_DIRTY_PAGES, ARM64_ADDR_DIRTY_PAGES, next.size, (uintptr_t)next.addr); next = alloc_guest_mem(&allocator, KVM_MAX_VCPU * KVM_PAGE_SIZE); vm_set_user_memory_region(vmfd, slot++, KVM_MEM_READONLY, ARM64_ADDR_USER_CODE, next.size, (uintptr_t)next.addr); if (text_slot) *text_slot = next.addr; next = alloc_guest_mem(&allocator, KVM_PAGE_SIZE); vm_set_user_memory_region(vmfd, slot++, 0, ARM64_ADDR_EL1_STACK_BOTTOM, next.size, (uintptr_t)next.addr); next = alloc_guest_mem(&allocator, KVM_PAGE_SIZE); vm_set_user_memory_region(vmfd, slot++, 0, ARM64_ADDR_SCRATCH_CODE, next.size, (uintptr_t)next.addr); int its_size = SZ_64K * (4 + 4 + 16); next = alloc_guest_mem(&allocator, its_size); vm_set_user_memory_region(vmfd, slot++, 0, ARM64_ADDR_ITS_TABLES, next.size, (uintptr_t)next.addr); next = alloc_guest_mem(&allocator, allocator.size); vm_set_user_memory_region(vmfd, slot++, 0, 0, next.size, (uintptr_t)next.addr); } static void vcpu_set_reg(int vcpu_fd, uint64_t id, uint64_t val) { struct kvm_one_reg reg = {.id = id, .addr = (uint64_t)&val}; ioctl(vcpu_fd, KVM_SET_ONE_REG, ®); } static void reset_cpu_regs(int cpufd, int cpu_id, size_t text_size) { vcpu_set_reg(cpufd, KVM_ARM64_REGS_PC, ARM64_ADDR_EXECUTOR_CODE + ((uint64_t)guest_main - (uint64_t)&__start_guest)); vcpu_set_reg(cpufd, KVM_ARM64_REGS_SP_EL1, ARM64_ADDR_EL1_STACK_BOTTOM + KVM_PAGE_SIZE - 128); vcpu_set_reg(cpufd, KVM_ARM64_REGS_TPIDR_EL1, cpu_id); vcpu_set_reg(cpufd, KVM_ARM64_REGS_X0, text_size); vcpu_set_reg(cpufd, KVM_ARM64_REGS_X1, cpu_id); } static void install_user_code(int cpufd, void* user_text_slot, int cpu_id, const void* text, size_t text_size) { if ((cpu_id < 0) || (cpu_id >= KVM_MAX_VCPU)) return; if (!user_text_slot) return; if (text_size > KVM_PAGE_SIZE) text_size = KVM_PAGE_SIZE; void* target = (void*)((uint64_t)user_text_slot + (KVM_PAGE_SIZE * cpu_id)); memcpy(target, text, text_size); reset_cpu_regs(cpufd, cpu_id, text_size); } static void setup_cpu_with_opts(int vmfd, int cpufd, const struct kvm_opt* opt, int opt_count) { uint32_t features = 0; if (opt_count > 1) opt_count = 1; for (int i = 0; i < opt_count; i++) { uint64_t typ = opt[i].typ; uint64_t val = opt[i].val; switch (typ) { case 1: features = val; break; } } struct kvm_vcpu_init init; ioctl(vmfd, KVM_ARM_PREFERRED_TARGET, &init); init.features[0] = features; ioctl(cpufd, KVM_ARM_VCPU_INIT, &init); } struct kvm_syz_vm { int vmfd; int next_cpu_id; void* user_text; }; static long syz_kvm_setup_syzos_vm(volatile long a0, volatile long a1) { const int vmfd = a0; void* host_mem = (void*)a1; void* user_text_slot = NULL; struct kvm_syz_vm* ret = (struct kvm_syz_vm*)host_mem; host_mem = (void*)((uint64_t)host_mem + KVM_PAGE_SIZE); setup_vm(vmfd, host_mem, &user_text_slot); ret->vmfd = vmfd; ret->next_cpu_id = 0; ret->user_text = user_text_slot; return (long)ret; } static long syz_kvm_add_vcpu(volatile long a0, volatile long a1, volatile long a2, volatile long a3) { struct kvm_syz_vm* vm = (struct kvm_syz_vm*)a0; struct kvm_text* utext = (struct kvm_text*)a1; const void* text = utext->text; size_t text_size = utext->size; const struct kvm_opt* const opt_array_ptr = (struct kvm_opt*)a2; uintptr_t opt_count = a3; if (!vm) { errno = EINVAL; return -1; } if (vm->next_cpu_id == KVM_MAX_VCPU) { errno = ENOMEM; return -1; } int cpu_id = vm->next_cpu_id; int cpufd = ioctl(vm->vmfd, KVM_CREATE_VCPU, cpu_id); if (cpufd == -1) return -1; vm->next_cpu_id++; setup_cpu_with_opts(vm->vmfd, cpufd, opt_array_ptr, opt_count); install_user_code(cpufd, vm->user_text, cpu_id, text, text_size); return cpufd; } static int kvm_set_device_attr(int dev_fd, uint32_t group, uint64_t attr, void* val) { struct kvm_device_attr kvmattr = { .flags = 0, .group = group, .attr = attr, .addr = (uintptr_t)val, }; return ioctl(dev_fd, KVM_SET_DEVICE_ATTR, &kvmattr); } static int kvm_create_device(int vm_fd, int type) { struct kvm_create_device create_dev = { .type = (uint32_t)type, .fd = (uint32_t)-1, .flags = 0, }; if (ioctl(vm_fd, KVM_CREATE_DEVICE, &create_dev) != -1) return create_dev.fd; else return -1; } #define REDIST_REGION_ATTR_ADDR(count, base, flags, index) \ (((uint64_t)(count) << 52) | ((uint64_t)((base) >> 16) << 16) | \ ((uint64_t)(flags) << 12) | index) static long syz_kvm_vgic_v3_setup(volatile long a0, volatile long a1, volatile long a2) { const int vm_fd = a0; const int nr_vcpus = a1; const int want_nr_irq = a2; int vgic_fd = kvm_create_device(vm_fd, KVM_DEV_TYPE_ARM_VGIC_V3); if (vgic_fd == -1) return -1; uint32_t nr_irq = want_nr_irq; int ret = kvm_set_device_attr(vgic_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0, &nr_irq); if (ret == -1) { close(vgic_fd); return -1; } uint64_t gicd_base_gpa = ARM64_ADDR_GICD_BASE; ret = kvm_set_device_attr(vgic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, KVM_VGIC_V3_ADDR_TYPE_DIST, &gicd_base_gpa); if (ret == -1) { close(vgic_fd); return -1; } uint64_t redist_attr = REDIST_REGION_ATTR_ADDR(nr_vcpus, ARM64_ADDR_GICR_BASE, 0, 0); ret = kvm_set_device_attr(vgic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &redist_attr); if (ret == -1) { close(vgic_fd); return -1; } ret = kvm_set_device_attr(vgic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, KVM_DEV_ARM_VGIC_CTRL_INIT, NULL); if (ret == -1) { close(vgic_fd); return -1; } return vgic_fd; } static void kill_and_wait(int pid, int* status) { kill(-pid, SIGKILL); kill(pid, SIGKILL); for (int i = 0; i < 100; i++) { if (waitpid(-1, status, WNOHANG | __WALL) == pid) return; usleep(1000); } DIR* dir = opendir("/sys/fs/fuse/connections"); if (dir) { for (;;) { struct dirent* ent = readdir(dir); if (!ent) break; if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) continue; char abort[300]; snprintf(abort, sizeof(abort), "/sys/fs/fuse/connections/%s/abort", ent->d_name); int fd = open(abort, O_WRONLY); if (fd == -1) { continue; } if (write(fd, abort, 1) < 0) { } close(fd); } closedir(dir); } else { } while (waitpid(-1, status, __WALL) != pid) { } } static void setup_test() { prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); setpgrp(); write_file("/proc/self/oom_score_adj", "1000"); } struct thread_t { int created, call; event_t ready, done; }; static struct thread_t threads[16]; static void execute_call(int call); static int running; static void* thr(void* arg) { struct thread_t* th = (struct thread_t*)arg; for (;;) { event_wait(&th->ready); event_reset(&th->ready); execute_call(th->call); __atomic_fetch_sub(&running, 1, __ATOMIC_RELAXED); event_set(&th->done); } return 0; } static void execute_one(void) { if (write(1, "executing program\n", sizeof("executing program\n") - 1)) { } int i, call, thread; for (call = 0; call < 10; call++) { for (thread = 0; thread < (int)(sizeof(threads) / sizeof(threads[0])); thread++) { struct thread_t* th = &threads[thread]; if (!th->created) { th->created = 1; event_init(&th->ready); event_init(&th->done); event_set(&th->done); thread_start(thr, th); } if (!event_isset(&th->done)) continue; event_reset(&th->done); th->call = call; __atomic_fetch_add(&running, 1, __ATOMIC_RELAXED); event_set(&th->ready); event_timedwait(&th->done, 500); break; } } for (i = 0; i < 100 && __atomic_load_n(&running, __ATOMIC_RELAXED); i++) sleep_ms(1); } static void execute_one(void); #define WAIT_FLAGS __WALL static void loop(void) { int iter = 0; for (;; iter++) { int pid = fork(); if (pid < 0) exit(1); if (pid == 0) { setup_test(); execute_one(); exit(0); } int status = 0; uint64_t start = current_time_ms(); for (;;) { sleep_ms(10); if (waitpid(-1, &status, WNOHANG | WAIT_FLAGS) == pid) break; if (current_time_ms() - start < 15000) continue; kill_and_wait(pid, &status); break; } } } uint64_t r[5] = {0xffffffffffffffff, 0xffffffffffffffff, 0x0, 0xffffffffffffffff, 0xffffffffffffffff}; void execute_call(int call) { intptr_t res = 0; switch (call) { case 0: memcpy((void*)0x20000040, "/dev/kvm\000", 9); res = syscall(__NR_openat, /*fd=*/0ul, /*file=*/0x20000040ul, /*flags=*/0, /*mode=*/0); if (res != -1) r[0] = res; break; case 1: res = syscall(__NR_ioctl, /*fd=*/r[0], /*cmd=*/0xae01, /*type=*/0ul); if (res != -1) r[1] = res; break; case 2: res = -1; res = syz_kvm_setup_syzos_vm(/*fd=*/r[1], /*usermem=*/0x20c00000); if (res != -1) r[2] = res; break; case 3: *(uint64_t*)0x20000080 = 0; *(uint64_t*)0x20000088 = 0x200000c0; *(uint64_t*)0x200000c0 = 7; *(uint64_t*)0x200000c8 = 0x28; *(uint64_t*)0x200000d0 = 2; *(uint64_t*)0x200000d8 = 2; *(uint64_t*)0x200000e0 = 1; *(uint64_t*)0x20000090 = 0x28; res = -1; res = syz_kvm_add_vcpu(/*vm=*/r[2], /*text=*/0x20000080, /*opts=*/0, /*nopt=*/0); if (res != -1) r[3] = res; break; case 4: syz_kvm_vgic_v3_setup(/*fd=*/r[1], /*ncpus=*/3, /*nirqs=*/0xa0); break; case 5: *(uint32_t*)0x20000100 = 8; *(uint32_t*)0x20000108 = 0; res = syscall(__NR_ioctl, /*fd=*/r[1], /*cmd=*/0xc00caee0, /*arg=*/0x20000100ul); if (res != -1) r[4] = *(uint32_t*)0x20000104; break; case 6: *(uint32_t*)0x20000000 = 0; *(uint32_t*)0x20000004 = 0; *(uint64_t*)0x20000008 = 4; *(uint64_t*)0x20000010 = 0x20000180; *(uint64_t*)0x20000180 = 0x8080000; syscall(__NR_ioctl, /*fd=*/r[4], /*cmd=*/0x4018aee1, /*arg=*/0x20000000ul); break; case 7: syscall(__NR_ioctl, /*fd=*/r[3], /*cmd=*/0xae80, /*arg=*/0ul); break; case 8: *(uint32_t*)0x20000300 = 0; *(uint32_t*)0x20000304 = 4; *(uint64_t*)0x20000308 = 1; *(uint64_t*)0x20000310 = 0; syscall(__NR_ioctl, /*fd=*/r[4], /*cmd=*/0x4018aee1, /*arg=*/0x20000300ul); for (int i = 0; i < 64; i++) { syscall(__NR_ioctl, /*fd=*/r[4], /*cmd=*/0x4018aee1, /*arg=*/0x20000300ul); } break; case 9: *(uint32_t*)0x200000c0 = 0; *(uint32_t*)0x200000c4 = 4; *(uint64_t*)0x200000c8 = 2; *(uint64_t*)0x200000d0 = 0; syscall(__NR_ioctl, /*fd=*/r[4], /*cmd=*/0x4018aee1, /*arg=*/0x200000c0ul); break; } } int main(void) { syscall(__NR_mmap, /*addr=*/0x1ffff000ul, /*len=*/0x1000ul, /*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/ 0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul); syscall(__NR_mmap, /*addr=*/0x20000000ul, /*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=*/0x21000000ul, /*len=*/0x1000ul, /*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/ 0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul); const char* reason; (void)reason; for (procid = 0; procid < 2; procid++) { if (fork() == 0) { loop(); } } sleep(1000000); return 0; }