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