Re: [PATCH] x86_32, entry, selftests: Add a selftest for kernel entries from vm86 mode

From: Shuah Khan
Date: Mon Jun 01 2015 - 12:02:31 EST


On 05/29/2015 03:58 PM, Andy Lutomirski wrote:
> 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.
>
> Tests: 394838c96013 x86/asm/entry/32: Fix user_mode() misuses
> Tests: 7ba554b5ac69 x86/asm/entry/32: Really make user_mode() work correctly for VM86 mode
> Signed-off-by: Andy Lutomirski <luto@xxxxxxxxxx>
> ---
>
> Ingo, Shuah: I think this should go in through -tip. (In general, I think
> new x86 tests that don't have interesting interactions with the kselftest
> infrastructure should go in through -tip, especially tests such as this one
> that are related to recent regressions.)

No problem going through tip. Could you please make sure

"make kselftest" run from top level and
tools/testing/selftests/kselftest_install.sh

don't break?

Once the above are verified, here is my

Acked-by: Shuah Khan <shuahkh@xxxxxxxxxxxxxxx>

Thanks,
-- Shuah

>
> tools/testing/selftests/x86/Makefile | 6 +-
> tools/testing/selftests/x86/entry_from_vm86.c | 114 ++++++++++++++++++++++++++
> 2 files changed, 118 insertions(+), 2 deletions(-)
> create mode 100644 tools/testing/selftests/x86/entry_from_vm86.c
>
> diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile
> index 5bdb781163d1..9b0d8baf2934 100644
> --- a/tools/testing/selftests/x86/Makefile
> +++ b/tools/testing/selftests/x86/Makefile
> @@ -5,8 +5,10 @@ include ../lib.mk
> .PHONY: all all_32 all_64 warn_32bit_failure clean
>
> TARGETS_C_BOTHBITS := sigreturn single_step_syscall
> +TARGETS_C_32BIT_ONLY := entry_from_vm86
>
> -BINARIES_32 := $(TARGETS_C_BOTHBITS:%=%_32)
> +TARGETS_C_32BIT_ALL := $(TARGETS_C_BOTHBITS) $(TARGETS_C_32BIT_ONLY)
> +BINARIES_32 := $(TARGETS_C_32BIT_ALL:%=%_32)
> BINARIES_64 := $(TARGETS_C_BOTHBITS:%=%_64)
>
> CFLAGS := -O2 -g -std=gnu99 -pthread -Wall
> @@ -32,7 +34,7 @@ all_64: $(BINARIES_64)
> clean:
> $(RM) $(BINARIES_32) $(BINARIES_64)
>
> -$(TARGETS_C_BOTHBITS:%=%_32): %_32: %.c
> +$(TARGETS_C_32BIT_ALL:%=%_32): %_32: %.c
> $(CC) -m32 -o $@ $(CFLAGS) $(EXTRA_CFLAGS) $^ -lrt -ldl
>
> $(TARGETS_C_BOTHBITS:%=%_64): %_64: %.c
> diff --git a/tools/testing/selftests/x86/entry_from_vm86.c b/tools/testing/selftests/x86/entry_from_vm86.c
> new file mode 100644
> index 000000000000..5c38a187677b
> --- /dev/null
> +++ b/tools/testing/selftests/x86/entry_from_vm86.c
> @@ -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);
> +}
>


--
Shuah Khan
Sr. Linux Kernel Developer
Open Source Innovation Group
Samsung Research America (Silicon Valley)
shuahkh@xxxxxxxxxxxxxxx | (970) 217-8978
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/