2 * entry_from_vm86.c - tests kernel entries from vm86 mode
3 * Copyright (c) 2014-2015 Andrew Lutomirski
5 * This exercises a few paths that need to special-case vm86 mode.
14 #include <sys/syscall.h>
15 #include <sys/signal.h>
16 #include <sys/ucontext.h>
28 static unsigned long load_addr
= 0x10000;
31 static void sethandler(int sig
, void (*handler
)(int, siginfo_t
*, void *),
35 memset(&sa
, 0, sizeof(sa
));
36 sa
.sa_sigaction
= handler
;
37 sa
.sa_flags
= SA_SIGINFO
| flags
;
38 sigemptyset(&sa
.sa_mask
);
39 if (sigaction(sig
, &sa
, 0))
43 static void clearhandler(int sig
)
46 memset(&sa
, 0, sizeof(sa
));
47 sa
.sa_handler
= SIG_DFL
;
48 sigemptyset(&sa
.sa_mask
);
49 if (sigaction(sig
, &sa
, 0))
53 static sig_atomic_t got_signal
;
55 static void sighandler(int sig
, siginfo_t
*info
, void *ctx_void
)
57 ucontext_t
*ctx
= (ucontext_t
*)ctx_void
;
59 if (ctx
->uc_mcontext
.gregs
[REG_EFL
] & X86_EFLAGS_VM
||
60 (ctx
->uc_mcontext
.gregs
[REG_CS
] & 3) != 3) {
61 printf("[FAIL]\tSignal frame should not reflect vm86 mode\n");
68 else if (sig
== SIGILL
)
71 signame
= "unexpected signal";
73 printf("[INFO]\t%s: FLAGS = 0x%lx, CS = 0x%hx\n", signame
,
74 (unsigned long)ctx
->uc_mcontext
.gregs
[REG_EFL
],
75 (unsigned short)ctx
->uc_mcontext
.gregs
[REG_CS
]);
81 ".pushsection .rodata\n\t"
82 ".type vmcode_bound, @object\n\t"
86 "bound %ax, (2048)\n\t"
88 "vmcode_sysenter:\n\t"
98 "vmcode_popf_hlt:\n\t"
103 /* addressing via displacements */
107 /* addressing via registers */
114 /* register operands, only for smsw */
116 "mov %ax, (2080)\n\t"
118 "vmcode_umip_str:\n\t"
120 "vmcode_umip_sldt:\n\t"
123 ".size vmcode, . - vmcode\n\t"
129 extern unsigned char vmcode
[], end_vmcode
[];
130 extern unsigned char vmcode_bound
[], vmcode_sysenter
[], vmcode_syscall
[],
131 vmcode_sti
[], vmcode_int3
[], vmcode_int80
[], vmcode_popf_hlt
[],
132 vmcode_umip
[], vmcode_umip_str
[], vmcode_umip_sldt
[];
134 /* Returns false if the test was skipped. */
135 static bool do_test(struct vm86plus_struct
*v86
, unsigned long eip
,
136 unsigned int rettype
, unsigned int retarg
,
141 printf("[RUN]\t%s from vm86 mode\n", text
);
143 ret
= vm86(VM86_ENTER
, v86
);
145 if (ret
== -1 && (errno
== ENOSYS
|| errno
== EPERM
)) {
146 printf("[SKIP]\tvm86 %s\n",
147 errno
== ENOSYS
? "not supported" : "not allowed");
151 if (VM86_TYPE(ret
) == VM86_INTx
) {
153 int trapno
= VM86_ARG(ret
);
155 strcpy(trapname
, "GP");
156 else if (trapno
== 5)
157 strcpy(trapname
, "BR");
158 else if (trapno
== 14)
159 strcpy(trapname
, "PF");
161 sprintf(trapname
, "%d", trapno
);
163 printf("[INFO]\tExited vm86 mode due to #%s\n", trapname
);
164 } else if (VM86_TYPE(ret
) == VM86_UNKNOWN
) {
165 printf("[INFO]\tExited vm86 mode due to unhandled GP fault\n");
166 } else if (VM86_TYPE(ret
) == VM86_TRAP
) {
167 printf("[INFO]\tExited vm86 mode due to a trap (arg=%ld)\n",
169 } else if (VM86_TYPE(ret
) == VM86_SIGNAL
) {
170 printf("[INFO]\tExited vm86 mode due to a signal\n");
171 } else if (VM86_TYPE(ret
) == VM86_STI
) {
172 printf("[INFO]\tExited vm86 mode due to STI\n");
174 printf("[INFO]\tExited vm86 mode due to type %ld, arg %ld\n",
175 VM86_TYPE(ret
), VM86_ARG(ret
));
179 (VM86_TYPE(ret
) == rettype
&& VM86_ARG(ret
) == retarg
)) {
180 printf("[OK]\tReturned correctly\n");
182 printf("[FAIL]\tIncorrect return reason (started at eip = 0x%lx, ended at eip = 0x%lx)\n", eip
, v86
->regs
.eip
);
189 void do_umip_tests(struct vm86plus_struct
*vm86
, unsigned char *test_mem
)
192 unsigned short limit
;
194 } __attribute__((packed
));
196 /* Initialize variables with arbitrary values */
197 struct table_desc gdt1
= { .base
= 0x3c3c3c3c, .limit
= 0x9999 };
198 struct table_desc gdt2
= { .base
= 0x1a1a1a1a, .limit
= 0xaeae };
199 struct table_desc idt1
= { .base
= 0x7b7b7b7b, .limit
= 0xf1f1 };
200 struct table_desc idt2
= { .base
= 0x89898989, .limit
= 0x1313 };
201 unsigned short msw1
= 0x1414, msw2
= 0x2525, msw3
= 3737;
203 /* UMIP -- exit with INT3 unless kernel emulation did not trap #GP */
204 do_test(vm86
, vmcode_umip
- vmcode
, VM86_TRAP
, 3, "UMIP tests");
206 /* Results from displacement-only addressing */
207 msw1
= *(unsigned short *)(test_mem
+ 2052);
208 memcpy(&idt1
, test_mem
+ 2054, sizeof(idt1
));
209 memcpy(&gdt1
, test_mem
+ 2060, sizeof(gdt1
));
211 /* Results from register-indirect addressing */
212 msw2
= *(unsigned short *)(test_mem
+ 2066);
213 memcpy(&idt2
, test_mem
+ 2068, sizeof(idt2
));
214 memcpy(&gdt2
, test_mem
+ 2074, sizeof(gdt2
));
216 /* Results when using register operands */
217 msw3
= *(unsigned short *)(test_mem
+ 2080);
219 printf("[INFO]\tResult from SMSW:[0x%04x]\n", msw1
);
220 printf("[INFO]\tResult from SIDT: limit[0x%04x]base[0x%08lx]\n",
221 idt1
.limit
, idt1
.base
);
222 printf("[INFO]\tResult from SGDT: limit[0x%04x]base[0x%08lx]\n",
223 gdt1
.limit
, gdt1
.base
);
225 if (msw1
!= msw2
|| msw1
!= msw3
)
226 printf("[FAIL]\tAll the results of SMSW should be the same.\n");
228 printf("[PASS]\tAll the results from SMSW are identical.\n");
230 if (memcmp(&gdt1
, &gdt2
, sizeof(gdt1
)))
231 printf("[FAIL]\tAll the results of SGDT should be the same.\n");
233 printf("[PASS]\tAll the results from SGDT are identical.\n");
235 if (memcmp(&idt1
, &idt2
, sizeof(idt1
)))
236 printf("[FAIL]\tAll the results of SIDT should be the same.\n");
238 printf("[PASS]\tAll the results from SIDT are identical.\n");
240 sethandler(SIGILL
, sighandler
, 0);
241 do_test(vm86
, vmcode_umip_str
- vmcode
, VM86_SIGNAL
, 0,
243 clearhandler(SIGILL
);
245 sethandler(SIGILL
, sighandler
, 0);
246 do_test(vm86
, vmcode_umip_sldt
- vmcode
, VM86_SIGNAL
, 0,
248 clearhandler(SIGILL
);
253 struct vm86plus_struct v86
;
254 unsigned char *addr
= mmap((void *)load_addr
, 4096,
255 PROT_READ
| PROT_WRITE
| PROT_EXEC
,
256 MAP_ANONYMOUS
| MAP_PRIVATE
, -1,0);
257 if (addr
!= (unsigned char *)load_addr
)
260 memcpy(addr
, vmcode
, end_vmcode
- vmcode
);
264 memset(&v86
, 0, sizeof(v86
));
266 v86
.regs
.cs
= load_addr
/ 16;
267 v86
.regs
.ss
= load_addr
/ 16;
268 v86
.regs
.ds
= load_addr
/ 16;
269 v86
.regs
.es
= load_addr
/ 16;
271 /* Use the end of the page as our stack. */
274 assert((v86
.regs
.cs
& 3) == 0); /* Looks like RPL = 0 */
276 /* #BR -- should deliver SIG??? */
277 do_test(&v86
, vmcode_bound
- vmcode
, VM86_INTx
, 5, "#BR");
280 * SYSENTER -- should cause #GP or #UD depending on CPU.
281 * Expected return type -1 means that we shouldn't validate
282 * the vm86 return value. This will avoid problems on non-SEP
285 sethandler(SIGILL
, sighandler
, 0);
286 do_test(&v86
, vmcode_sysenter
- vmcode
, -1, 0, "SYSENTER");
287 clearhandler(SIGILL
);
290 * SYSCALL would be a disaster in VM86 mode. Fortunately,
291 * there is no kernel that both enables SYSCALL and sets
292 * EFER.SCE, so it's #UD on all systems. But vm86 is
293 * buggy (or has a "feature"), so the SIGILL will actually
296 sethandler(SIGILL
, sighandler
, 0);
297 do_test(&v86
, vmcode_syscall
- vmcode
, VM86_SIGNAL
, 0, "SYSCALL");
298 clearhandler(SIGILL
);
300 /* STI with VIP set */
301 v86
.regs
.eflags
|= X86_EFLAGS_VIP
;
302 v86
.regs
.eflags
&= ~X86_EFLAGS_IF
;
303 do_test(&v86
, vmcode_sti
- vmcode
, VM86_STI
, 0, "STI with VIP set");
305 /* POPF with VIP set but IF clear: should not trap */
306 v86
.regs
.eflags
= X86_EFLAGS_VIP
;
308 do_test(&v86
, vmcode_popf_hlt
- vmcode
, VM86_UNKNOWN
, 0, "POPF with VIP set and IF clear");
310 /* POPF with VIP set and IF set: should trap */
311 v86
.regs
.eflags
= X86_EFLAGS_VIP
;
312 v86
.regs
.eax
= X86_EFLAGS_IF
;
313 do_test(&v86
, vmcode_popf_hlt
- vmcode
, VM86_STI
, 0, "POPF with VIP and IF set");
315 /* POPF with VIP clear and IF set: should not trap */
317 v86
.regs
.eax
= X86_EFLAGS_IF
;
318 do_test(&v86
, vmcode_popf_hlt
- vmcode
, VM86_UNKNOWN
, 0, "POPF with VIP clear and IF set");
322 /* INT3 -- should cause #BP */
323 do_test(&v86
, vmcode_int3
- vmcode
, VM86_TRAP
, 3, "INT3");
325 /* INT80 -- should exit with "INTx 0x80" */
326 v86
.regs
.eax
= (unsigned int)-1;
327 do_test(&v86
, vmcode_int80
- vmcode
, VM86_INTx
, 0x80, "int80");
329 /* UMIP -- should exit with INTx 0x80 unless UMIP was not disabled */
330 do_umip_tests(&v86
, addr
);
332 /* Execute a null pointer */
335 sethandler(SIGSEGV
, sighandler
, 0);
337 if (do_test(&v86
, 0, VM86_SIGNAL
, 0, "Execute null pointer") &&
339 printf("[FAIL]\tDid not receive SIGSEGV\n");
342 clearhandler(SIGSEGV
);
344 /* Make sure nothing explodes if we fork. */
348 return (nerrs
== 0 ? 0 : 1);