~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/testing/selftests/x86/sysret_rip.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php