summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndy Lutomirski <luto@amacapital.net>2014-07-03 19:49:42 -0700
committerAndy Lutomirski <luto@amacapital.net>2014-07-03 19:49:42 -0700
commitfc315dc49fbdec7e5c99a76eba5af9cdb4813bf8 (patch)
tree308282e78632587d67cdd37dc547c89f8472dcb9
parent4ef0abaf950bceb0607f3af0e7ccc4e579c64cf7 (diff)
downloadmisc-tests-fc315dc49fbdec7e5c99a76eba5af9cdb4813bf8.tar.gz
Add sigreturn test
-rw-r--r--.gitignore1
-rw-r--r--Makefile2
-rw-r--r--sigreturn.c160
3 files changed, 162 insertions, 1 deletions
diff --git a/.gitignore b/.gitignore
index a0587a1..df19b87 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,6 +6,7 @@ kernel_pf
user_visible_state
highsys
null_seccomp
+sigreturn
*~
*_32
*_64
diff --git a/Makefile b/Makefile
index e752ac7..3edbc2e 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
.PHONY: all clean
-SIMPLE_C_TARGETS := dump-vdso dump-vvar dump-vsyscall context_switch_latency kernel_pf user_visible_state null_seccomp highsys
+SIMPLE_C_TARGETS := dump-vsyscall context_switch_latency kernel_pf user_visible_state null_seccomp highsys sigreturn
SIMPLE_CC_TARGETS := evil-clock-test
diff --git a/sigreturn.c b/sigreturn.c
new file mode 100644
index 0000000..01f92f7
--- /dev/null
+++ b/sigreturn.c
@@ -0,0 +1,160 @@
+#define _GNU_SOURCE
+
+#include <sys/time.h>
+#include <time.h>
+#include <stdlib.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <sys/mman.h>
+#include <sys/signal.h>
+#include <sys/ucontext.h>
+#include <asm/ldt.h>
+#include <err.h>
+#include <setjmp.h>
+
+struct selectors {
+ short cs, gs, fs, __pad0;
+};
+
+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 unsigned char stack16[65536] __attribute__((aligned(4096)));
+
+asm (".pushsection .text\n\t"
+ ".type int3, @function\n\t"
+ ".align 4096\n\t"
+ "int3:\n\t"
+ "int3\n\t"
+ ".size int3, . - int3\n\t"
+ ".align 4096, 0xcc\n\t"
+ ".popsection");
+extern char int3[4096];
+
+static void setup_ldt(void)
+{
+ if ((unsigned long)stack16 > (1UL << 32) - sizeof(stack16))
+ errx(1, "stack16 is too high\n");
+ if ((unsigned long)int3 > (1UL << 32) - sizeof(int3))
+ errx(1, "stack16 is too high\n");
+
+ // Borrowed from a test case by hpa
+ const struct user_desc code16_desc = {
+ .entry_number = 0,
+ .base_addr = (unsigned long)int3,
+ .limit = 4095,
+ .seg_32bit = 0,
+ .contents = 2, /* Code, not conforming */
+ .read_exec_only = 0,
+ .limit_in_pages = 0,
+ .seg_not_present = 0,
+ .useable = 0
+ };
+ const struct user_desc data16_desc = {
+ .entry_number = 1,
+ .base_addr = (unsigned long)stack16,
+ .limit = 0xffff,
+ .seg_32bit = 0,
+ .contents = 0, /* Data, grow-up */
+ .read_exec_only = 0,
+ .limit_in_pages = 0,
+ .seg_not_present = 0,
+ .useable = 0
+ };
+
+ if (syscall(SYS_modify_ldt, 1, &code16_desc, sizeof code16_desc) != 0)
+ err(1, "modify_ldt");
+ if (syscall(SYS_modify_ldt, 1, &data16_desc, sizeof data16_desc) != 0)
+ err(1, "modify_ldt");
+}
+
+static gregset_t initial_regs, requested_regs, resulting_regs;
+
+static void sigusr1(int sig, siginfo_t *info, void *ctx_void)
+{
+ ucontext_t *ctx = (ucontext_t*)ctx_void;
+
+ memcpy(&initial_regs, &ctx->uc_mcontext.gregs, sizeof(gregset_t));
+
+ struct selectors *sels = (void *)&ctx->uc_mcontext.gregs[REG_CSGSFS];
+ sels->cs = (0 << 3) | 7; /* LDT selector 0, RPL = 3 */
+ sels->__pad0 = 0; /* Avoid spurious failures. */
+
+ asm volatile ("mov %0,%%ss\n\t"
+ "mov %0,%%ds\n\t"
+ "mov %0,%%es\n\t"
+ : : "r" ((1 << 3) | 7) /* LDT selector 1, RPL = 3 */
+ );
+
+ ctx->uc_mcontext.gregs[REG_RIP] = 0;
+ ctx->uc_mcontext.gregs[REG_RSP] = 0x8badf00d5aad0000;
+
+ memcpy(&requested_regs, &ctx->uc_mcontext.gregs, sizeof(gregset_t));
+
+ return;
+}
+
+static void sigtrap(int sig, siginfo_t *info, void *ctx_void)
+{
+ ucontext_t *ctx = (ucontext_t*)ctx_void;
+ memcpy(&resulting_regs, &ctx->uc_mcontext.gregs, sizeof(gregset_t));
+ memcpy(&ctx->uc_mcontext.gregs, &initial_regs, sizeof(gregset_t));
+}
+
+static char altstack_data[SIGSTKSZ];
+
+int main()
+{
+ setup_ldt();
+
+ stack_t stack = {
+ .ss_sp = altstack_data,
+ .ss_size = SIGSTKSZ,
+ };
+ if (sigaltstack(&stack, NULL) != 0)
+ err(1, "sigaltstack");
+
+ sethandler(SIGUSR1, sigusr1, 0);
+ sethandler(SIGTRAP, sigtrap, SA_ONSTACK);
+
+ raise(SIGUSR1);
+
+ int nerrs = 0;
+
+ for (int i = 0; i < NGREG; i++) {
+ greg_t req = requested_regs[i], res = resulting_regs[i];
+ if (i == REG_TRAPNO || i == REG_RIP)
+ continue; /* don't care */
+ if (i == REG_RSP) {
+ printf("RSP: %llx -> %llx\n", req, res);
+ if (res == (req & 0xFFFFFFFF))
+ continue; /* OK; not expected to work */
+ }
+ if (requested_regs[i] != resulting_regs[i]) {
+ printf("Reg %d mismatch: requested 0x%llx; got 0x%llx\n",
+ i, requested_regs[i], resulting_regs[i]);
+ nerrs++;
+ }
+ }
+
+ if (nerrs) {
+ printf("[FAIL]\t%d registers were corrupted\n", nerrs);
+ return 1;
+ } else {
+ printf("[OK]\tall tests passed\n");
+ return 0;
+ }
+}