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"
99 /* addressing via displacements */
103 /* addressing via registers */
110 /* register operands, only for smsw */
112 "mov %ax, (2080)\n\t"
114 "vmcode_umip_str:\n\t"
116 "vmcode_umip_sldt:\n\t"
119 ".size vmcode, . - vmcode\n\t"
125 extern unsigned char vmcode
[], end_vmcode
[];
126 extern unsigned char vmcode_bound
[], vmcode_sysenter
[], vmcode_syscall
[],
127 vmcode_sti
[], vmcode_int3
[], vmcode_int80
[], vmcode_umip
[],
128 vmcode_umip_str
[], vmcode_umip_sldt
[];
130 /* Returns false if the test was skipped. */
131 static bool do_test(struct vm86plus_struct
*v86
, unsigned long eip
,
132 unsigned int rettype
, unsigned int retarg
,
137 printf("[RUN]\t%s from vm86 mode\n", text
);
139 ret
= vm86(VM86_ENTER
, v86
);
141 if (ret
== -1 && (errno
== ENOSYS
|| errno
== EPERM
)) {
142 printf("[SKIP]\tvm86 %s\n",
143 errno
== ENOSYS
? "not supported" : "not allowed");
147 if (VM86_TYPE(ret
) == VM86_INTx
) {
149 int trapno
= VM86_ARG(ret
);
151 strcpy(trapname
, "GP");
152 else if (trapno
== 5)
153 strcpy(trapname
, "BR");
154 else if (trapno
== 14)
155 strcpy(trapname
, "PF");
157 sprintf(trapname
, "%d", trapno
);
159 printf("[INFO]\tExited vm86 mode due to #%s\n", trapname
);
160 } else if (VM86_TYPE(ret
) == VM86_UNKNOWN
) {
161 printf("[INFO]\tExited vm86 mode due to unhandled GP fault\n");
162 } else if (VM86_TYPE(ret
) == VM86_TRAP
) {
163 printf("[INFO]\tExited vm86 mode due to a trap (arg=%ld)\n",
165 } else if (VM86_TYPE(ret
) == VM86_SIGNAL
) {
166 printf("[INFO]\tExited vm86 mode due to a signal\n");
167 } else if (VM86_TYPE(ret
) == VM86_STI
) {
168 printf("[INFO]\tExited vm86 mode due to STI\n");
170 printf("[INFO]\tExited vm86 mode due to type %ld, arg %ld\n",
171 VM86_TYPE(ret
), VM86_ARG(ret
));
175 (VM86_TYPE(ret
) == rettype
&& VM86_ARG(ret
) == retarg
)) {
176 printf("[OK]\tReturned correctly\n");
178 printf("[FAIL]\tIncorrect return reason\n");
185 void do_umip_tests(struct vm86plus_struct
*vm86
, unsigned char *test_mem
)
188 unsigned short limit
;
190 } __attribute__((packed
));
192 /* Initialize variables with arbitrary values */
193 struct table_desc gdt1
= { .base
= 0x3c3c3c3c, .limit
= 0x9999 };
194 struct table_desc gdt2
= { .base
= 0x1a1a1a1a, .limit
= 0xaeae };
195 struct table_desc idt1
= { .base
= 0x7b7b7b7b, .limit
= 0xf1f1 };
196 struct table_desc idt2
= { .base
= 0x89898989, .limit
= 0x1313 };
197 unsigned short msw1
= 0x1414, msw2
= 0x2525, msw3
= 3737;
199 /* UMIP -- exit with INT3 unless kernel emulation did not trap #GP */
200 do_test(vm86
, vmcode_umip
- vmcode
, VM86_TRAP
, 3, "UMIP tests");
202 /* Results from displacement-only addressing */
203 msw1
= *(unsigned short *)(test_mem
+ 2052);
204 memcpy(&idt1
, test_mem
+ 2054, sizeof(idt1
));
205 memcpy(&gdt1
, test_mem
+ 2060, sizeof(gdt1
));
207 /* Results from register-indirect addressing */
208 msw2
= *(unsigned short *)(test_mem
+ 2066);
209 memcpy(&idt2
, test_mem
+ 2068, sizeof(idt2
));
210 memcpy(&gdt2
, test_mem
+ 2074, sizeof(gdt2
));
212 /* Results when using register operands */
213 msw3
= *(unsigned short *)(test_mem
+ 2080);
215 printf("[INFO]\tResult from SMSW:[0x%04x]\n", msw1
);
216 printf("[INFO]\tResult from SIDT: limit[0x%04x]base[0x%08lx]\n",
217 idt1
.limit
, idt1
.base
);
218 printf("[INFO]\tResult from SGDT: limit[0x%04x]base[0x%08lx]\n",
219 gdt1
.limit
, gdt1
.base
);
221 if (msw1
!= msw2
|| msw1
!= msw3
)
222 printf("[FAIL]\tAll the results of SMSW should be the same.\n");
224 printf("[PASS]\tAll the results from SMSW are identical.\n");
226 if (memcmp(&gdt1
, &gdt2
, sizeof(gdt1
)))
227 printf("[FAIL]\tAll the results of SGDT should be the same.\n");
229 printf("[PASS]\tAll the results from SGDT are identical.\n");
231 if (memcmp(&idt1
, &idt2
, sizeof(idt1
)))
232 printf("[FAIL]\tAll the results of SIDT should be the same.\n");
234 printf("[PASS]\tAll the results from SIDT are identical.\n");
236 sethandler(SIGILL
, sighandler
, 0);
237 do_test(vm86
, vmcode_umip_str
- vmcode
, VM86_SIGNAL
, 0,
239 clearhandler(SIGILL
);
241 sethandler(SIGILL
, sighandler
, 0);
242 do_test(vm86
, vmcode_umip_sldt
- vmcode
, VM86_SIGNAL
, 0,
244 clearhandler(SIGILL
);
249 struct vm86plus_struct v86
;
250 unsigned char *addr
= mmap((void *)load_addr
, 4096,
251 PROT_READ
| PROT_WRITE
| PROT_EXEC
,
252 MAP_ANONYMOUS
| MAP_PRIVATE
, -1,0);
253 if (addr
!= (unsigned char *)load_addr
)
256 memcpy(addr
, vmcode
, end_vmcode
- vmcode
);
260 memset(&v86
, 0, sizeof(v86
));
262 v86
.regs
.cs
= load_addr
/ 16;
263 v86
.regs
.ss
= load_addr
/ 16;
264 v86
.regs
.ds
= load_addr
/ 16;
265 v86
.regs
.es
= load_addr
/ 16;
267 assert((v86
.regs
.cs
& 3) == 0); /* Looks like RPL = 0 */
269 /* #BR -- should deliver SIG??? */
270 do_test(&v86
, vmcode_bound
- vmcode
, VM86_INTx
, 5, "#BR");
273 * SYSENTER -- should cause #GP or #UD depending on CPU.
274 * Expected return type -1 means that we shouldn't validate
275 * the vm86 return value. This will avoid problems on non-SEP
278 sethandler(SIGILL
, sighandler
, 0);
279 do_test(&v86
, vmcode_sysenter
- vmcode
, -1, 0, "SYSENTER");
280 clearhandler(SIGILL
);
283 * SYSCALL would be a disaster in VM86 mode. Fortunately,
284 * there is no kernel that both enables SYSCALL and sets
285 * EFER.SCE, so it's #UD on all systems. But vm86 is
286 * buggy (or has a "feature"), so the SIGILL will actually
289 sethandler(SIGILL
, sighandler
, 0);
290 do_test(&v86
, vmcode_syscall
- vmcode
, VM86_SIGNAL
, 0, "SYSCALL");
291 clearhandler(SIGILL
);
293 /* STI with VIP set */
294 v86
.regs
.eflags
|= X86_EFLAGS_VIP
;
295 v86
.regs
.eflags
&= ~X86_EFLAGS_IF
;
296 do_test(&v86
, vmcode_sti
- vmcode
, VM86_STI
, 0, "STI with VIP set");
298 /* INT3 -- should cause #BP */
299 do_test(&v86
, vmcode_int3
- vmcode
, VM86_TRAP
, 3, "INT3");
301 /* INT80 -- should exit with "INTx 0x80" */
302 v86
.regs
.eax
= (unsigned int)-1;
303 do_test(&v86
, vmcode_int80
- vmcode
, VM86_INTx
, 0x80, "int80");
305 /* UMIP -- should exit with INTx 0x80 unless UMIP was not disabled */
306 do_umip_tests(&v86
, addr
);
308 /* Execute a null pointer */
311 sethandler(SIGSEGV
, sighandler
, 0);
313 if (do_test(&v86
, 0, VM86_SIGNAL
, 0, "Execute null pointer") &&
315 printf("[FAIL]\tDid not receive SIGSEGV\n");
318 clearhandler(SIGSEGV
);
320 /* Make sure nothing explodes if we fork. */
324 return (nerrs
== 0 ? 0 : 1);