diff options
author | Andy Lutomirski <luto@amacapital.net> | 2014-07-14 19:23:15 -0700 |
---|---|---|
committer | Andy Lutomirski <luto@amacapital.net> | 2014-07-14 19:23:15 -0700 |
commit | dbfe196a0f6efedc119deb1cdbb0139dbdf609ee (patch) | |
tree | b8d2063c4344fb5b3b4dc23a6bbfb6e74801187f | |
parent | cb45491da0ec079ee440b36af13086722ba594ed (diff) | |
download | misc-tests-dbfe196a0f6efedc119deb1cdbb0139dbdf609ee.tar.gz |
sigreturn: split into 32-bit and 64-bit versions
The 32-bit version works on unpatched kernels.
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Makefile | 4 | ||||
-rw-r--r-- | sigreturn.c | 119 |
3 files changed, 95 insertions, 29 deletions
@@ -6,7 +6,6 @@ kernel_pf user_visible_state highsys null_seccomp -sigreturn *~ *_32 *_64 @@ -1,10 +1,10 @@ .PHONY: all clean -SIMPLE_C_TARGETS := dump-vsyscall context_switch_latency kernel_pf user_visible_state null_seccomp highsys sigreturn +SIMPLE_C_TARGETS := dump-vsyscall context_switch_latency kernel_pf user_visible_state null_seccomp highsys SIMPLE_CC_TARGETS := evil-clock-test -SPLIT_C_TARGETS := dump-vdso dump-vvar syscall_exit_regs +SPLIT_C_TARGETS := dump-vdso dump-vvar syscall_exit_regs sigreturn SPLIT_CC_TARGETS := timing_test test_vsyscall test_vdso_parser ALL_TARGETS := $(SIMPLE_C_TARGETS) $(SIMPLE_CC_TARGETS) $(SPLIT_C_TARGETS:%=%_64) $(SPLIT_CC_TARGETS:%=%_64) $(SPLIT_C_TARGETS:%=%_32) $(SPLIT_CC_TARGETS:%=%_32) syscall32_from_64 diff --git a/sigreturn.c b/sigreturn.c index b863eab..ac68944 100644 --- a/sigreturn.c +++ b/sigreturn.c @@ -20,7 +20,7 @@ #include <sys/user.h> struct selectors { - short cs, gs, fs, ss; + unsigned short cs, gs, fs, ss; }; static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *), @@ -33,7 +33,6 @@ static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *), sigemptyset(&sa.sa_mask); if (sigaction(sig, &sa, 0)) err(1, "sigaction"); - } static unsigned char stack16[65536] __attribute__((aligned(4096))); @@ -42,6 +41,7 @@ asm (".pushsection .text\n\t" ".type int3, @function\n\t" ".align 4096\n\t" "int3:\n\t" + "mov %ss,%ax\n\t" "int3\n\t" ".size int3, . - int3\n\t" ".align 4096, 0xcc\n\t" @@ -50,9 +50,9 @@ extern char int3[4096]; static void setup_ldt(void) { - if ((unsigned long)stack16 > (1UL << 32) - sizeof(stack16)) + if ((unsigned long)stack16 > (1ULL << 32) - sizeof(stack16)) errx(1, "stack16 is too high\n"); - if ((unsigned long)int3 > (1UL << 32) - sizeof(int3)) + if ((unsigned long)int3 > (1ULL << 32) - sizeof(int3)) errx(1, "int3 is too high\n"); // Borrowed from a test case by hpa @@ -89,29 +89,59 @@ static gregset_t initial_regs, requested_regs, resulting_regs; static bool sig_twiddle_cs, sig_twiddle_ss; +#ifdef __x86_64__ +# define REG_IP REG_RIP +# define REG_SP REG_RSP +# define REG_AX REG_RAX + +static unsigned short *ssptr(ucontext_t *ctx) +{ + struct selectors *sels = (void *)&ctx->uc_mcontext.gregs[REG_CSGSFS]; + return &sels->ss; +} + +static unsigned short *csptr(ucontext_t *ctx) +{ + struct selectors *sels = (void *)&ctx->uc_mcontext.gregs[REG_CSGSFS]; + return &sels->cs; +} +#else +# define REG_IP REG_EIP +# define REG_SP REG_ESP +# define REG_AX REG_EAX + +static greg_t *ssptr(ucontext_t *ctx) +{ + return &ctx->uc_mcontext.gregs[REG_SS]; +} + +static greg_t *csptr(ucontext_t *ctx) +{ + return &ctx->uc_mcontext.gregs[REG_CS]; +} +#endif + 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]; if (sig_twiddle_cs) - sels->cs = (0 << 3) | 7; /* LDT selector 0, RPL = 3 */ - if (sig_twiddle_ss) - sels->ss = (1 << 3) | 7; /* LDT selector 1, RPL = 3 */ + *csptr(ctx) = (0 << 3) | 7; /* LDT selector 0, RPL = 3 */ - /* Avoid spurious failures. */ - asm volatile ("mov %0,%%ds\n\t" - "mov %0,%%es\n\t" - : : "r" ((1 << 3) | 7) /* LDT selector 1, RPL = 3 */ - ); + if (sig_twiddle_ss) + *ssptr(ctx) = (1 << 3) | 7; /* LDT selector 1, RPL = 3 */ + else /* Fix for unpatched kernels. */ + asm volatile ("mov %%ss,%0" : "=r" (*ssptr(ctx))); - ctx->uc_mcontext.gregs[REG_RIP] = + ctx->uc_mcontext.gregs[REG_IP] = sig_twiddle_cs ? 0 : (unsigned long)&int3; - ctx->uc_mcontext.gregs[REG_RSP] = 0x8badf00d5aadc0de; + ctx->uc_mcontext.gregs[REG_SP] = (unsigned long)0x8badf00d5aadc0deULL; + ctx->uc_mcontext.gregs[REG_AX] = 0; memcpy(&requested_regs, &ctx->uc_mcontext.gregs, sizeof(gregset_t)); + requested_regs[REG_AX] = *ssptr(ctx); /* The asm code does this. */ return; } @@ -119,11 +149,12 @@ static void sigusr1(int sig, siginfo_t *info, void *ctx_void) static void sigtrap(int sig, siginfo_t *info, void *ctx_void) { ucontext_t *ctx = (ucontext_t*)ctx_void; - struct selectors *sels = (void *)&ctx->uc_mcontext.gregs[REG_CSGSFS]; unsigned short ss; asm ("mov %%ss,%0" : "=r" (ss)); - printf("\tSIGTRAP: ss = %hx, frame ss = %hx\n", ss, sels->ss); + printf("\tSIGTRAP: ss = %hx, frame ss = %hx, ax = %llx\n", + ss, *ssptr(ctx), + (unsigned long long)ctx->uc_mcontext.gregs[REG_AX]); memcpy(&resulting_regs, &ctx->uc_mcontext.gregs, sizeof(gregset_t)); memcpy(&ctx->uc_mcontext.gregs, &initial_regs, sizeof(gregset_t)); @@ -144,23 +175,59 @@ static int do_test(bool twiddle_cs, bool twiddle_ss) for (int i = 0; i < NGREG; i++) { greg_t req = requested_regs[i], res = resulting_regs[i]; - if (i == REG_TRAPNO || i == REG_RIP) + if (i == REG_TRAPNO || i == REG_IP) continue; /* don't care */ - if (i == REG_RSP) { - printf("\tRSP: %llx -> %llx\n", req, res); + if (i == REG_SP) { + printf("\tSP: %llx -> %llx\n", (unsigned long long)req, + (unsigned long long)res); if (res == (req & 0xFFFFFFFF)) continue; /* OK; not expected to work */ } - if (requested_regs[i] != resulting_regs[i]) { - printf("\tReg %d mismatch: requested 0x%llx; got 0x%llx\n", - i, requested_regs[i], resulting_regs[i]); + + bool ignore_reg = false; +#if __i386__ + if (i == REG_UESP) + ignore_reg = true; +#else + if (i == REG_CSGSFS) { + struct selectors *req_sels = + (void *)&requested_regs[REG_CSGSFS]; + struct selectors *res_sels = + (void *)&resulting_regs[REG_CSGSFS]; + if (req_sels->cs != res_sels->cs) { + printf("[FAIL]\tCS mismatch: requested 0x%hx; got 0x%hx\n", + req_sels->cs, res_sels->cs); + nerrs++; + } + + if (req_sels->ss != res_sels->ss) { + printf("[FAIL]\tSS mismatch: requested 0x%hx; got 0x%hx\n", + req_sels->ss, res_sels->ss); + nerrs++; + } + + continue; + } +#endif + + /* Sanity check on the kernel */ + if (i == REG_AX && requested_regs[i] != resulting_regs[i]) { + printf("[FAIL]\tAX (saved SP) mismatch: requested 0x%llx; got 0x%llx\n", + (unsigned long long)requested_regs[i], + (unsigned long long)resulting_regs[i]); + nerrs++; + continue; + } + + if (requested_regs[i] != resulting_regs[i] && !ignore_reg) { + printf("[FAIL]\tReg %d mismatch: requested 0x%llx; got 0x%llx\n", + i, (unsigned long long)requested_regs[i], + (unsigned long long)resulting_regs[i]); nerrs++; } } - if (nerrs) - printf("[FAIL]\t%d registers were corrupted\n", nerrs); - else + if (nerrs == 0) printf("[OK]\tall registers okay\n"); return nerrs; |