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, &regs)) {
    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, &regs)) {
   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