xref: /openbmc/linux/tools/testing/selftests/x86/sysret_rip.c (revision 6606021401032919c559a829a5d273ba1741b434)
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