xref: /linux/tools/testing/selftests/mm/pkey_sighandler_tests.c (revision 3a39d672e7f48b8d6b91a09afa4b55352773b4b5)
1*6998a73eSKeith Lucas // SPDX-License-Identifier: GPL-2.0
2*6998a73eSKeith Lucas /*
3*6998a73eSKeith Lucas  * Tests Memory Protection Keys (see Documentation/core-api/protection-keys.rst)
4*6998a73eSKeith Lucas  *
5*6998a73eSKeith Lucas  * The testcases in this file exercise various flows related to signal handling,
6*6998a73eSKeith Lucas  * using an alternate signal stack, with the default pkey (pkey 0) disabled.
7*6998a73eSKeith Lucas  *
8*6998a73eSKeith Lucas  * Compile with:
9*6998a73eSKeith Lucas  * gcc -mxsave      -o pkey_sighandler_tests -O2 -g -std=gnu99 -pthread -Wall pkey_sighandler_tests.c -I../../../../tools/include -lrt -ldl -lm
10*6998a73eSKeith Lucas  * gcc -mxsave -m32 -o pkey_sighandler_tests -O2 -g -std=gnu99 -pthread -Wall pkey_sighandler_tests.c -I../../../../tools/include -lrt -ldl -lm
11*6998a73eSKeith Lucas  */
12*6998a73eSKeith Lucas #define _GNU_SOURCE
13*6998a73eSKeith Lucas #define __SANE_USERSPACE_TYPES__
14*6998a73eSKeith Lucas #include <errno.h>
15*6998a73eSKeith Lucas #include <sys/syscall.h>
16*6998a73eSKeith Lucas #include <string.h>
17*6998a73eSKeith Lucas #include <stdio.h>
18*6998a73eSKeith Lucas #include <stdint.h>
19*6998a73eSKeith Lucas #include <stdbool.h>
20*6998a73eSKeith Lucas #include <signal.h>
21*6998a73eSKeith Lucas #include <assert.h>
22*6998a73eSKeith Lucas #include <stdlib.h>
23*6998a73eSKeith Lucas #include <sys/mman.h>
24*6998a73eSKeith Lucas #include <sys/types.h>
25*6998a73eSKeith Lucas #include <sys/stat.h>
26*6998a73eSKeith Lucas #include <unistd.h>
27*6998a73eSKeith Lucas #include <pthread.h>
28*6998a73eSKeith Lucas #include <limits.h>
29*6998a73eSKeith Lucas 
30*6998a73eSKeith Lucas #include "pkey-helpers.h"
31*6998a73eSKeith Lucas 
32*6998a73eSKeith Lucas #define STACK_SIZE PTHREAD_STACK_MIN
33*6998a73eSKeith Lucas 
expected_pkey_fault(int pkey)34*6998a73eSKeith Lucas void expected_pkey_fault(int pkey) {}
35*6998a73eSKeith Lucas 
36*6998a73eSKeith Lucas pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
37*6998a73eSKeith Lucas pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
38*6998a73eSKeith Lucas siginfo_t siginfo = {0};
39*6998a73eSKeith Lucas 
40*6998a73eSKeith Lucas /*
41*6998a73eSKeith Lucas  * We need to use inline assembly instead of glibc's syscall because glibc's
42*6998a73eSKeith Lucas  * syscall will attempt to access the PLT in order to call a library function
43*6998a73eSKeith Lucas  * which is protected by MPK 0 which we don't have access to.
44*6998a73eSKeith Lucas  */
45*6998a73eSKeith Lucas static inline __always_inline
syscall_raw(long n,long a1,long a2,long a3,long a4,long a5,long a6)46*6998a73eSKeith Lucas long syscall_raw(long n, long a1, long a2, long a3, long a4, long a5, long a6)
47*6998a73eSKeith Lucas {
48*6998a73eSKeith Lucas 	unsigned long ret;
49*6998a73eSKeith Lucas #ifdef __x86_64__
50*6998a73eSKeith Lucas 	register long r10 asm("r10") = a4;
51*6998a73eSKeith Lucas 	register long r8 asm("r8") = a5;
52*6998a73eSKeith Lucas 	register long r9 asm("r9") = a6;
53*6998a73eSKeith Lucas 	asm volatile ("syscall"
54*6998a73eSKeith Lucas 		      : "=a"(ret)
55*6998a73eSKeith Lucas 		      : "a"(n), "D"(a1), "S"(a2), "d"(a3), "r"(r10), "r"(r8), "r"(r9)
56*6998a73eSKeith Lucas 		      : "rcx", "r11", "memory");
57*6998a73eSKeith Lucas #elif defined __i386__
58*6998a73eSKeith Lucas 	asm volatile ("int $0x80"
59*6998a73eSKeith Lucas 		      : "=a"(ret)
60*6998a73eSKeith Lucas 		      : "a"(n), "b"(a1), "c"(a2), "d"(a3), "S"(a4), "D"(a5)
61*6998a73eSKeith Lucas 		      : "memory");
62*6998a73eSKeith Lucas #else
63*6998a73eSKeith Lucas # error syscall_raw() not implemented
64*6998a73eSKeith Lucas #endif
65*6998a73eSKeith Lucas 	return ret;
66*6998a73eSKeith Lucas }
67*6998a73eSKeith Lucas 
sigsegv_handler(int signo,siginfo_t * info,void * ucontext)68*6998a73eSKeith Lucas static void sigsegv_handler(int signo, siginfo_t *info, void *ucontext)
69*6998a73eSKeith Lucas {
70*6998a73eSKeith Lucas 	pthread_mutex_lock(&mutex);
71*6998a73eSKeith Lucas 
72*6998a73eSKeith Lucas 	memcpy(&siginfo, info, sizeof(siginfo_t));
73*6998a73eSKeith Lucas 
74*6998a73eSKeith Lucas 	pthread_cond_signal(&cond);
75*6998a73eSKeith Lucas 	pthread_mutex_unlock(&mutex);
76*6998a73eSKeith Lucas 
77*6998a73eSKeith Lucas 	syscall_raw(SYS_exit, 0, 0, 0, 0, 0, 0);
78*6998a73eSKeith Lucas }
79*6998a73eSKeith Lucas 
sigusr1_handler(int signo,siginfo_t * info,void * ucontext)80*6998a73eSKeith Lucas static void sigusr1_handler(int signo, siginfo_t *info, void *ucontext)
81*6998a73eSKeith Lucas {
82*6998a73eSKeith Lucas 	pthread_mutex_lock(&mutex);
83*6998a73eSKeith Lucas 
84*6998a73eSKeith Lucas 	memcpy(&siginfo, info, sizeof(siginfo_t));
85*6998a73eSKeith Lucas 
86*6998a73eSKeith Lucas 	pthread_cond_signal(&cond);
87*6998a73eSKeith Lucas 	pthread_mutex_unlock(&mutex);
88*6998a73eSKeith Lucas }
89*6998a73eSKeith Lucas 
sigusr2_handler(int signo,siginfo_t * info,void * ucontext)90*6998a73eSKeith Lucas static void sigusr2_handler(int signo, siginfo_t *info, void *ucontext)
91*6998a73eSKeith Lucas {
92*6998a73eSKeith Lucas 	/*
93*6998a73eSKeith Lucas 	 * pkru should be the init_pkru value which enabled MPK 0 so
94*6998a73eSKeith Lucas 	 * we can use library functions.
95*6998a73eSKeith Lucas 	 */
96*6998a73eSKeith Lucas 	printf("%s invoked.\n", __func__);
97*6998a73eSKeith Lucas }
98*6998a73eSKeith Lucas 
raise_sigusr2(void)99*6998a73eSKeith Lucas static void raise_sigusr2(void)
100*6998a73eSKeith Lucas {
101*6998a73eSKeith Lucas 	pid_t tid = 0;
102*6998a73eSKeith Lucas 
103*6998a73eSKeith Lucas 	tid = syscall_raw(SYS_gettid, 0, 0, 0, 0, 0, 0);
104*6998a73eSKeith Lucas 
105*6998a73eSKeith Lucas 	syscall_raw(SYS_tkill, tid, SIGUSR2, 0, 0, 0, 0);
106*6998a73eSKeith Lucas 
107*6998a73eSKeith Lucas 	/*
108*6998a73eSKeith Lucas 	 * We should return from the signal handler here and be able to
109*6998a73eSKeith Lucas 	 * return to the interrupted thread.
110*6998a73eSKeith Lucas 	 */
111*6998a73eSKeith Lucas }
112*6998a73eSKeith Lucas 
thread_segv_with_pkey0_disabled(void * ptr)113*6998a73eSKeith Lucas static void *thread_segv_with_pkey0_disabled(void *ptr)
114*6998a73eSKeith Lucas {
115*6998a73eSKeith Lucas 	/* Disable MPK 0 (and all others too) */
116*6998a73eSKeith Lucas 	__write_pkey_reg(0x55555555);
117*6998a73eSKeith Lucas 
118*6998a73eSKeith Lucas 	/* Segfault (with SEGV_MAPERR) */
119*6998a73eSKeith Lucas 	*(int *) (0x1) = 1;
120*6998a73eSKeith Lucas 	return NULL;
121*6998a73eSKeith Lucas }
122*6998a73eSKeith Lucas 
thread_segv_pkuerr_stack(void * ptr)123*6998a73eSKeith Lucas static void *thread_segv_pkuerr_stack(void *ptr)
124*6998a73eSKeith Lucas {
125*6998a73eSKeith Lucas 	/* Disable MPK 0 (and all others too) */
126*6998a73eSKeith Lucas 	__write_pkey_reg(0x55555555);
127*6998a73eSKeith Lucas 
128*6998a73eSKeith Lucas 	/* After we disable MPK 0, we can't access the stack to return */
129*6998a73eSKeith Lucas 	return NULL;
130*6998a73eSKeith Lucas }
131*6998a73eSKeith Lucas 
thread_segv_maperr_ptr(void * ptr)132*6998a73eSKeith Lucas static void *thread_segv_maperr_ptr(void *ptr)
133*6998a73eSKeith Lucas {
134*6998a73eSKeith Lucas 	stack_t *stack = ptr;
135*6998a73eSKeith Lucas 	int *bad = (int *)1;
136*6998a73eSKeith Lucas 
137*6998a73eSKeith Lucas 	/*
138*6998a73eSKeith Lucas 	 * Setup alternate signal stack, which should be pkey_mprotect()ed by
139*6998a73eSKeith Lucas 	 * MPK 0. The thread's stack cannot be used for signals because it is
140*6998a73eSKeith Lucas 	 * not accessible by the default init_pkru value of 0x55555554.
141*6998a73eSKeith Lucas 	 */
142*6998a73eSKeith Lucas 	syscall_raw(SYS_sigaltstack, (long)stack, 0, 0, 0, 0, 0);
143*6998a73eSKeith Lucas 
144*6998a73eSKeith Lucas 	/* Disable MPK 0.  Only MPK 1 is enabled. */
145*6998a73eSKeith Lucas 	__write_pkey_reg(0x55555551);
146*6998a73eSKeith Lucas 
147*6998a73eSKeith Lucas 	/* Segfault */
148*6998a73eSKeith Lucas 	*bad = 1;
149*6998a73eSKeith Lucas 	syscall_raw(SYS_exit, 0, 0, 0, 0, 0, 0);
150*6998a73eSKeith Lucas 	return NULL;
151*6998a73eSKeith Lucas }
152*6998a73eSKeith Lucas 
153*6998a73eSKeith Lucas /*
154*6998a73eSKeith Lucas  * Verify that the sigsegv handler is invoked when pkey 0 is disabled.
155*6998a73eSKeith Lucas  * Note that the new thread stack and the alternate signal stack is
156*6998a73eSKeith Lucas  * protected by MPK 0.
157*6998a73eSKeith Lucas  */
test_sigsegv_handler_with_pkey0_disabled(void)158*6998a73eSKeith Lucas static void test_sigsegv_handler_with_pkey0_disabled(void)
159*6998a73eSKeith Lucas {
160*6998a73eSKeith Lucas 	struct sigaction sa;
161*6998a73eSKeith Lucas 	pthread_attr_t attr;
162*6998a73eSKeith Lucas 	pthread_t thr;
163*6998a73eSKeith Lucas 
164*6998a73eSKeith Lucas 	sa.sa_flags = SA_SIGINFO;
165*6998a73eSKeith Lucas 
166*6998a73eSKeith Lucas 	sa.sa_sigaction = sigsegv_handler;
167*6998a73eSKeith Lucas 	sigemptyset(&sa.sa_mask);
168*6998a73eSKeith Lucas 	if (sigaction(SIGSEGV, &sa, NULL) == -1) {
169*6998a73eSKeith Lucas 		perror("sigaction");
170*6998a73eSKeith Lucas 		exit(EXIT_FAILURE);
171*6998a73eSKeith Lucas 	}
172*6998a73eSKeith Lucas 
173*6998a73eSKeith Lucas 	memset(&siginfo, 0, sizeof(siginfo));
174*6998a73eSKeith Lucas 
175*6998a73eSKeith Lucas 	pthread_attr_init(&attr);
176*6998a73eSKeith Lucas 	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
177*6998a73eSKeith Lucas 
178*6998a73eSKeith Lucas 	pthread_create(&thr, &attr, thread_segv_with_pkey0_disabled, NULL);
179*6998a73eSKeith Lucas 
180*6998a73eSKeith Lucas 	pthread_mutex_lock(&mutex);
181*6998a73eSKeith Lucas 	while (siginfo.si_signo == 0)
182*6998a73eSKeith Lucas 		pthread_cond_wait(&cond, &mutex);
183*6998a73eSKeith Lucas 	pthread_mutex_unlock(&mutex);
184*6998a73eSKeith Lucas 
185*6998a73eSKeith Lucas 	ksft_test_result(siginfo.si_signo == SIGSEGV &&
186*6998a73eSKeith Lucas 			 siginfo.si_code == SEGV_MAPERR &&
187*6998a73eSKeith Lucas 			 siginfo.si_addr == (void *)1,
188*6998a73eSKeith Lucas 			 "%s\n", __func__);
189*6998a73eSKeith Lucas }
190*6998a73eSKeith Lucas 
191*6998a73eSKeith Lucas /*
192*6998a73eSKeith Lucas  * Verify that the sigsegv handler is invoked when pkey 0 is disabled.
193*6998a73eSKeith Lucas  * Note that the new thread stack and the alternate signal stack is
194*6998a73eSKeith Lucas  * protected by MPK 0, which renders them inaccessible when MPK 0
195*6998a73eSKeith Lucas  * is disabled. So just the return from the thread should cause a
196*6998a73eSKeith Lucas  * segfault with SEGV_PKUERR.
197*6998a73eSKeith Lucas  */
test_sigsegv_handler_cannot_access_stack(void)198*6998a73eSKeith Lucas static void test_sigsegv_handler_cannot_access_stack(void)
199*6998a73eSKeith Lucas {
200*6998a73eSKeith Lucas 	struct sigaction sa;
201*6998a73eSKeith Lucas 	pthread_attr_t attr;
202*6998a73eSKeith Lucas 	pthread_t thr;
203*6998a73eSKeith Lucas 
204*6998a73eSKeith Lucas 	sa.sa_flags = SA_SIGINFO;
205*6998a73eSKeith Lucas 
206*6998a73eSKeith Lucas 	sa.sa_sigaction = sigsegv_handler;
207*6998a73eSKeith Lucas 	sigemptyset(&sa.sa_mask);
208*6998a73eSKeith Lucas 	if (sigaction(SIGSEGV, &sa, NULL) == -1) {
209*6998a73eSKeith Lucas 		perror("sigaction");
210*6998a73eSKeith Lucas 		exit(EXIT_FAILURE);
211*6998a73eSKeith Lucas 	}
212*6998a73eSKeith Lucas 
213*6998a73eSKeith Lucas 	memset(&siginfo, 0, sizeof(siginfo));
214*6998a73eSKeith Lucas 
215*6998a73eSKeith Lucas 	pthread_attr_init(&attr);
216*6998a73eSKeith Lucas 	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
217*6998a73eSKeith Lucas 
218*6998a73eSKeith Lucas 	pthread_create(&thr, &attr, thread_segv_pkuerr_stack, NULL);
219*6998a73eSKeith Lucas 
220*6998a73eSKeith Lucas 	pthread_mutex_lock(&mutex);
221*6998a73eSKeith Lucas 	while (siginfo.si_signo == 0)
222*6998a73eSKeith Lucas 		pthread_cond_wait(&cond, &mutex);
223*6998a73eSKeith Lucas 	pthread_mutex_unlock(&mutex);
224*6998a73eSKeith Lucas 
225*6998a73eSKeith Lucas 	ksft_test_result(siginfo.si_signo == SIGSEGV &&
226*6998a73eSKeith Lucas 			 siginfo.si_code == SEGV_PKUERR,
227*6998a73eSKeith Lucas 			 "%s\n", __func__);
228*6998a73eSKeith Lucas }
229*6998a73eSKeith Lucas 
230*6998a73eSKeith Lucas /*
231*6998a73eSKeith Lucas  * Verify that the sigsegv handler that uses an alternate signal stack
232*6998a73eSKeith Lucas  * is correctly invoked for a thread which uses a non-zero MPK to protect
233*6998a73eSKeith Lucas  * its own stack, and disables all other MPKs (including 0).
234*6998a73eSKeith Lucas  */
test_sigsegv_handler_with_different_pkey_for_stack(void)235*6998a73eSKeith Lucas static void test_sigsegv_handler_with_different_pkey_for_stack(void)
236*6998a73eSKeith Lucas {
237*6998a73eSKeith Lucas 	struct sigaction sa;
238*6998a73eSKeith Lucas 	static stack_t sigstack;
239*6998a73eSKeith Lucas 	void *stack;
240*6998a73eSKeith Lucas 	int pkey;
241*6998a73eSKeith Lucas 	int parent_pid = 0;
242*6998a73eSKeith Lucas 	int child_pid = 0;
243*6998a73eSKeith Lucas 
244*6998a73eSKeith Lucas 	sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
245*6998a73eSKeith Lucas 
246*6998a73eSKeith Lucas 	sa.sa_sigaction = sigsegv_handler;
247*6998a73eSKeith Lucas 
248*6998a73eSKeith Lucas 	sigemptyset(&sa.sa_mask);
249*6998a73eSKeith Lucas 	if (sigaction(SIGSEGV, &sa, NULL) == -1) {
250*6998a73eSKeith Lucas 		perror("sigaction");
251*6998a73eSKeith Lucas 		exit(EXIT_FAILURE);
252*6998a73eSKeith Lucas 	}
253*6998a73eSKeith Lucas 
254*6998a73eSKeith Lucas 	stack = mmap(0, STACK_SIZE, PROT_READ | PROT_WRITE,
255*6998a73eSKeith Lucas 		     MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
256*6998a73eSKeith Lucas 
257*6998a73eSKeith Lucas 	assert(stack != MAP_FAILED);
258*6998a73eSKeith Lucas 
259*6998a73eSKeith Lucas 	/* Allow access to MPK 0 and MPK 1 */
260*6998a73eSKeith Lucas 	__write_pkey_reg(0x55555550);
261*6998a73eSKeith Lucas 
262*6998a73eSKeith Lucas 	/* Protect the new stack with MPK 1 */
263*6998a73eSKeith Lucas 	pkey = pkey_alloc(0, 0);
264*6998a73eSKeith Lucas 	pkey_mprotect(stack, STACK_SIZE, PROT_READ | PROT_WRITE, pkey);
265*6998a73eSKeith Lucas 
266*6998a73eSKeith Lucas 	/* Set up alternate signal stack that will use the default MPK */
267*6998a73eSKeith Lucas 	sigstack.ss_sp = mmap(0, STACK_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC,
268*6998a73eSKeith Lucas 			      MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
269*6998a73eSKeith Lucas 	sigstack.ss_flags = 0;
270*6998a73eSKeith Lucas 	sigstack.ss_size = STACK_SIZE;
271*6998a73eSKeith Lucas 
272*6998a73eSKeith Lucas 	memset(&siginfo, 0, sizeof(siginfo));
273*6998a73eSKeith Lucas 
274*6998a73eSKeith Lucas 	/* Use clone to avoid newer glibcs using rseq on new threads */
275*6998a73eSKeith Lucas 	long ret = syscall_raw(SYS_clone,
276*6998a73eSKeith Lucas 			       CLONE_VM | CLONE_FS | CLONE_FILES |
277*6998a73eSKeith Lucas 			       CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM |
278*6998a73eSKeith Lucas 			       CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID |
279*6998a73eSKeith Lucas 			       CLONE_DETACHED,
280*6998a73eSKeith Lucas 			       (long) ((char *)(stack) + STACK_SIZE),
281*6998a73eSKeith Lucas 			       (long) &parent_pid,
282*6998a73eSKeith Lucas 			       (long) &child_pid, 0, 0);
283*6998a73eSKeith Lucas 
284*6998a73eSKeith Lucas 	if (ret < 0) {
285*6998a73eSKeith Lucas 		errno = -ret;
286*6998a73eSKeith Lucas 		perror("clone");
287*6998a73eSKeith Lucas 	} else if (ret == 0) {
288*6998a73eSKeith Lucas 		thread_segv_maperr_ptr(&sigstack);
289*6998a73eSKeith Lucas 		syscall_raw(SYS_exit, 0, 0, 0, 0, 0, 0);
290*6998a73eSKeith Lucas 	}
291*6998a73eSKeith Lucas 
292*6998a73eSKeith Lucas 	pthread_mutex_lock(&mutex);
293*6998a73eSKeith Lucas 	while (siginfo.si_signo == 0)
294*6998a73eSKeith Lucas 		pthread_cond_wait(&cond, &mutex);
295*6998a73eSKeith Lucas 	pthread_mutex_unlock(&mutex);
296*6998a73eSKeith Lucas 
297*6998a73eSKeith Lucas 	ksft_test_result(siginfo.si_signo == SIGSEGV &&
298*6998a73eSKeith Lucas 			 siginfo.si_code == SEGV_MAPERR &&
299*6998a73eSKeith Lucas 			 siginfo.si_addr == (void *)1,
300*6998a73eSKeith Lucas 			 "%s\n", __func__);
301*6998a73eSKeith Lucas }
302*6998a73eSKeith Lucas 
303*6998a73eSKeith Lucas /*
304*6998a73eSKeith Lucas  * Verify that the PKRU value set by the application is correctly
305*6998a73eSKeith Lucas  * restored upon return from signal handling.
306*6998a73eSKeith Lucas  */
test_pkru_preserved_after_sigusr1(void)307*6998a73eSKeith Lucas static void test_pkru_preserved_after_sigusr1(void)
308*6998a73eSKeith Lucas {
309*6998a73eSKeith Lucas 	struct sigaction sa;
310*6998a73eSKeith Lucas 	unsigned long pkru = 0x45454544;
311*6998a73eSKeith Lucas 
312*6998a73eSKeith Lucas 	sa.sa_flags = SA_SIGINFO;
313*6998a73eSKeith Lucas 
314*6998a73eSKeith Lucas 	sa.sa_sigaction = sigusr1_handler;
315*6998a73eSKeith Lucas 	sigemptyset(&sa.sa_mask);
316*6998a73eSKeith Lucas 	if (sigaction(SIGUSR1, &sa, NULL) == -1) {
317*6998a73eSKeith Lucas 		perror("sigaction");
318*6998a73eSKeith Lucas 		exit(EXIT_FAILURE);
319*6998a73eSKeith Lucas 	}
320*6998a73eSKeith Lucas 
321*6998a73eSKeith Lucas 	memset(&siginfo, 0, sizeof(siginfo));
322*6998a73eSKeith Lucas 
323*6998a73eSKeith Lucas 	__write_pkey_reg(pkru);
324*6998a73eSKeith Lucas 
325*6998a73eSKeith Lucas 	raise(SIGUSR1);
326*6998a73eSKeith Lucas 
327*6998a73eSKeith Lucas 	pthread_mutex_lock(&mutex);
328*6998a73eSKeith Lucas 	while (siginfo.si_signo == 0)
329*6998a73eSKeith Lucas 		pthread_cond_wait(&cond, &mutex);
330*6998a73eSKeith Lucas 	pthread_mutex_unlock(&mutex);
331*6998a73eSKeith Lucas 
332*6998a73eSKeith Lucas 	/* Ensure the pkru value is the same after returning from signal. */
333*6998a73eSKeith Lucas 	ksft_test_result(pkru == __read_pkey_reg() &&
334*6998a73eSKeith Lucas 			 siginfo.si_signo == SIGUSR1,
335*6998a73eSKeith Lucas 			 "%s\n", __func__);
336*6998a73eSKeith Lucas }
337*6998a73eSKeith Lucas 
thread_sigusr2_self(void * ptr)338*6998a73eSKeith Lucas static noinline void *thread_sigusr2_self(void *ptr)
339*6998a73eSKeith Lucas {
340*6998a73eSKeith Lucas 	/*
341*6998a73eSKeith Lucas 	 * A const char array like "Resuming after SIGUSR2" won't be stored on
342*6998a73eSKeith Lucas 	 * the stack and the code could access it via an offset from the program
343*6998a73eSKeith Lucas 	 * counter. This makes sure it's on the function's stack frame.
344*6998a73eSKeith Lucas 	 */
345*6998a73eSKeith Lucas 	char str[] = {'R', 'e', 's', 'u', 'm', 'i', 'n', 'g', ' ',
346*6998a73eSKeith Lucas 		'a', 'f', 't', 'e', 'r', ' ',
347*6998a73eSKeith Lucas 		'S', 'I', 'G', 'U', 'S', 'R', '2',
348*6998a73eSKeith Lucas 		'.', '.', '.', '\n', '\0'};
349*6998a73eSKeith Lucas 	stack_t *stack = ptr;
350*6998a73eSKeith Lucas 
351*6998a73eSKeith Lucas 	/*
352*6998a73eSKeith Lucas 	 * Setup alternate signal stack, which should be pkey_mprotect()ed by
353*6998a73eSKeith Lucas 	 * MPK 0. The thread's stack cannot be used for signals because it is
354*6998a73eSKeith Lucas 	 * not accessible by the default init_pkru value of 0x55555554.
355*6998a73eSKeith Lucas 	 */
356*6998a73eSKeith Lucas 	syscall(SYS_sigaltstack, (long)stack, 0, 0, 0, 0, 0);
357*6998a73eSKeith Lucas 
358*6998a73eSKeith Lucas 	/* Disable MPK 0.  Only MPK 2 is enabled. */
359*6998a73eSKeith Lucas 	__write_pkey_reg(0x55555545);
360*6998a73eSKeith Lucas 
361*6998a73eSKeith Lucas 	raise_sigusr2();
362*6998a73eSKeith Lucas 
363*6998a73eSKeith Lucas 	/* Do something, to show the thread resumed execution after the signal */
364*6998a73eSKeith Lucas 	syscall_raw(SYS_write, 1, (long) str, sizeof(str) - 1, 0, 0, 0);
365*6998a73eSKeith Lucas 
366*6998a73eSKeith Lucas 	/*
367*6998a73eSKeith Lucas 	 * We can't return to test_pkru_sigreturn because it
368*6998a73eSKeith Lucas 	 * will attempt to use a %rbp value which is on the stack
369*6998a73eSKeith Lucas 	 * of the main thread.
370*6998a73eSKeith Lucas 	 */
371*6998a73eSKeith Lucas 	syscall_raw(SYS_exit, 0, 0, 0, 0, 0, 0);
372*6998a73eSKeith Lucas 	return NULL;
373*6998a73eSKeith Lucas }
374*6998a73eSKeith Lucas 
375*6998a73eSKeith Lucas /*
376*6998a73eSKeith Lucas  * Verify that sigreturn is able to restore altstack even if the thread had
377*6998a73eSKeith Lucas  * disabled pkey 0.
378*6998a73eSKeith Lucas  */
test_pkru_sigreturn(void)379*6998a73eSKeith Lucas static void test_pkru_sigreturn(void)
380*6998a73eSKeith Lucas {
381*6998a73eSKeith Lucas 	struct sigaction sa = {0};
382*6998a73eSKeith Lucas 	static stack_t sigstack;
383*6998a73eSKeith Lucas 	void *stack;
384*6998a73eSKeith Lucas 	int pkey;
385*6998a73eSKeith Lucas 	int parent_pid = 0;
386*6998a73eSKeith Lucas 	int child_pid = 0;
387*6998a73eSKeith Lucas 
388*6998a73eSKeith Lucas 	sa.sa_handler = SIG_DFL;
389*6998a73eSKeith Lucas 	sa.sa_flags = 0;
390*6998a73eSKeith Lucas 	sigemptyset(&sa.sa_mask);
391*6998a73eSKeith Lucas 
392*6998a73eSKeith Lucas 	/*
393*6998a73eSKeith Lucas 	 * For this testcase, we do not want to handle SIGSEGV. Reset handler
394*6998a73eSKeith Lucas 	 * to default so that the application can crash if it receives SIGSEGV.
395*6998a73eSKeith Lucas 	 */
396*6998a73eSKeith Lucas 	if (sigaction(SIGSEGV, &sa, NULL) == -1) {
397*6998a73eSKeith Lucas 		perror("sigaction");
398*6998a73eSKeith Lucas 		exit(EXIT_FAILURE);
399*6998a73eSKeith Lucas 	}
400*6998a73eSKeith Lucas 
401*6998a73eSKeith Lucas 	sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
402*6998a73eSKeith Lucas 	sa.sa_sigaction = sigusr2_handler;
403*6998a73eSKeith Lucas 	sigemptyset(&sa.sa_mask);
404*6998a73eSKeith Lucas 
405*6998a73eSKeith Lucas 	if (sigaction(SIGUSR2, &sa, NULL) == -1) {
406*6998a73eSKeith Lucas 		perror("sigaction");
407*6998a73eSKeith Lucas 		exit(EXIT_FAILURE);
408*6998a73eSKeith Lucas 	}
409*6998a73eSKeith Lucas 
410*6998a73eSKeith Lucas 	stack = mmap(0, STACK_SIZE, PROT_READ | PROT_WRITE,
411*6998a73eSKeith Lucas 		     MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
412*6998a73eSKeith Lucas 
413*6998a73eSKeith Lucas 	assert(stack != MAP_FAILED);
414*6998a73eSKeith Lucas 
415*6998a73eSKeith Lucas 	/*
416*6998a73eSKeith Lucas 	 * Allow access to MPK 0 and MPK 2. The child thread (to be created
417*6998a73eSKeith Lucas 	 * later in this flow) will have its stack protected by MPK 2, whereas
418*6998a73eSKeith Lucas 	 * the current thread's stack is protected by the default MPK 0. Hence
419*6998a73eSKeith Lucas 	 * both need to be enabled.
420*6998a73eSKeith Lucas 	 */
421*6998a73eSKeith Lucas 	__write_pkey_reg(0x55555544);
422*6998a73eSKeith Lucas 
423*6998a73eSKeith Lucas 	/* Protect the stack with MPK 2 */
424*6998a73eSKeith Lucas 	pkey = pkey_alloc(0, 0);
425*6998a73eSKeith Lucas 	pkey_mprotect(stack, STACK_SIZE, PROT_READ | PROT_WRITE, pkey);
426*6998a73eSKeith Lucas 
427*6998a73eSKeith Lucas 	/* Set up alternate signal stack that will use the default MPK */
428*6998a73eSKeith Lucas 	sigstack.ss_sp = mmap(0, STACK_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC,
429*6998a73eSKeith Lucas 			      MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
430*6998a73eSKeith Lucas 	sigstack.ss_flags = 0;
431*6998a73eSKeith Lucas 	sigstack.ss_size = STACK_SIZE;
432*6998a73eSKeith Lucas 
433*6998a73eSKeith Lucas 	/* Use clone to avoid newer glibcs using rseq on new threads */
434*6998a73eSKeith Lucas 	long ret = syscall_raw(SYS_clone,
435*6998a73eSKeith Lucas 			       CLONE_VM | CLONE_FS | CLONE_FILES |
436*6998a73eSKeith Lucas 			       CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM |
437*6998a73eSKeith Lucas 			       CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID |
438*6998a73eSKeith Lucas 			       CLONE_DETACHED,
439*6998a73eSKeith Lucas 			       (long) ((char *)(stack) + STACK_SIZE),
440*6998a73eSKeith Lucas 			       (long) &parent_pid,
441*6998a73eSKeith Lucas 			       (long) &child_pid, 0, 0);
442*6998a73eSKeith Lucas 
443*6998a73eSKeith Lucas 	if (ret < 0) {
444*6998a73eSKeith Lucas 		errno = -ret;
445*6998a73eSKeith Lucas 		perror("clone");
446*6998a73eSKeith Lucas 	}  else if (ret == 0) {
447*6998a73eSKeith Lucas 		thread_sigusr2_self(&sigstack);
448*6998a73eSKeith Lucas 		syscall_raw(SYS_exit, 0, 0, 0, 0, 0, 0);
449*6998a73eSKeith Lucas 	}
450*6998a73eSKeith Lucas 
451*6998a73eSKeith Lucas 	child_pid =  ret;
452*6998a73eSKeith Lucas 	/* Check that thread exited */
453*6998a73eSKeith Lucas 	do {
454*6998a73eSKeith Lucas 		sched_yield();
455*6998a73eSKeith Lucas 		ret = syscall_raw(SYS_tkill, child_pid, 0, 0, 0, 0, 0);
456*6998a73eSKeith Lucas 	} while (ret != -ESRCH && ret != -EINVAL);
457*6998a73eSKeith Lucas 
458*6998a73eSKeith Lucas 	ksft_test_result_pass("%s\n", __func__);
459*6998a73eSKeith Lucas }
460*6998a73eSKeith Lucas 
461*6998a73eSKeith Lucas static void (*pkey_tests[])(void) = {
462*6998a73eSKeith Lucas 	test_sigsegv_handler_with_pkey0_disabled,
463*6998a73eSKeith Lucas 	test_sigsegv_handler_cannot_access_stack,
464*6998a73eSKeith Lucas 	test_sigsegv_handler_with_different_pkey_for_stack,
465*6998a73eSKeith Lucas 	test_pkru_preserved_after_sigusr1,
466*6998a73eSKeith Lucas 	test_pkru_sigreturn
467*6998a73eSKeith Lucas };
468*6998a73eSKeith Lucas 
main(int argc,char * argv[])469*6998a73eSKeith Lucas int main(int argc, char *argv[])
470*6998a73eSKeith Lucas {
471*6998a73eSKeith Lucas 	int i;
472*6998a73eSKeith Lucas 
473*6998a73eSKeith Lucas 	ksft_print_header();
474*6998a73eSKeith Lucas 	ksft_set_plan(ARRAY_SIZE(pkey_tests));
475*6998a73eSKeith Lucas 
476*6998a73eSKeith Lucas 	for (i = 0; i < ARRAY_SIZE(pkey_tests); i++)
477*6998a73eSKeith Lucas 		(*pkey_tests[i])();
478*6998a73eSKeith Lucas 
479*6998a73eSKeith Lucas 	ksft_finished();
480*6998a73eSKeith Lucas 	return 0;
481*6998a73eSKeith Lucas }
482