18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * sigreturn.c - tests that x86 avoids Intel SYSRET pitfalls 48c2ecf20Sopenharmony_ci * Copyright (c) 2014-2016 Andrew Lutomirski 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#define _GNU_SOURCE 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <stdlib.h> 108c2ecf20Sopenharmony_ci#include <unistd.h> 118c2ecf20Sopenharmony_ci#include <stdio.h> 128c2ecf20Sopenharmony_ci#include <string.h> 138c2ecf20Sopenharmony_ci#include <inttypes.h> 148c2ecf20Sopenharmony_ci#include <sys/signal.h> 158c2ecf20Sopenharmony_ci#include <sys/ucontext.h> 168c2ecf20Sopenharmony_ci#include <sys/syscall.h> 178c2ecf20Sopenharmony_ci#include <err.h> 188c2ecf20Sopenharmony_ci#include <stddef.h> 198c2ecf20Sopenharmony_ci#include <stdbool.h> 208c2ecf20Sopenharmony_ci#include <setjmp.h> 218c2ecf20Sopenharmony_ci#include <sys/user.h> 228c2ecf20Sopenharmony_ci#include <sys/mman.h> 238c2ecf20Sopenharmony_ci#include <assert.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ciasm ( 278c2ecf20Sopenharmony_ci ".pushsection \".text\", \"ax\"\n\t" 288c2ecf20Sopenharmony_ci ".balign 4096\n\t" 298c2ecf20Sopenharmony_ci "test_page: .globl test_page\n\t" 308c2ecf20Sopenharmony_ci ".fill 4094,1,0xcc\n\t" 318c2ecf20Sopenharmony_ci "test_syscall_insn:\n\t" 328c2ecf20Sopenharmony_ci "syscall\n\t" 338c2ecf20Sopenharmony_ci ".ifne . - test_page - 4096\n\t" 348c2ecf20Sopenharmony_ci ".error \"test page is not one page long\"\n\t" 358c2ecf20Sopenharmony_ci ".endif\n\t" 368c2ecf20Sopenharmony_ci ".popsection" 378c2ecf20Sopenharmony_ci ); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ciextern const char test_page[]; 408c2ecf20Sopenharmony_cistatic void const *current_test_page_addr = test_page; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic void sethandler(int sig, void (*handler)(int, siginfo_t *, void *), 438c2ecf20Sopenharmony_ci int flags) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci struct sigaction sa; 468c2ecf20Sopenharmony_ci memset(&sa, 0, sizeof(sa)); 478c2ecf20Sopenharmony_ci sa.sa_sigaction = handler; 488c2ecf20Sopenharmony_ci sa.sa_flags = SA_SIGINFO | flags; 498c2ecf20Sopenharmony_ci sigemptyset(&sa.sa_mask); 508c2ecf20Sopenharmony_ci if (sigaction(sig, &sa, 0)) 518c2ecf20Sopenharmony_ci err(1, "sigaction"); 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic void clearhandler(int sig) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci struct sigaction sa; 578c2ecf20Sopenharmony_ci memset(&sa, 0, sizeof(sa)); 588c2ecf20Sopenharmony_ci sa.sa_handler = SIG_DFL; 598c2ecf20Sopenharmony_ci sigemptyset(&sa.sa_mask); 608c2ecf20Sopenharmony_ci if (sigaction(sig, &sa, 0)) 618c2ecf20Sopenharmony_ci err(1, "sigaction"); 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci/* State used by our signal handlers. */ 658c2ecf20Sopenharmony_cistatic gregset_t initial_regs; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic volatile unsigned long rip; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic void sigsegv_for_sigreturn_test(int sig, siginfo_t *info, void *ctx_void) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci ucontext_t *ctx = (ucontext_t*)ctx_void; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci if (rip != ctx->uc_mcontext.gregs[REG_RIP]) { 748c2ecf20Sopenharmony_ci printf("[FAIL]\tRequested RIP=0x%lx but got RIP=0x%lx\n", 758c2ecf20Sopenharmony_ci rip, (unsigned long)ctx->uc_mcontext.gregs[REG_RIP]); 768c2ecf20Sopenharmony_ci fflush(stdout); 778c2ecf20Sopenharmony_ci _exit(1); 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci memcpy(&ctx->uc_mcontext.gregs, &initial_regs, sizeof(gregset_t)); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci printf("[OK]\tGot SIGSEGV at RIP=0x%lx\n", rip); 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic void sigusr1(int sig, siginfo_t *info, void *ctx_void) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci ucontext_t *ctx = (ucontext_t*)ctx_void; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci memcpy(&initial_regs, &ctx->uc_mcontext.gregs, sizeof(gregset_t)); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci /* Set IP and CX to match so that SYSRET can happen. */ 928c2ecf20Sopenharmony_ci ctx->uc_mcontext.gregs[REG_RIP] = rip; 938c2ecf20Sopenharmony_ci ctx->uc_mcontext.gregs[REG_RCX] = rip; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci /* R11 and EFLAGS should already match. */ 968c2ecf20Sopenharmony_ci assert(ctx->uc_mcontext.gregs[REG_EFL] == 978c2ecf20Sopenharmony_ci ctx->uc_mcontext.gregs[REG_R11]); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci sethandler(SIGSEGV, sigsegv_for_sigreturn_test, SA_RESETHAND); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci return; 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic void test_sigreturn_to(unsigned long ip) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci rip = ip; 1078c2ecf20Sopenharmony_ci printf("[RUN]\tsigreturn to 0x%lx\n", ip); 1088c2ecf20Sopenharmony_ci raise(SIGUSR1); 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic jmp_buf jmpbuf; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic void sigsegv_for_fallthrough(int sig, siginfo_t *info, void *ctx_void) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci ucontext_t *ctx = (ucontext_t*)ctx_void; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci if (rip != ctx->uc_mcontext.gregs[REG_RIP]) { 1188c2ecf20Sopenharmony_ci printf("[FAIL]\tExpected SIGSEGV at 0x%lx but got RIP=0x%lx\n", 1198c2ecf20Sopenharmony_ci rip, (unsigned long)ctx->uc_mcontext.gregs[REG_RIP]); 1208c2ecf20Sopenharmony_ci fflush(stdout); 1218c2ecf20Sopenharmony_ci _exit(1); 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci siglongjmp(jmpbuf, 1); 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic void test_syscall_fallthrough_to(unsigned long ip) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci void *new_address = (void *)(ip - 4096); 1308c2ecf20Sopenharmony_ci void *ret; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci printf("[RUN]\tTrying a SYSCALL that falls through to 0x%lx\n", ip); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci ret = mremap((void *)current_test_page_addr, 4096, 4096, 1358c2ecf20Sopenharmony_ci MREMAP_MAYMOVE | MREMAP_FIXED, new_address); 1368c2ecf20Sopenharmony_ci if (ret == MAP_FAILED) { 1378c2ecf20Sopenharmony_ci if (ip <= (1UL << 47) - PAGE_SIZE) { 1388c2ecf20Sopenharmony_ci err(1, "mremap to %p", new_address); 1398c2ecf20Sopenharmony_ci } else { 1408c2ecf20Sopenharmony_ci printf("[OK]\tmremap to %p failed\n", new_address); 1418c2ecf20Sopenharmony_ci return; 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci if (ret != new_address) 1468c2ecf20Sopenharmony_ci errx(1, "mremap malfunctioned: asked for %p but got %p\n", 1478c2ecf20Sopenharmony_ci new_address, ret); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci current_test_page_addr = new_address; 1508c2ecf20Sopenharmony_ci rip = ip; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci if (sigsetjmp(jmpbuf, 1) == 0) { 1538c2ecf20Sopenharmony_ci asm volatile ("call *%[syscall_insn]" :: "a" (SYS_getpid), 1548c2ecf20Sopenharmony_ci [syscall_insn] "rm" (ip - 2)); 1558c2ecf20Sopenharmony_ci errx(1, "[FAIL]\tSyscall trampoline returned"); 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci printf("[OK]\tWe survived\n"); 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ciint main() 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci /* 1648c2ecf20Sopenharmony_ci * When the kernel returns from a slow-path syscall, it will 1658c2ecf20Sopenharmony_ci * detect whether SYSRET is appropriate. If it incorrectly 1668c2ecf20Sopenharmony_ci * thinks that SYSRET is appropriate when RIP is noncanonical, 1678c2ecf20Sopenharmony_ci * it'll crash on Intel CPUs. 1688c2ecf20Sopenharmony_ci */ 1698c2ecf20Sopenharmony_ci sethandler(SIGUSR1, sigusr1, 0); 1708c2ecf20Sopenharmony_ci for (int i = 47; i < 64; i++) 1718c2ecf20Sopenharmony_ci test_sigreturn_to(1UL<<i); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci clearhandler(SIGUSR1); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci sethandler(SIGSEGV, sigsegv_for_fallthrough, 0); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci /* One extra test to check that we didn't screw up the mremap logic. */ 1788c2ecf20Sopenharmony_ci test_syscall_fallthrough_to((1UL << 47) - 2*PAGE_SIZE); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci /* These are the interesting cases. */ 1818c2ecf20Sopenharmony_ci for (int i = 47; i < 64; i++) { 1828c2ecf20Sopenharmony_ci test_syscall_fallthrough_to((1UL<<i) - PAGE_SIZE); 1838c2ecf20Sopenharmony_ci test_syscall_fallthrough_to(1UL<<i); 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci return 0; 1878c2ecf20Sopenharmony_ci} 188