github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/executor/test_linux.h (about) 1 // Copyright 2017 syzkaller project authors. All rights reserved. 2 // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4 #include <stdint.h> 5 #include <sys/utsname.h> 6 7 static unsigned host_kernel_version(); 8 static void dump_cpu_state(int cpufd, char* vm_mem); 9 10 #ifdef GOARCH_amd64 11 static int cpu_feature_enabled(uint32_t function, uint32_t eax_bits, uint32_t ebx_bits, uint32_t ecx_bits, uint32_t edx_bits); 12 13 #define FEATURE_INTEL 0x00000001 14 #define FEATURE_INTEL_ECX_VMX (1 << 5) 15 16 #define FEATURE_AMD 0x800000001 17 #define FEATURE_AMD_ECX_SVM (1 << 2) 18 #endif 19 20 static int test_one(int text_type, const char* text, int text_size, int flags, unsigned reason, bool check_rax) 21 { 22 printf("=== testing text %d, text size 0x%x, flags 0x%x\n", text_type, text_size, flags); 23 int kvmfd = open("/dev/kvm", O_RDWR); 24 if (kvmfd == -1) { 25 if (errno == ENOENT) { 26 printf("/dev/kvm is not present\n"); 27 return -1; 28 } 29 if (errno == EPERM || errno == EACCES) { 30 printf("no permissions to open /dev/kvm\n"); 31 return -1; 32 } 33 printf("failed to open /dev/kvm (%d)\n", errno); 34 return 1; 35 } 36 int vmfd = ioctl(kvmfd, KVM_CREATE_VM, 0); 37 if (vmfd == -1) { 38 printf("KVM_CREATE_VM failed (%d)\n", errno); 39 return 1; 40 } 41 int cpufd = ioctl(vmfd, KVM_CREATE_VCPU, 0); 42 if (cpufd == -1) { 43 printf("KVM_CREATE_VCPU failed (%d)\n", errno); 44 return 1; 45 } 46 int cpu_mem_size = ioctl(kvmfd, KVM_GET_VCPU_MMAP_SIZE, 0); 47 if (cpu_mem_size <= 0) { 48 printf("KVM_GET_VCPU_MMAP_SIZE failed (%d)\n", errno); 49 return 1; 50 } 51 struct kvm_run* cpu_mem = (struct kvm_run*)mmap(0, cpu_mem_size, 52 PROT_READ | PROT_WRITE, MAP_SHARED, cpufd, 0); 53 if (cpu_mem == MAP_FAILED) { 54 printf("cpu mmap failed (%d)\n", errno); 55 return 1; 56 } 57 int vm_mem_size = 24 * SYZ_PAGE_SIZE; // Allocate what executor allocates for vma[24] 58 void* vm_mem = mmap(0, vm_mem_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 59 if (vm_mem == MAP_FAILED) { 60 printf("mmap failed (%d)\n", errno); 61 return 1; 62 } 63 struct kvm_text kvm_text; 64 kvm_text.typ = text_type; 65 kvm_text.text = text; 66 kvm_text.size = text_size; 67 if (syz_kvm_setup_cpu(vmfd, cpufd, (uintptr_t)vm_mem, (uintptr_t)&kvm_text, 1, flags, 0, 0)) { 68 printf("syz_kvm_setup_cpu failed (%d)\n", errno); 69 return 1; 70 } 71 72 int ret = ioctl(cpufd, KVM_RUN, 0); 73 // KVM_RUN returns positive values on PPC64 74 if (ret < 0) { 75 printf("KVM_RUN returned %d, errno=%d\n", ret, errno); 76 return 1; 77 } 78 struct kvm_regs regs; 79 if (ioctl(cpufd, KVM_GET_REGS, ®s)) { 80 printf("KVM_GET_REGS failed (%d)\n", errno); 81 dump_cpu_state(cpufd, (char*)vm_mem); 82 return 1; 83 } 84 if (cpu_mem->exit_reason != reason) { 85 printf("KVM_RUN exit reason %d, expect %d\n", cpu_mem->exit_reason, reason); 86 if (cpu_mem->exit_reason == KVM_EXIT_FAIL_ENTRY) 87 printf("hardware exit reason 0x%llx\n", 88 (unsigned long long)cpu_mem->fail_entry.hardware_entry_failure_reason); 89 dump_cpu_state(cpufd, (char*)vm_mem); 90 return 1; 91 } 92 #ifdef GOARCH_amd64 93 if (check_rax && regs.rax != 0xbadc0de) { 94 printf("wrong result: rax=0x%llx\n", (long long)regs.rax); 95 dump_cpu_state(cpufd, (char*)vm_mem); 96 return 1; 97 } 98 #elif GOARCH_ppc64le 99 if (check_rax && regs.gpr[3] != 0xbadc0de) { 100 printf("wrong result: gps[3]=0x%llx\n", (long long)regs.gpr[3]); 101 dump_cpu_state(cpufd, (char*)vm_mem); 102 return 1; 103 } 104 #endif 105 munmap(vm_mem, vm_mem_size); 106 munmap(cpu_mem, cpu_mem_size); 107 close(cpufd); 108 close(vmfd); 109 close(kvmfd); 110 return 0; 111 } 112 113 static int test_kvm() 114 { 115 int res = 0; 116 117 unsigned ver = host_kernel_version(); 118 printf("host kernel version %u\n", ver); 119 120 // TODO: test VM mode. 121 // const char text16_vm[] = "\x48\xc7\xc3\xde\xc0\xad\x0b\x90\x90\x48\xc7\xc0\xef\xcd\xab\x00\xf4"; 122 // if (res = test_one(64, text16_vm, sizeof(text16_vm) - 1, KVM_SETUP_VM, KVM_EXIT_HLT, true)) 123 // return res; 124 125 // TODO: test code executed in interrupt handlers. 126 // const char text32_div0[] = "\x31\xc0\xf7\xf0"; 127 // if (res = test_one(32, text32_div0, sizeof(text32_div0)-1, 0, KVM_EXIT_HLT, true)) 128 // return res; 129 130 #ifdef GOARCH_amd64 131 // Note: VIRT86 and CPL3 prefix seems to work with vmx only 132 if (cpu_feature_enabled(FEATURE_INTEL, 0, 0, FEATURE_INTEL_ECX_VMX, 0) == 1) { 133 const char text8[] = "\x66\xb8\xde\xc0\xad\x0b"; 134 if ((res = test_one(8, text8, sizeof(text8) - 1, 0, KVM_EXIT_HLT, true))) 135 return res; 136 if ((res = test_one(8, text8, sizeof(text8) - 1, KVM_SETUP_VIRT86, KVM_EXIT_SHUTDOWN, true))) 137 return res; 138 if ((res = test_one(8, text8, sizeof(text8) - 1, KVM_SETUP_VIRT86 | KVM_SETUP_PAGING, KVM_EXIT_SHUTDOWN, true))) 139 return res; 140 141 const char text16[] = "\x66\xb8\xde\xc0\xad\x0b"; 142 if ((res = test_one(16, text16, sizeof(text16) - 1, 0, KVM_EXIT_HLT, true))) 143 return res; 144 if ((res = test_one(16, text16, sizeof(text16) - 1, KVM_SETUP_CPL3, KVM_EXIT_SHUTDOWN, true))) 145 return res; 146 147 const char text32[] = "\xb8\xde\xc0\xad\x0b"; 148 if ((res = test_one(32, text32, sizeof(text32) - 1, 0, KVM_EXIT_HLT, true))) 149 return res; 150 if ((res = test_one(32, text32, sizeof(text32) - 1, KVM_SETUP_PAGING, KVM_EXIT_HLT, true))) 151 return res; 152 if ((res = test_one(32, text32, sizeof(text32) - 1, KVM_SETUP_CPL3, KVM_EXIT_SHUTDOWN, true))) 153 return res; 154 155 const char text64[] = "\x90\xb8\xde\xc0\xad\x0b"; 156 if ((res = test_one(64, text64, sizeof(text64) - 1, 0, KVM_EXIT_HLT, true))) 157 return res; 158 if ((res = test_one(64, text64, sizeof(text64) - 1, KVM_SETUP_PAGING, KVM_EXIT_HLT, true))) 159 return res; 160 if ((res = test_one(64, text64, sizeof(text64) - 1, KVM_SETUP_CPL3, KVM_EXIT_SHUTDOWN, true))) 161 return res; 162 163 const char text64_sysenter[] = "\xb8\xde\xc0\xad\x0b\x0f\x34"; 164 if ((res = test_one(64, text64_sysenter, sizeof(text64_sysenter) - 1, KVM_SETUP_CPL3, KVM_EXIT_SHUTDOWN, true))) 165 return res; 166 } 167 168 // Note: SMM does not work on 3.13 kernels. 169 if (ver >= 404) { 170 const char text8_smm[] = "\x66\xb8\xde\xc0\xad\x0b"; 171 if ((res = test_one(8, text8_smm, sizeof(text8_smm) - 1, KVM_SETUP_SMM, KVM_EXIT_HLT, true))) 172 return res; 173 if ((res = test_one(8, text8_smm, sizeof(text8_smm) - 1, KVM_SETUP_SMM | KVM_SETUP_PROTECTED, KVM_EXIT_HLT, true))) 174 return res; 175 176 // const char text32_smm[] = "\xb8\xde\xc0\xad\x0b"; 177 if ((res = test_one(32, text8_smm, sizeof(text8_smm) - 1, KVM_SETUP_SMM, KVM_EXIT_HLT, true))) 178 return res; 179 180 // Also ensure that we are actually in SMM. 181 // If we do MOV to RAX and then RSM, RAX will be restored to host value so RAX check will fail. 182 // So instead we execute just RSM, if we are in SMM we will get KVM_EXIT_HLT, 183 // otherwise KVM_EXIT_INTERNAL_ERROR. 184 const char text_rsm[] = "\x0f\xaa"; 185 if ((res = test_one(8, text_rsm, sizeof(text_rsm) - 1, KVM_SETUP_SMM, KVM_EXIT_HLT, false))) 186 return res; 187 if ((res = test_one(32, text_rsm, sizeof(text_rsm) - 1, KVM_SETUP_SMM, KVM_EXIT_HLT, false))) 188 return res; 189 } 190 #elif GOARCH_ppc64le 191 for (unsigned i = 0; i < (1 << 5); ++i) { 192 res = test_one(8, kvm_ppc64_mr, sizeof(kvm_ppc64_mr) - 1, i, KVM_EXIT_DEBUG, true); 193 if (res) 194 return res; 195 res = test_one(8, kvm_ppc64_ld, sizeof(kvm_ppc64_ld) - 1, i, KVM_EXIT_DEBUG, true); 196 if (res) 197 return res; 198 } 199 #else 200 // Keeping gcc happy 201 const char text8[] = "\x66\xb8\xde\xc0\xad\x0b"; 202 if ((res = test_one(8, text8, sizeof(text8) - 1, 0, KVM_EXIT_HLT, true))) 203 return res; 204 #endif 205 206 return 0; 207 } 208 209 static unsigned host_kernel_version() 210 { 211 struct utsname name; 212 if (uname(&name)) { 213 printf("uname failed (%d)\n", errno); 214 doexit(1); 215 } 216 unsigned major = atoi(name.release); 217 unsigned minor = 0; 218 if (strchr(name.release, '.')) 219 minor = atoi(strchr(name.release, '.') + 1); 220 return major * 100 + minor; 221 } 222 223 #ifdef GOARCH_amd64 224 static void dump_seg(const char* name, struct kvm_segment* seg) 225 { 226 printf("%s: base=0x%llx limit=0x%x sel=0x%x type=%d p=%d dpl=%d, db=%d s=%d l=%d g=%d\n", 227 name, seg->base, seg->limit, seg->selector, seg->type, seg->present, seg->dpl, seg->db, seg->s, seg->l, seg->g); 228 } 229 #endif 230 231 static void dump_cpu_state(int cpufd, char* vm_mem) 232 { 233 struct kvm_sregs sregs; 234 if (ioctl(cpufd, KVM_GET_SREGS, &sregs)) { 235 printf("KVM_GET_SREGS failed (%d)\n", errno); 236 return; 237 } 238 struct kvm_regs regs; 239 if (ioctl(cpufd, KVM_GET_REGS, ®s)) { 240 printf("KVM_GET_REGS failed (%d)\n", errno); 241 return; 242 } 243 #ifdef GOARCH_amd64 244 printf("RIP=0x%llx RAX=0x%llx RDX=0x%llx RCX=0x%llx RBX=0x%llx CF=%d ZF=%d\n", 245 regs.rip, regs.rax, regs.rdx, regs.rcx, regs.rbx, !!(regs.rflags & (1 << 0)), !!(regs.rflags & (1 << 6))); 246 printf("CR0=0x%llx CR2=0x%llx CR4=0x%llx EFER=0x%llx\n", 247 sregs.cr0, sregs.cr2, sregs.cr4, sregs.efer); 248 dump_seg("CS", &sregs.cs); 249 dump_seg("SS", &sregs.ss); 250 dump_seg("DS", &sregs.ds); 251 252 if (false) { 253 printf("memory:\n"); 254 for (int i = 0; i < 0x80; i++) 255 printf("0x%02x: 0x%02x\n", i, ((unsigned char*)vm_mem)[i]); 256 } 257 258 if (false) { 259 printf("vmcs:\n"); 260 const int vmcs_size = 0x1000; 261 for (int i = 0; i < vmcs_size / 8; i += 4) { 262 printf("0x%04x: 0x%016llx 0x%016llx 0x%016llx 0x%016llx\n", i, 263 ((long long*)vm_mem)[i], ((long long*)vm_mem)[i + 1], ((long long*)vm_mem)[i + 2], ((long long*)vm_mem)[i + 3]); 264 } 265 } 266 #elif GOARCH_ppc64 || GOARCH_ppc64le 267 printf("NIP %016lx\n", regs.pc); 268 printf("MSR %016lx\n", regs.msr); 269 printf("GPR00 %016lx %016lx %016lx %016lx\n", regs.gpr[0], regs.gpr[1], regs.gpr[2], regs.gpr[3]); 270 printf("GPR04 %016lx %016lx %016lx %016lx\n", regs.gpr[4], regs.gpr[5], regs.gpr[6], regs.gpr[7]); 271 printf("GPR08 %016lx %016lx %016lx %016lx\n", regs.gpr[8], regs.gpr[9], regs.gpr[10], regs.gpr[11]); 272 printf("GPR12 %016lx %016lx %016lx %016lx\n", regs.gpr[12], regs.gpr[13], regs.gpr[14], regs.gpr[15]); 273 printf("GPR16 %016lx %016lx %016lx %016lx\n", regs.gpr[16], regs.gpr[17], regs.gpr[18], regs.gpr[19]); 274 printf("GPR20 %016lx %016lx %016lx %016lx\n", regs.gpr[20], regs.gpr[21], regs.gpr[22], regs.gpr[23]); 275 printf("GPR24 %016lx %016lx %016lx %016lx\n", regs.gpr[24], regs.gpr[25], regs.gpr[26], regs.gpr[27]); 276 printf("GPR28 %016lx %016lx %016lx %016lx\n", regs.gpr[28], regs.gpr[29], regs.gpr[30], regs.gpr[31]); 277 printf(" SRR0 %016lx SRR1 %016lx\n", regs.srr0, regs.srr1); 278 #endif 279 } 280 281 #ifdef GOARCH_amd64 282 // retcodes: 283 // 0 : feature disabled 284 // 1 : feature enabled 285 // -1 : error getting feature state 286 static int cpu_feature_enabled(uint32_t function, uint32_t eax_bits, uint32_t ebx_bits, uint32_t ecx_bits, uint32_t edx_bits) 287 { 288 int kvmfd = open("/dev/kvm", O_RDWR); 289 if (kvmfd == -1) { 290 printf("failed to open /dev/kvm (%d)\n", errno); 291 return -1; 292 } 293 char buf[sizeof(struct kvm_cpuid2) + 128 * sizeof(struct kvm_cpuid_entry2)]; 294 memset(buf, 0, sizeof(buf)); 295 struct kvm_cpuid2* cpuid = (struct kvm_cpuid2*)buf; 296 cpuid->nent = 128; 297 ioctl(kvmfd, KVM_GET_SUPPORTED_CPUID, cpuid); 298 close(kvmfd); 299 for (uint32_t i = 0; i < cpuid->nent; i++) { 300 struct kvm_cpuid_entry2* entry = &cpuid->entries[i]; 301 if (entry->function == function && (!eax_bits || (entry->eax & eax_bits)) && (!ebx_bits || (entry->ebx & ebx_bits)) && (!ecx_bits || (entry->ecx & ecx_bits)) && (!edx_bits || (entry->edx & edx_bits))) 302 return 1; 303 } 304 return 0; 305 } 306 #endif 307 308 #ifdef GOARCH_arm64 309 static int test_syzos() 310 { 311 int mem_size = SYZ_PAGE_SIZE * 4; 312 void* mem = mmap(0, mem_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 313 if (mem == MAP_FAILED) { 314 printf("mmap failed (%d)\n", errno); 315 return 1; 316 } 317 // Right now SyzOS testing just boils down to installing code into memory. 318 install_syzos_code(mem, mem_size); 319 munmap(mem, mem_size); 320 return 0; 321 } 322 #endif