forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
x86/asm/entry/32, selftests: Add a selftest for kernel entries from V…
…M86 mode Test a couple of special cases in 32-bit kernels for entries from vm86 mode. This will OOPS both old kernels due to a bug and and 4.1-rc5 due to a regression I introduced, and it should make sure that the SYSENTER-from-vm86-mode hack in the kernel keeps working. Signed-off-by: Andy Lutomirski <[email protected]> Cc: Jan Beulich <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Shuah Khan <[email protected]> Cc: Thomas Gleixner <[email protected]> Link: http://lkml.kernel.org/r/09a9916761e0a9e42d4922f147af45a0079cc1e8.1432936374.git.luto@kernel.org Tests: 394838c x86/asm/entry/32: Fix user_mode() misuses Tests: 7ba554b x86/asm/entry/32: Really make user_mode() work correctly for VM86 mode Signed-off-by: Ingo Molnar <[email protected]>
- Loading branch information
Showing
2 changed files
with
118 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
/* | ||
* entry_from_vm86.c - tests kernel entries from vm86 mode | ||
* Copyright (c) 2014-2015 Andrew Lutomirski | ||
* | ||
* This exercises a few paths that need to special-case vm86 mode. | ||
* | ||
* GPL v2. | ||
*/ | ||
|
||
#define _GNU_SOURCE | ||
|
||
#include <assert.h> | ||
#include <stdlib.h> | ||
#include <sys/syscall.h> | ||
#include <sys/signal.h> | ||
#include <sys/ucontext.h> | ||
#include <unistd.h> | ||
#include <stdio.h> | ||
#include <string.h> | ||
#include <inttypes.h> | ||
#include <sys/mman.h> | ||
#include <err.h> | ||
#include <stddef.h> | ||
#include <stdbool.h> | ||
#include <errno.h> | ||
#include <sys/vm86.h> | ||
|
||
static unsigned long load_addr = 0x10000; | ||
static int nerrs = 0; | ||
|
||
asm ( | ||
".pushsection .rodata\n\t" | ||
".type vmcode_bound, @object\n\t" | ||
"vmcode:\n\t" | ||
"vmcode_bound:\n\t" | ||
".code16\n\t" | ||
"bound %ax, (2048)\n\t" | ||
"int3\n\t" | ||
"vmcode_sysenter:\n\t" | ||
"sysenter\n\t" | ||
".size vmcode, . - vmcode\n\t" | ||
"end_vmcode:\n\t" | ||
".code32\n\t" | ||
".popsection" | ||
); | ||
|
||
extern unsigned char vmcode[], end_vmcode[]; | ||
extern unsigned char vmcode_bound[], vmcode_sysenter[]; | ||
|
||
static void do_test(struct vm86plus_struct *v86, unsigned long eip, | ||
const char *text) | ||
{ | ||
long ret; | ||
|
||
printf("[RUN]\t%s from vm86 mode\n", text); | ||
v86->regs.eip = eip; | ||
ret = vm86(VM86_ENTER, v86); | ||
|
||
if (ret == -1 && errno == ENOSYS) { | ||
printf("[SKIP]\tvm86 not supported\n"); | ||
return; | ||
} | ||
|
||
if (VM86_TYPE(ret) == VM86_INTx) { | ||
char trapname[32]; | ||
int trapno = VM86_ARG(ret); | ||
if (trapno == 13) | ||
strcpy(trapname, "GP"); | ||
else if (trapno == 5) | ||
strcpy(trapname, "BR"); | ||
else if (trapno == 14) | ||
strcpy(trapname, "PF"); | ||
else | ||
sprintf(trapname, "%d", trapno); | ||
|
||
printf("[OK]\tExited vm86 mode due to #%s\n", trapname); | ||
} else if (VM86_TYPE(ret) == VM86_UNKNOWN) { | ||
printf("[OK]\tExited vm86 mode due to unhandled GP fault\n"); | ||
} else { | ||
printf("[OK]\tExited vm86 mode due to type %ld, arg %ld\n", | ||
VM86_TYPE(ret), VM86_ARG(ret)); | ||
} | ||
} | ||
|
||
int main(void) | ||
{ | ||
struct vm86plus_struct v86; | ||
unsigned char *addr = mmap((void *)load_addr, 4096, | ||
PROT_READ | PROT_WRITE | PROT_EXEC, | ||
MAP_ANONYMOUS | MAP_PRIVATE, -1,0); | ||
if (addr != (unsigned char *)load_addr) | ||
err(1, "mmap"); | ||
|
||
memcpy(addr, vmcode, end_vmcode - vmcode); | ||
addr[2048] = 2; | ||
addr[2050] = 3; | ||
|
||
memset(&v86, 0, sizeof(v86)); | ||
|
||
v86.regs.cs = load_addr / 16; | ||
v86.regs.ss = load_addr / 16; | ||
v86.regs.ds = load_addr / 16; | ||
v86.regs.es = load_addr / 16; | ||
|
||
assert((v86.regs.cs & 3) == 0); /* Looks like RPL = 0 */ | ||
|
||
/* #BR -- should deliver SIG??? */ | ||
do_test(&v86, vmcode_bound - vmcode, "#BR"); | ||
|
||
/* SYSENTER -- should cause #GP or #UD depending on CPU */ | ||
do_test(&v86, vmcode_sysenter - vmcode, "SYSENTER"); | ||
|
||
return (nerrs == 0 ? 0 : 1); | ||
} |