1*2025cf9eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 266060214SAndy Lutomirski /* 366060214SAndy Lutomirski * sigreturn.c - tests that x86 avoids Intel SYSRET pitfalls 466060214SAndy Lutomirski * Copyright (c) 2014-2016 Andrew Lutomirski 566060214SAndy Lutomirski */ 666060214SAndy Lutomirski 766060214SAndy Lutomirski #define _GNU_SOURCE 866060214SAndy Lutomirski 966060214SAndy Lutomirski #include <stdlib.h> 1066060214SAndy Lutomirski #include <unistd.h> 1166060214SAndy Lutomirski #include <stdio.h> 1266060214SAndy Lutomirski #include <string.h> 1366060214SAndy Lutomirski #include <inttypes.h> 1466060214SAndy Lutomirski #include <sys/signal.h> 1566060214SAndy Lutomirski #include <sys/ucontext.h> 1666060214SAndy Lutomirski #include <sys/syscall.h> 1766060214SAndy Lutomirski #include <err.h> 1866060214SAndy Lutomirski #include <stddef.h> 1966060214SAndy Lutomirski #include <stdbool.h> 2066060214SAndy Lutomirski #include <setjmp.h> 2166060214SAndy Lutomirski #include <sys/user.h> 2266060214SAndy Lutomirski #include <sys/mman.h> 2366060214SAndy Lutomirski #include <assert.h> 2466060214SAndy Lutomirski 2566060214SAndy Lutomirski 2666060214SAndy Lutomirski asm ( 2766060214SAndy Lutomirski ".pushsection \".text\", \"ax\"\n\t" 2866060214SAndy Lutomirski ".balign 4096\n\t" 2966060214SAndy Lutomirski "test_page: .globl test_page\n\t" 3066060214SAndy Lutomirski ".fill 4094,1,0xcc\n\t" 3166060214SAndy Lutomirski "test_syscall_insn:\n\t" 3266060214SAndy Lutomirski "syscall\n\t" 3366060214SAndy Lutomirski ".ifne . - test_page - 4096\n\t" 3466060214SAndy Lutomirski ".error \"test page is not one page long\"\n\t" 3566060214SAndy Lutomirski ".endif\n\t" 3666060214SAndy Lutomirski ".popsection" 3766060214SAndy Lutomirski ); 3866060214SAndy Lutomirski 3966060214SAndy Lutomirski extern const char test_page[]; 4066060214SAndy Lutomirski static void const *current_test_page_addr = test_page; 4166060214SAndy Lutomirski 4266060214SAndy Lutomirski static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *), 4366060214SAndy Lutomirski int flags) 4466060214SAndy Lutomirski { 4566060214SAndy Lutomirski struct sigaction sa; 4666060214SAndy Lutomirski memset(&sa, 0, sizeof(sa)); 4766060214SAndy Lutomirski sa.sa_sigaction = handler; 4866060214SAndy Lutomirski sa.sa_flags = SA_SIGINFO | flags; 4966060214SAndy Lutomirski sigemptyset(&sa.sa_mask); 5066060214SAndy Lutomirski if (sigaction(sig, &sa, 0)) 5166060214SAndy Lutomirski err(1, "sigaction"); 5266060214SAndy Lutomirski } 5366060214SAndy Lutomirski 5466060214SAndy Lutomirski static void clearhandler(int sig) 5566060214SAndy Lutomirski { 5666060214SAndy Lutomirski struct sigaction sa; 5766060214SAndy Lutomirski memset(&sa, 0, sizeof(sa)); 5866060214SAndy Lutomirski sa.sa_handler = SIG_DFL; 5966060214SAndy Lutomirski sigemptyset(&sa.sa_mask); 6066060214SAndy Lutomirski if (sigaction(sig, &sa, 0)) 6166060214SAndy Lutomirski err(1, "sigaction"); 6266060214SAndy Lutomirski } 6366060214SAndy Lutomirski 6466060214SAndy Lutomirski /* State used by our signal handlers. */ 6566060214SAndy Lutomirski static gregset_t initial_regs; 6666060214SAndy Lutomirski 6766060214SAndy Lutomirski static volatile unsigned long rip; 6866060214SAndy Lutomirski 6966060214SAndy Lutomirski static void sigsegv_for_sigreturn_test(int sig, siginfo_t *info, void *ctx_void) 7066060214SAndy Lutomirski { 7166060214SAndy Lutomirski ucontext_t *ctx = (ucontext_t*)ctx_void; 7266060214SAndy Lutomirski 7366060214SAndy Lutomirski if (rip != ctx->uc_mcontext.gregs[REG_RIP]) { 7466060214SAndy Lutomirski printf("[FAIL]\tRequested RIP=0x%lx but got RIP=0x%lx\n", 7566060214SAndy Lutomirski rip, (unsigned long)ctx->uc_mcontext.gregs[REG_RIP]); 7666060214SAndy Lutomirski fflush(stdout); 7766060214SAndy Lutomirski _exit(1); 7866060214SAndy Lutomirski } 7966060214SAndy Lutomirski 8066060214SAndy Lutomirski memcpy(&ctx->uc_mcontext.gregs, &initial_regs, sizeof(gregset_t)); 8166060214SAndy Lutomirski 8266060214SAndy Lutomirski printf("[OK]\tGot SIGSEGV at RIP=0x%lx\n", rip); 8366060214SAndy Lutomirski } 8466060214SAndy Lutomirski 8566060214SAndy Lutomirski static void sigusr1(int sig, siginfo_t *info, void *ctx_void) 8666060214SAndy Lutomirski { 8766060214SAndy Lutomirski ucontext_t *ctx = (ucontext_t*)ctx_void; 8866060214SAndy Lutomirski 8966060214SAndy Lutomirski memcpy(&initial_regs, &ctx->uc_mcontext.gregs, sizeof(gregset_t)); 9066060214SAndy Lutomirski 9166060214SAndy Lutomirski /* Set IP and CX to match so that SYSRET can happen. */ 9266060214SAndy Lutomirski ctx->uc_mcontext.gregs[REG_RIP] = rip; 9366060214SAndy Lutomirski ctx->uc_mcontext.gregs[REG_RCX] = rip; 9466060214SAndy Lutomirski 9566060214SAndy Lutomirski /* R11 and EFLAGS should already match. */ 9666060214SAndy Lutomirski assert(ctx->uc_mcontext.gregs[REG_EFL] == 9766060214SAndy Lutomirski ctx->uc_mcontext.gregs[REG_R11]); 9866060214SAndy Lutomirski 9966060214SAndy Lutomirski sethandler(SIGSEGV, sigsegv_for_sigreturn_test, SA_RESETHAND); 10066060214SAndy Lutomirski 10166060214SAndy Lutomirski return; 10266060214SAndy Lutomirski } 10366060214SAndy Lutomirski 10466060214SAndy Lutomirski static void test_sigreturn_to(unsigned long ip) 10566060214SAndy Lutomirski { 10666060214SAndy Lutomirski rip = ip; 10766060214SAndy Lutomirski printf("[RUN]\tsigreturn to 0x%lx\n", ip); 10866060214SAndy Lutomirski raise(SIGUSR1); 10966060214SAndy Lutomirski } 11066060214SAndy Lutomirski 11166060214SAndy Lutomirski static jmp_buf jmpbuf; 11266060214SAndy Lutomirski 11366060214SAndy Lutomirski static void sigsegv_for_fallthrough(int sig, siginfo_t *info, void *ctx_void) 11466060214SAndy Lutomirski { 11566060214SAndy Lutomirski ucontext_t *ctx = (ucontext_t*)ctx_void; 11666060214SAndy Lutomirski 11766060214SAndy Lutomirski if (rip != ctx->uc_mcontext.gregs[REG_RIP]) { 11866060214SAndy Lutomirski printf("[FAIL]\tExpected SIGSEGV at 0x%lx but got RIP=0x%lx\n", 11966060214SAndy Lutomirski rip, (unsigned long)ctx->uc_mcontext.gregs[REG_RIP]); 12066060214SAndy Lutomirski fflush(stdout); 12166060214SAndy Lutomirski _exit(1); 12266060214SAndy Lutomirski } 12366060214SAndy Lutomirski 12466060214SAndy Lutomirski siglongjmp(jmpbuf, 1); 12566060214SAndy Lutomirski } 12666060214SAndy Lutomirski 12766060214SAndy Lutomirski static void test_syscall_fallthrough_to(unsigned long ip) 12866060214SAndy Lutomirski { 12966060214SAndy Lutomirski void *new_address = (void *)(ip - 4096); 13066060214SAndy Lutomirski void *ret; 13166060214SAndy Lutomirski 13266060214SAndy Lutomirski printf("[RUN]\tTrying a SYSCALL that falls through to 0x%lx\n", ip); 13366060214SAndy Lutomirski 13466060214SAndy Lutomirski ret = mremap((void *)current_test_page_addr, 4096, 4096, 13566060214SAndy Lutomirski MREMAP_MAYMOVE | MREMAP_FIXED, new_address); 13666060214SAndy Lutomirski if (ret == MAP_FAILED) { 13766060214SAndy Lutomirski if (ip <= (1UL << 47) - PAGE_SIZE) { 13866060214SAndy Lutomirski err(1, "mremap to %p", new_address); 13966060214SAndy Lutomirski } else { 14066060214SAndy Lutomirski printf("[OK]\tmremap to %p failed\n", new_address); 14166060214SAndy Lutomirski return; 14266060214SAndy Lutomirski } 14366060214SAndy Lutomirski } 14466060214SAndy Lutomirski 14566060214SAndy Lutomirski if (ret != new_address) 14666060214SAndy Lutomirski errx(1, "mremap malfunctioned: asked for %p but got %p\n", 14766060214SAndy Lutomirski new_address, ret); 14866060214SAndy Lutomirski 14966060214SAndy Lutomirski current_test_page_addr = new_address; 15066060214SAndy Lutomirski rip = ip; 15166060214SAndy Lutomirski 15266060214SAndy Lutomirski if (sigsetjmp(jmpbuf, 1) == 0) { 15366060214SAndy Lutomirski asm volatile ("call *%[syscall_insn]" :: "a" (SYS_getpid), 15466060214SAndy Lutomirski [syscall_insn] "rm" (ip - 2)); 15566060214SAndy Lutomirski errx(1, "[FAIL]\tSyscall trampoline returned"); 15666060214SAndy Lutomirski } 15766060214SAndy Lutomirski 15866060214SAndy Lutomirski printf("[OK]\tWe survived\n"); 15966060214SAndy Lutomirski } 16066060214SAndy Lutomirski 16166060214SAndy Lutomirski int main() 16266060214SAndy Lutomirski { 16366060214SAndy Lutomirski /* 16466060214SAndy Lutomirski * When the kernel returns from a slow-path syscall, it will 16566060214SAndy Lutomirski * detect whether SYSRET is appropriate. If it incorrectly 16666060214SAndy Lutomirski * thinks that SYSRET is appropriate when RIP is noncanonical, 16766060214SAndy Lutomirski * it'll crash on Intel CPUs. 16866060214SAndy Lutomirski */ 16966060214SAndy Lutomirski sethandler(SIGUSR1, sigusr1, 0); 17066060214SAndy Lutomirski for (int i = 47; i < 64; i++) 17166060214SAndy Lutomirski test_sigreturn_to(1UL<<i); 17266060214SAndy Lutomirski 17366060214SAndy Lutomirski clearhandler(SIGUSR1); 17466060214SAndy Lutomirski 17566060214SAndy Lutomirski sethandler(SIGSEGV, sigsegv_for_fallthrough, 0); 17666060214SAndy Lutomirski 17766060214SAndy Lutomirski /* One extra test to check that we didn't screw up the mremap logic. */ 17866060214SAndy Lutomirski test_syscall_fallthrough_to((1UL << 47) - 2*PAGE_SIZE); 17966060214SAndy Lutomirski 18066060214SAndy Lutomirski /* These are the interesting cases. */ 18166060214SAndy Lutomirski for (int i = 47; i < 64; i++) { 18266060214SAndy Lutomirski test_syscall_fallthrough_to((1UL<<i) - PAGE_SIZE); 18366060214SAndy Lutomirski test_syscall_fallthrough_to(1UL<<i); 18466060214SAndy Lutomirski } 18566060214SAndy Lutomirski 18666060214SAndy Lutomirski return 0; 18766060214SAndy Lutomirski } 188