1*66060214SAndy Lutomirski /* 2*66060214SAndy Lutomirski * sigreturn.c - tests that x86 avoids Intel SYSRET pitfalls 3*66060214SAndy Lutomirski * Copyright (c) 2014-2016 Andrew Lutomirski 4*66060214SAndy Lutomirski * 5*66060214SAndy Lutomirski * This program is free software; you can redistribute it and/or modify 6*66060214SAndy Lutomirski * it under the terms and conditions of the GNU General Public License, 7*66060214SAndy Lutomirski * version 2, as published by the Free Software Foundation. 8*66060214SAndy Lutomirski * 9*66060214SAndy Lutomirski * This program is distributed in the hope it will be useful, but 10*66060214SAndy Lutomirski * WITHOUT ANY WARRANTY; without even the implied warranty of 11*66060214SAndy Lutomirski * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12*66060214SAndy Lutomirski * General Public License for more details. 13*66060214SAndy Lutomirski */ 14*66060214SAndy Lutomirski 15*66060214SAndy Lutomirski #define _GNU_SOURCE 16*66060214SAndy Lutomirski 17*66060214SAndy Lutomirski #include <stdlib.h> 18*66060214SAndy Lutomirski #include <unistd.h> 19*66060214SAndy Lutomirski #include <stdio.h> 20*66060214SAndy Lutomirski #include <string.h> 21*66060214SAndy Lutomirski #include <inttypes.h> 22*66060214SAndy Lutomirski #include <sys/signal.h> 23*66060214SAndy Lutomirski #include <sys/ucontext.h> 24*66060214SAndy Lutomirski #include <sys/syscall.h> 25*66060214SAndy Lutomirski #include <err.h> 26*66060214SAndy Lutomirski #include <stddef.h> 27*66060214SAndy Lutomirski #include <stdbool.h> 28*66060214SAndy Lutomirski #include <setjmp.h> 29*66060214SAndy Lutomirski #include <sys/user.h> 30*66060214SAndy Lutomirski #include <sys/mman.h> 31*66060214SAndy Lutomirski #include <assert.h> 32*66060214SAndy Lutomirski 33*66060214SAndy Lutomirski 34*66060214SAndy Lutomirski asm ( 35*66060214SAndy Lutomirski ".pushsection \".text\", \"ax\"\n\t" 36*66060214SAndy Lutomirski ".balign 4096\n\t" 37*66060214SAndy Lutomirski "test_page: .globl test_page\n\t" 38*66060214SAndy Lutomirski ".fill 4094,1,0xcc\n\t" 39*66060214SAndy Lutomirski "test_syscall_insn:\n\t" 40*66060214SAndy Lutomirski "syscall\n\t" 41*66060214SAndy Lutomirski ".ifne . - test_page - 4096\n\t" 42*66060214SAndy Lutomirski ".error \"test page is not one page long\"\n\t" 43*66060214SAndy Lutomirski ".endif\n\t" 44*66060214SAndy Lutomirski ".popsection" 45*66060214SAndy Lutomirski ); 46*66060214SAndy Lutomirski 47*66060214SAndy Lutomirski extern const char test_page[]; 48*66060214SAndy Lutomirski static void const *current_test_page_addr = test_page; 49*66060214SAndy Lutomirski 50*66060214SAndy Lutomirski static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *), 51*66060214SAndy Lutomirski int flags) 52*66060214SAndy Lutomirski { 53*66060214SAndy Lutomirski struct sigaction sa; 54*66060214SAndy Lutomirski memset(&sa, 0, sizeof(sa)); 55*66060214SAndy Lutomirski sa.sa_sigaction = handler; 56*66060214SAndy Lutomirski sa.sa_flags = SA_SIGINFO | flags; 57*66060214SAndy Lutomirski sigemptyset(&sa.sa_mask); 58*66060214SAndy Lutomirski if (sigaction(sig, &sa, 0)) 59*66060214SAndy Lutomirski err(1, "sigaction"); 60*66060214SAndy Lutomirski } 61*66060214SAndy Lutomirski 62*66060214SAndy Lutomirski static void clearhandler(int sig) 63*66060214SAndy Lutomirski { 64*66060214SAndy Lutomirski struct sigaction sa; 65*66060214SAndy Lutomirski memset(&sa, 0, sizeof(sa)); 66*66060214SAndy Lutomirski sa.sa_handler = SIG_DFL; 67*66060214SAndy Lutomirski sigemptyset(&sa.sa_mask); 68*66060214SAndy Lutomirski if (sigaction(sig, &sa, 0)) 69*66060214SAndy Lutomirski err(1, "sigaction"); 70*66060214SAndy Lutomirski } 71*66060214SAndy Lutomirski 72*66060214SAndy Lutomirski /* State used by our signal handlers. */ 73*66060214SAndy Lutomirski static gregset_t initial_regs; 74*66060214SAndy Lutomirski 75*66060214SAndy Lutomirski static volatile unsigned long rip; 76*66060214SAndy Lutomirski 77*66060214SAndy Lutomirski static void sigsegv_for_sigreturn_test(int sig, siginfo_t *info, void *ctx_void) 78*66060214SAndy Lutomirski { 79*66060214SAndy Lutomirski ucontext_t *ctx = (ucontext_t*)ctx_void; 80*66060214SAndy Lutomirski 81*66060214SAndy Lutomirski if (rip != ctx->uc_mcontext.gregs[REG_RIP]) { 82*66060214SAndy Lutomirski printf("[FAIL]\tRequested RIP=0x%lx but got RIP=0x%lx\n", 83*66060214SAndy Lutomirski rip, (unsigned long)ctx->uc_mcontext.gregs[REG_RIP]); 84*66060214SAndy Lutomirski fflush(stdout); 85*66060214SAndy Lutomirski _exit(1); 86*66060214SAndy Lutomirski } 87*66060214SAndy Lutomirski 88*66060214SAndy Lutomirski memcpy(&ctx->uc_mcontext.gregs, &initial_regs, sizeof(gregset_t)); 89*66060214SAndy Lutomirski 90*66060214SAndy Lutomirski printf("[OK]\tGot SIGSEGV at RIP=0x%lx\n", rip); 91*66060214SAndy Lutomirski } 92*66060214SAndy Lutomirski 93*66060214SAndy Lutomirski static void sigusr1(int sig, siginfo_t *info, void *ctx_void) 94*66060214SAndy Lutomirski { 95*66060214SAndy Lutomirski ucontext_t *ctx = (ucontext_t*)ctx_void; 96*66060214SAndy Lutomirski 97*66060214SAndy Lutomirski memcpy(&initial_regs, &ctx->uc_mcontext.gregs, sizeof(gregset_t)); 98*66060214SAndy Lutomirski 99*66060214SAndy Lutomirski /* Set IP and CX to match so that SYSRET can happen. */ 100*66060214SAndy Lutomirski ctx->uc_mcontext.gregs[REG_RIP] = rip; 101*66060214SAndy Lutomirski ctx->uc_mcontext.gregs[REG_RCX] = rip; 102*66060214SAndy Lutomirski 103*66060214SAndy Lutomirski /* R11 and EFLAGS should already match. */ 104*66060214SAndy Lutomirski assert(ctx->uc_mcontext.gregs[REG_EFL] == 105*66060214SAndy Lutomirski ctx->uc_mcontext.gregs[REG_R11]); 106*66060214SAndy Lutomirski 107*66060214SAndy Lutomirski sethandler(SIGSEGV, sigsegv_for_sigreturn_test, SA_RESETHAND); 108*66060214SAndy Lutomirski 109*66060214SAndy Lutomirski return; 110*66060214SAndy Lutomirski } 111*66060214SAndy Lutomirski 112*66060214SAndy Lutomirski static void test_sigreturn_to(unsigned long ip) 113*66060214SAndy Lutomirski { 114*66060214SAndy Lutomirski rip = ip; 115*66060214SAndy Lutomirski printf("[RUN]\tsigreturn to 0x%lx\n", ip); 116*66060214SAndy Lutomirski raise(SIGUSR1); 117*66060214SAndy Lutomirski } 118*66060214SAndy Lutomirski 119*66060214SAndy Lutomirski static jmp_buf jmpbuf; 120*66060214SAndy Lutomirski 121*66060214SAndy Lutomirski static void sigsegv_for_fallthrough(int sig, siginfo_t *info, void *ctx_void) 122*66060214SAndy Lutomirski { 123*66060214SAndy Lutomirski ucontext_t *ctx = (ucontext_t*)ctx_void; 124*66060214SAndy Lutomirski 125*66060214SAndy Lutomirski if (rip != ctx->uc_mcontext.gregs[REG_RIP]) { 126*66060214SAndy Lutomirski printf("[FAIL]\tExpected SIGSEGV at 0x%lx but got RIP=0x%lx\n", 127*66060214SAndy Lutomirski rip, (unsigned long)ctx->uc_mcontext.gregs[REG_RIP]); 128*66060214SAndy Lutomirski fflush(stdout); 129*66060214SAndy Lutomirski _exit(1); 130*66060214SAndy Lutomirski } 131*66060214SAndy Lutomirski 132*66060214SAndy Lutomirski siglongjmp(jmpbuf, 1); 133*66060214SAndy Lutomirski } 134*66060214SAndy Lutomirski 135*66060214SAndy Lutomirski static void test_syscall_fallthrough_to(unsigned long ip) 136*66060214SAndy Lutomirski { 137*66060214SAndy Lutomirski void *new_address = (void *)(ip - 4096); 138*66060214SAndy Lutomirski void *ret; 139*66060214SAndy Lutomirski 140*66060214SAndy Lutomirski printf("[RUN]\tTrying a SYSCALL that falls through to 0x%lx\n", ip); 141*66060214SAndy Lutomirski 142*66060214SAndy Lutomirski ret = mremap((void *)current_test_page_addr, 4096, 4096, 143*66060214SAndy Lutomirski MREMAP_MAYMOVE | MREMAP_FIXED, new_address); 144*66060214SAndy Lutomirski if (ret == MAP_FAILED) { 145*66060214SAndy Lutomirski if (ip <= (1UL << 47) - PAGE_SIZE) { 146*66060214SAndy Lutomirski err(1, "mremap to %p", new_address); 147*66060214SAndy Lutomirski } else { 148*66060214SAndy Lutomirski printf("[OK]\tmremap to %p failed\n", new_address); 149*66060214SAndy Lutomirski return; 150*66060214SAndy Lutomirski } 151*66060214SAndy Lutomirski } 152*66060214SAndy Lutomirski 153*66060214SAndy Lutomirski if (ret != new_address) 154*66060214SAndy Lutomirski errx(1, "mremap malfunctioned: asked for %p but got %p\n", 155*66060214SAndy Lutomirski new_address, ret); 156*66060214SAndy Lutomirski 157*66060214SAndy Lutomirski current_test_page_addr = new_address; 158*66060214SAndy Lutomirski rip = ip; 159*66060214SAndy Lutomirski 160*66060214SAndy Lutomirski if (sigsetjmp(jmpbuf, 1) == 0) { 161*66060214SAndy Lutomirski asm volatile ("call *%[syscall_insn]" :: "a" (SYS_getpid), 162*66060214SAndy Lutomirski [syscall_insn] "rm" (ip - 2)); 163*66060214SAndy Lutomirski errx(1, "[FAIL]\tSyscall trampoline returned"); 164*66060214SAndy Lutomirski } 165*66060214SAndy Lutomirski 166*66060214SAndy Lutomirski printf("[OK]\tWe survived\n"); 167*66060214SAndy Lutomirski } 168*66060214SAndy Lutomirski 169*66060214SAndy Lutomirski int main() 170*66060214SAndy Lutomirski { 171*66060214SAndy Lutomirski /* 172*66060214SAndy Lutomirski * When the kernel returns from a slow-path syscall, it will 173*66060214SAndy Lutomirski * detect whether SYSRET is appropriate. If it incorrectly 174*66060214SAndy Lutomirski * thinks that SYSRET is appropriate when RIP is noncanonical, 175*66060214SAndy Lutomirski * it'll crash on Intel CPUs. 176*66060214SAndy Lutomirski */ 177*66060214SAndy Lutomirski sethandler(SIGUSR1, sigusr1, 0); 178*66060214SAndy Lutomirski for (int i = 47; i < 64; i++) 179*66060214SAndy Lutomirski test_sigreturn_to(1UL<<i); 180*66060214SAndy Lutomirski 181*66060214SAndy Lutomirski clearhandler(SIGUSR1); 182*66060214SAndy Lutomirski 183*66060214SAndy Lutomirski sethandler(SIGSEGV, sigsegv_for_fallthrough, 0); 184*66060214SAndy Lutomirski 185*66060214SAndy Lutomirski /* One extra test to check that we didn't screw up the mremap logic. */ 186*66060214SAndy Lutomirski test_syscall_fallthrough_to((1UL << 47) - 2*PAGE_SIZE); 187*66060214SAndy Lutomirski 188*66060214SAndy Lutomirski /* These are the interesting cases. */ 189*66060214SAndy Lutomirski for (int i = 47; i < 64; i++) { 190*66060214SAndy Lutomirski test_syscall_fallthrough_to((1UL<<i) - PAGE_SIZE); 191*66060214SAndy Lutomirski test_syscall_fallthrough_to(1UL<<i); 192*66060214SAndy Lutomirski } 193*66060214SAndy Lutomirski 194*66060214SAndy Lutomirski return 0; 195*66060214SAndy Lutomirski } 196