• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <sys/utsname.h>
5 
6 static unsigned host_kernel_version();
7 static void dump_cpu_state(int cpufd, char* vm_mem);
8 
test_one(int text_type,const char * text,int text_size,int flags,unsigned reason,bool check_rax)9 static int test_one(int text_type, const char* text, int text_size, int flags, unsigned reason, bool check_rax)
10 {
11 	printf("=== testing text %d, text size 0x%x, flags 0x%x\n", text_type, text_size, flags);
12 	int kvmfd = open("/dev/kvm", O_RDWR);
13 	if (kvmfd == -1) {
14 		if (errno == ENOENT) {
15 			printf("/dev/kvm is not present\n");
16 			return -1;
17 		}
18 		if (errno == EPERM || errno == EACCES) {
19 			printf("no permissions to open /dev/kvm\n");
20 			return -1;
21 		}
22 		printf("failed to open /dev/kvm (%d)\n", errno);
23 		return 1;
24 	}
25 	int vmfd = ioctl(kvmfd, KVM_CREATE_VM, 0);
26 	if (vmfd == -1) {
27 		printf("KVM_CREATE_VM failed (%d)\n", errno);
28 		return 1;
29 	}
30 	int cpufd = ioctl(vmfd, KVM_CREATE_VCPU, 0);
31 	if (cpufd == -1) {
32 		printf("KVM_CREATE_VCPU failed (%d)\n", errno);
33 		return 1;
34 	}
35 	int cpu_mem_size = ioctl(kvmfd, KVM_GET_VCPU_MMAP_SIZE, 0);
36 	if (cpu_mem_size <= 0) {
37 		printf("KVM_GET_VCPU_MMAP_SIZE failed (%d)\n", errno);
38 		return 1;
39 	}
40 	struct kvm_run* cpu_mem = (struct kvm_run*)mmap(0, cpu_mem_size,
41 							PROT_READ | PROT_WRITE, MAP_SHARED, cpufd, 0);
42 	if (cpu_mem == MAP_FAILED) {
43 		printf("cpu mmap failed (%d)\n", errno);
44 		return 1;
45 	}
46 	int vm_mem_size = 96 << 10;
47 	void* vm_mem = mmap(0, vm_mem_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
48 	if (vm_mem == MAP_FAILED) {
49 		printf("mmap failed (%d)\n", errno);
50 		return 1;
51 	}
52 	struct kvm_text kvm_text;
53 	kvm_text.typ = text_type;
54 	kvm_text.text = text;
55 	kvm_text.size = text_size;
56 	if (syz_kvm_setup_cpu(vmfd, cpufd, (uintptr_t)vm_mem, (uintptr_t)&kvm_text, 1, flags, 0, 0)) {
57 		printf("syz_kvm_setup_cpu failed (%d)\n", errno);
58 		return 1;
59 	}
60 	if (ioctl(cpufd, KVM_RUN, 0)) {
61 		printf("KVM_RUN failed (%d)\n", errno);
62 		return 1;
63 	}
64 	struct kvm_regs regs;
65 	if (ioctl(cpufd, KVM_GET_REGS, &regs)) {
66 		printf("KVM_GET_REGS failed (%d)\n", errno);
67 		dump_cpu_state(cpufd, (char*)vm_mem);
68 		return 1;
69 	}
70 	if (cpu_mem->exit_reason != reason) {
71 		printf("KVM_RUN exit reason %d, expect %d\n", cpu_mem->exit_reason, reason);
72 		if (cpu_mem->exit_reason == KVM_EXIT_FAIL_ENTRY)
73 			printf("hardware exit reason 0x%llx\n",
74 			       cpu_mem->fail_entry.hardware_entry_failure_reason);
75 		dump_cpu_state(cpufd, (char*)vm_mem);
76 		return 1;
77 	}
78 	if (check_rax && regs.rax != 0xbadc0de) {
79 		printf("wrong result: rax=0x%llx\n", (long long)regs.rax);
80 		dump_cpu_state(cpufd, (char*)vm_mem);
81 		return 1;
82 	}
83 	munmap(vm_mem, vm_mem_size);
84 	munmap(cpu_mem, cpu_mem_size);
85 	close(cpufd);
86 	close(vmfd);
87 	close(kvmfd);
88 	return 0;
89 }
90 
test_kvm()91 static int test_kvm()
92 {
93 	int res;
94 
95 	unsigned ver = host_kernel_version();
96 	printf("host kernel version %u\n", ver);
97 
98 	// TODO: test VM mode.
99 	//const char text16_vm[] = "\x48\xc7\xc3\xde\xc0\xad\x0b\x90\x90\x48\xc7\xc0\xef\xcd\xab\x00\xf4";
100 	//if (res = test_one(64, text16_vm, sizeof(text16_vm) - 1, KVM_SETUP_VM, KVM_EXIT_HLT, true))
101 	//	return res;
102 
103 	/// TODO: test code executed in interrupt handlers.
104 	//const char text32_div0[] = "\x31\xc0\xf7\xf0";
105 	//if (res = test_one(32, text32_div0, sizeof(text32_div0)-1, 0, KVM_EXIT_HLT, true))
106 	//	return res;
107 
108 	const char text8[] = "\x66\xb8\xde\xc0\xad\x0b";
109 	if ((res = test_one(8, text8, sizeof(text8) - 1, 0, KVM_EXIT_HLT, true)))
110 		return res;
111 	if ((res = test_one(8, text8, sizeof(text8) - 1, KVM_SETUP_VIRT86, KVM_EXIT_SHUTDOWN, true)))
112 		return res;
113 	if ((res = test_one(8, text8, sizeof(text8) - 1, KVM_SETUP_VIRT86 | KVM_SETUP_PAGING, KVM_EXIT_SHUTDOWN, true)))
114 		return res;
115 
116 	const char text16[] = "\x66\xb8\xde\xc0\xad\x0b";
117 	if ((res = test_one(16, text16, sizeof(text16) - 1, 0, KVM_EXIT_HLT, true)))
118 		return res;
119 	if ((res = test_one(16, text16, sizeof(text16) - 1, KVM_SETUP_CPL3, KVM_EXIT_SHUTDOWN, true)))
120 		return res;
121 
122 	const char text32[] = "\xb8\xde\xc0\xad\x0b";
123 	if ((res = test_one(32, text32, sizeof(text32) - 1, 0, KVM_EXIT_HLT, true)))
124 		return res;
125 	if ((res = test_one(32, text32, sizeof(text32) - 1, KVM_SETUP_PAGING, KVM_EXIT_HLT, true)))
126 		return res;
127 	if ((res = test_one(32, text32, sizeof(text32) - 1, KVM_SETUP_CPL3, KVM_EXIT_SHUTDOWN, true)))
128 		return res;
129 
130 	const char text64[] = "\x90\xb8\xde\xc0\xad\x0b";
131 	if ((res = test_one(64, text64, sizeof(text64) - 1, 0, KVM_EXIT_HLT, true)))
132 		return res;
133 	if ((res = test_one(64, text64, sizeof(text64) - 1, KVM_SETUP_PAGING, KVM_EXIT_HLT, true)))
134 		return res;
135 	if ((res = test_one(64, text64, sizeof(text64) - 1, KVM_SETUP_CPL3, KVM_EXIT_SHUTDOWN, true)))
136 		return res;
137 
138 	const char text64_sysenter[] = "\xb8\xde\xc0\xad\x0b\x0f\x34";
139 	if ((res = test_one(64, text64_sysenter, sizeof(text64_sysenter) - 1, KVM_SETUP_CPL3, KVM_EXIT_SHUTDOWN, true)))
140 		return res;
141 
142 	// Note: SMM does not work on 3.13 kernels.
143 	if (ver >= 404) {
144 		const char text8_smm[] = "\x66\xb8\xde\xc0\xad\x0b";
145 		if ((res = test_one(8, text8_smm, sizeof(text8_smm) - 1, KVM_SETUP_SMM, KVM_EXIT_HLT, true)))
146 			return res;
147 		if ((res = test_one(8, text8_smm, sizeof(text8_smm) - 1, KVM_SETUP_SMM | KVM_SETUP_PROTECTED, KVM_EXIT_HLT, true)))
148 			return res;
149 
150 		//const char text32_smm[] = "\xb8\xde\xc0\xad\x0b";
151 		if ((res = test_one(32, text8_smm, sizeof(text8_smm) - 1, KVM_SETUP_SMM, KVM_EXIT_HLT, true)))
152 			return res;
153 
154 		// Also ensure that we are actually in SMM.
155 		// If we do MOV to RAX and then RSM, RAX will be restored to host value so RAX check will fail.
156 		// So instead we execute just RSM, if we are in SMM we will get KVM_EXIT_HLT,
157 		// otherwise KVM_EXIT_INTERNAL_ERROR.
158 		const char text_rsm[] = "\x0f\xaa";
159 		if ((res = test_one(8, text_rsm, sizeof(text_rsm) - 1, KVM_SETUP_SMM, KVM_EXIT_HLT, false)))
160 			return res;
161 		if ((res = test_one(32, text_rsm, sizeof(text_rsm) - 1, KVM_SETUP_SMM, KVM_EXIT_HLT, false)))
162 			return res;
163 	}
164 
165 	return 0;
166 }
167 
host_kernel_version()168 static unsigned host_kernel_version()
169 {
170 	struct utsname name;
171 	if (uname(&name)) {
172 		printf("uname failed (%d)\n", errno);
173 		doexit(1);
174 	}
175 	unsigned major = atoi(name.release);
176 	unsigned minor = 0;
177 	if (strchr(name.release, '.'))
178 		minor = atoi(strchr(name.release, '.') + 1);
179 	return major * 100 + minor;
180 }
181 
dump_seg(const char * name,struct kvm_segment * seg)182 static void dump_seg(const char* name, struct kvm_segment* seg)
183 {
184 	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",
185 	       name, seg->base, seg->limit, seg->selector, seg->type, seg->present, seg->dpl, seg->db, seg->s, seg->l, seg->g);
186 }
187 
dump_cpu_state(int cpufd,char * vm_mem)188 static void dump_cpu_state(int cpufd, char* vm_mem)
189 {
190 	struct kvm_sregs sregs;
191 	if (ioctl(cpufd, KVM_GET_SREGS, &sregs)) {
192 		printf("KVM_GET_SREGS failed (%d)\n", errno);
193 		return;
194 	}
195 	struct kvm_regs regs;
196 	if (ioctl(cpufd, KVM_GET_REGS, &regs)) {
197 		printf("KVM_GET_REGS failed (%d)\n", errno);
198 		return;
199 	}
200 	printf("RIP=0x%llx RAX=0x%llx RDX=0x%llx RCX=0x%llx RBX=0x%llx CF=%d ZF=%d\n",
201 	       regs.rip, regs.rax, regs.rdx, regs.rcx, regs.rbx, !!(regs.rflags & (1 << 0)), !!(regs.rflags & (1 << 6)));
202 	printf("CR0=0x%llx CR2=0x%llx CR4=0x%llx EFER=0x%llx\n",
203 	       sregs.cr0, sregs.cr2, sregs.cr4, sregs.efer);
204 	dump_seg("CS", &sregs.cs);
205 	dump_seg("SS", &sregs.ss);
206 	dump_seg("DS", &sregs.ds);
207 
208 	if (false) {
209 		printf("memory:\n");
210 		for (int i = 0; i < 0x80; i++)
211 			printf("0x%02x: 0x%02x\n", i, ((unsigned char*)vm_mem)[i]);
212 	}
213 
214 	if (false) {
215 		printf("vmcs:\n");
216 		const int vmcs_size = 0x1000;
217 		for (int i = 0; i < vmcs_size / 8; i += 4) {
218 			printf("0x%04x: 0x%016llx 0x%016llx 0x%016llx 0x%016llx\n", i,
219 			       ((long long*)vm_mem)[i], ((long long*)vm_mem)[i + 1], ((long long*)vm_mem)[i + 2], ((long long*)vm_mem)[i + 3]);
220 		}
221 	}
222 }
223