These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / tools / testing / selftests / x86 / syscall_arg_fault.c
diff --git a/kernel/tools/testing/selftests/x86/syscall_arg_fault.c b/kernel/tools/testing/selftests/x86/syscall_arg_fault.c
new file mode 100644 (file)
index 0000000..7db4fc9
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * syscall_arg_fault.c - tests faults 32-bit fast syscall stack args
+ * Copyright (c) 2015 Andrew Lutomirski
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/signal.h>
+#include <sys/ucontext.h>
+#include <err.h>
+#include <setjmp.h>
+#include <errno.h>
+
+/* Our sigaltstack scratch space. */
+static unsigned char altstack_data[SIGSTKSZ];
+
+static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
+                      int flags)
+{
+       struct sigaction sa;
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_sigaction = handler;
+       sa.sa_flags = SA_SIGINFO | flags;
+       sigemptyset(&sa.sa_mask);
+       if (sigaction(sig, &sa, 0))
+               err(1, "sigaction");
+}
+
+static volatile sig_atomic_t sig_traps;
+static sigjmp_buf jmpbuf;
+
+static volatile sig_atomic_t n_errs;
+
+static void sigsegv(int sig, siginfo_t *info, void *ctx_void)
+{
+       ucontext_t *ctx = (ucontext_t*)ctx_void;
+
+       if (ctx->uc_mcontext.gregs[REG_EAX] != -EFAULT) {
+               printf("[FAIL]\tAX had the wrong value: 0x%x\n",
+                      ctx->uc_mcontext.gregs[REG_EAX]);
+               n_errs++;
+       } else {
+               printf("[OK]\tSeems okay\n");
+       }
+
+       siglongjmp(jmpbuf, 1);
+}
+
+static void sigill(int sig, siginfo_t *info, void *ctx_void)
+{
+       printf("[SKIP]\tIllegal instruction\n");
+       siglongjmp(jmpbuf, 1);
+}
+
+int main()
+{
+       stack_t stack = {
+               .ss_sp = altstack_data,
+               .ss_size = SIGSTKSZ,
+       };
+       if (sigaltstack(&stack, NULL) != 0)
+               err(1, "sigaltstack");
+
+       sethandler(SIGSEGV, sigsegv, SA_ONSTACK);
+       sethandler(SIGILL, sigill, SA_ONSTACK);
+
+       /*
+        * Exercise another nasty special case.  The 32-bit SYSCALL
+        * and SYSENTER instructions (even in compat mode) each
+        * clobber one register.  A Linux system call has a syscall
+        * number and six arguments, and the user stack pointer
+        * needs to live in some register on return.  That means
+        * that we need eight registers, but SYSCALL and SYSENTER
+        * only preserve seven registers.  As a result, one argument
+        * ends up on the stack.  The stack is user memory, which
+        * means that the kernel can fail to read it.
+        *
+        * The 32-bit fast system calls don't have a defined ABI:
+        * we're supposed to invoke them through the vDSO.  So we'll
+        * fudge it: we set all regs to invalid pointer values and
+        * invoke the entry instruction.  The return will fail no
+        * matter what, and we completely lose our program state,
+        * but we can fix it up with a signal handler.
+        */
+
+       printf("[RUN]\tSYSENTER with invalid state\n");
+       if (sigsetjmp(jmpbuf, 1) == 0) {
+               asm volatile (
+                       "movl $-1, %%eax\n\t"
+                       "movl $-1, %%ebx\n\t"
+                       "movl $-1, %%ecx\n\t"
+                       "movl $-1, %%edx\n\t"
+                       "movl $-1, %%esi\n\t"
+                       "movl $-1, %%edi\n\t"
+                       "movl $-1, %%ebp\n\t"
+                       "movl $-1, %%esp\n\t"
+                       "sysenter"
+                       : : : "memory", "flags");
+       }
+
+       printf("[RUN]\tSYSCALL with invalid state\n");
+       if (sigsetjmp(jmpbuf, 1) == 0) {
+               asm volatile (
+                       "movl $-1, %%eax\n\t"
+                       "movl $-1, %%ebx\n\t"
+                       "movl $-1, %%ecx\n\t"
+                       "movl $-1, %%edx\n\t"
+                       "movl $-1, %%esi\n\t"
+                       "movl $-1, %%edi\n\t"
+                       "movl $-1, %%ebp\n\t"
+                       "movl $-1, %%esp\n\t"
+                       "syscall\n\t"
+                       "pushl $0"      /* make sure we segfault cleanly */
+                       : : : "memory", "flags");
+       }
+
+       return 0;
+}