selftests/x86: Test __kernel_sigreturn and __kernel_rt_sigreturn
authorAndy Lutomirski <luto@kernel.org>
Tue, 12 Jan 2016 19:56:15 +0000 (11:56 -0800)
committerIngo Molnar <mingo@kernel.org>
Wed, 13 Jan 2016 09:34:40 +0000 (10:34 +0100)
The vdso-based sigreturn mechanism is fragile and isn't used by
modern glibc so, if we break it, we'll only notice when someone
tests an unusual libc.

Add an explicit selftest.

[ I wrote this while debugging a Bionic breakage -- my first guess
  was that I had somehow messed up sigreturn.  I've caused problems in
  that code before, and it's really easy to fail to notice it because
  there's nothing on a modern distro that needs vdso-based sigreturn. ]

Signed-off-by: Andy Lutomirski <luto@kernel.org>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Shuah Khan <shuahkh@osg.samsung.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Link: http://lkml.kernel.org/r/32946d714156879cd8e5d8eab044cd07557ed558.1452628504.git.luto@kernel.org
Signed-off-by: Ingo Molnar <mingo@kernel.org>
tools/testing/selftests/x86/Makefile
tools/testing/selftests/x86/vdso_restorer.c [new file with mode: 0644]

index 398bb0e6e38cc09df135c045798ad05b50cbe476..d0c473f6585058053d83ea2e6a3eee46321668a1 100644 (file)
@@ -7,7 +7,8 @@ include ../lib.mk
 TARGETS_C_BOTHBITS := single_step_syscall sysret_ss_attrs syscall_nt ptrace_syscall
 TARGETS_C_32BIT_ONLY := entry_from_vm86 syscall_arg_fault sigreturn test_syscall_vdso unwind_vdso \
                        test_FCMOV test_FCOMI test_FISTTP \
-                       ldt_gdt
+                       ldt_gdt \
+                       vdso_restorer
 
 TARGETS_C_32BIT_ALL := $(TARGETS_C_BOTHBITS) $(TARGETS_C_32BIT_ONLY)
 BINARIES_32 := $(TARGETS_C_32BIT_ALL:%=%_32)
diff --git a/tools/testing/selftests/x86/vdso_restorer.c b/tools/testing/selftests/x86/vdso_restorer.c
new file mode 100644 (file)
index 0000000..cb03842
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * vdso_restorer.c - tests vDSO-based signal restore
+ * 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.
+ *
+ * This makes sure that sa_restorer == NULL keeps working on 32-bit
+ * configurations.  Modern glibc doesn't use it under any circumstances,
+ * so it's easy to overlook breakage.
+ *
+ * 64-bit userspace has never supported sa_restorer == NULL, so this is
+ * 32-bit only.
+ */
+
+#define _GNU_SOURCE
+
+#include <err.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <syscall.h>
+#include <sys/syscall.h>
+
+/* Open-code this -- the headers are too messy to easily use them. */
+struct real_sigaction {
+       void *handler;
+       unsigned long flags;
+       void *restorer;
+       unsigned int mask[2];
+};
+
+static volatile sig_atomic_t handler_called;
+
+static void handler_with_siginfo(int sig, siginfo_t *info, void *ctx_void)
+{
+       handler_called = 1;
+}
+
+static void handler_without_siginfo(int sig)
+{
+       handler_called = 1;
+}
+
+int main()
+{
+       int nerrs = 0;
+       struct real_sigaction sa;
+
+       memset(&sa, 0, sizeof(sa));
+       sa.handler = handler_with_siginfo;
+       sa.flags = SA_SIGINFO;
+       sa.restorer = NULL;     /* request kernel-provided restorer */
+
+       if (syscall(SYS_rt_sigaction, SIGUSR1, &sa, NULL, 8) != 0)
+               err(1, "raw rt_sigaction syscall");
+
+       raise(SIGUSR1);
+
+       if (handler_called) {
+               printf("[OK]\tSA_SIGINFO handler returned successfully\n");
+       } else {
+               printf("[FAIL]\tSA_SIGINFO handler was not called\n");
+               nerrs++;
+       }
+
+       sa.flags = 0;
+       sa.handler = handler_without_siginfo;
+       if (syscall(SYS_sigaction, SIGUSR1, &sa, 0) != 0)
+               err(1, "raw sigaction syscall");
+       handler_called = 0;
+
+       raise(SIGUSR1);
+
+       if (handler_called) {
+               printf("[OK]\t!SA_SIGINFO handler returned successfully\n");
+       } else {
+               printf("[FAIL]\t!SA_SIGINFO handler was not called\n");
+               nerrs++;
+       }
+}