xref: /OK3568_Linux_fs/kernel/tools/testing/selftests/powerpc/mm/pkey_siginfo.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun 
3*4882a593Smuzhiyun /*
4*4882a593Smuzhiyun  * Copyright 2020, Sandipan Das, IBM Corp.
5*4882a593Smuzhiyun  *
6*4882a593Smuzhiyun  * Test if the signal information reports the correct memory protection
7*4882a593Smuzhiyun  * key upon getting a key access violation fault for a page that was
8*4882a593Smuzhiyun  * attempted to be protected by two different keys from two competing
9*4882a593Smuzhiyun  * threads at the same time.
10*4882a593Smuzhiyun  */
11*4882a593Smuzhiyun 
12*4882a593Smuzhiyun #define _GNU_SOURCE
13*4882a593Smuzhiyun #include <stdio.h>
14*4882a593Smuzhiyun #include <stdlib.h>
15*4882a593Smuzhiyun #include <string.h>
16*4882a593Smuzhiyun #include <signal.h>
17*4882a593Smuzhiyun 
18*4882a593Smuzhiyun #include <unistd.h>
19*4882a593Smuzhiyun #include <pthread.h>
20*4882a593Smuzhiyun #include <sys/mman.h>
21*4882a593Smuzhiyun 
22*4882a593Smuzhiyun #include "pkeys.h"
23*4882a593Smuzhiyun 
24*4882a593Smuzhiyun #define PPC_INST_NOP	0x60000000
25*4882a593Smuzhiyun #define PPC_INST_BLR	0x4e800020
26*4882a593Smuzhiyun #define PROT_RWX	(PROT_READ | PROT_WRITE | PROT_EXEC)
27*4882a593Smuzhiyun 
28*4882a593Smuzhiyun #define NUM_ITERATIONS	1000000
29*4882a593Smuzhiyun 
30*4882a593Smuzhiyun static volatile sig_atomic_t perm_pkey, rest_pkey;
31*4882a593Smuzhiyun static volatile sig_atomic_t rights, fault_count;
32*4882a593Smuzhiyun static volatile unsigned int *volatile fault_addr;
33*4882a593Smuzhiyun static pthread_barrier_t iteration_barrier;
34*4882a593Smuzhiyun 
segv_handler(int signum,siginfo_t * sinfo,void * ctx)35*4882a593Smuzhiyun static void segv_handler(int signum, siginfo_t *sinfo, void *ctx)
36*4882a593Smuzhiyun {
37*4882a593Smuzhiyun 	void *pgstart;
38*4882a593Smuzhiyun 	size_t pgsize;
39*4882a593Smuzhiyun 	int pkey;
40*4882a593Smuzhiyun 
41*4882a593Smuzhiyun 	pkey = siginfo_pkey(sinfo);
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun 	/* Check if this fault originated from a pkey access violation */
44*4882a593Smuzhiyun 	if (sinfo->si_code != SEGV_PKUERR) {
45*4882a593Smuzhiyun 		sigsafe_err("got a fault for an unexpected reason\n");
46*4882a593Smuzhiyun 		_exit(1);
47*4882a593Smuzhiyun 	}
48*4882a593Smuzhiyun 
49*4882a593Smuzhiyun 	/* Check if this fault originated from the expected address */
50*4882a593Smuzhiyun 	if (sinfo->si_addr != (void *) fault_addr) {
51*4882a593Smuzhiyun 		sigsafe_err("got a fault for an unexpected address\n");
52*4882a593Smuzhiyun 		_exit(1);
53*4882a593Smuzhiyun 	}
54*4882a593Smuzhiyun 
55*4882a593Smuzhiyun 	/* Check if this fault originated from the restrictive pkey */
56*4882a593Smuzhiyun 	if (pkey != rest_pkey) {
57*4882a593Smuzhiyun 		sigsafe_err("got a fault for an unexpected pkey\n");
58*4882a593Smuzhiyun 		_exit(1);
59*4882a593Smuzhiyun 	}
60*4882a593Smuzhiyun 
61*4882a593Smuzhiyun 	/* Check if too many faults have occurred for the same iteration */
62*4882a593Smuzhiyun 	if (fault_count > 0) {
63*4882a593Smuzhiyun 		sigsafe_err("got too many faults for the same address\n");
64*4882a593Smuzhiyun 		_exit(1);
65*4882a593Smuzhiyun 	}
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun 	pgsize = getpagesize();
68*4882a593Smuzhiyun 	pgstart = (void *) ((unsigned long) fault_addr & ~(pgsize - 1));
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun 	/*
71*4882a593Smuzhiyun 	 * If the current fault occurred due to lack of execute rights,
72*4882a593Smuzhiyun 	 * reassociate the page with the exec-only pkey since execute
73*4882a593Smuzhiyun 	 * rights cannot be changed directly for the faulting pkey as
74*4882a593Smuzhiyun 	 * IAMR is inaccessible from userspace.
75*4882a593Smuzhiyun 	 *
76*4882a593Smuzhiyun 	 * Otherwise, if the current fault occurred due to lack of
77*4882a593Smuzhiyun 	 * read-write rights, change the AMR permission bits for the
78*4882a593Smuzhiyun 	 * pkey.
79*4882a593Smuzhiyun 	 *
80*4882a593Smuzhiyun 	 * This will let the test continue.
81*4882a593Smuzhiyun 	 */
82*4882a593Smuzhiyun 	if (rights == PKEY_DISABLE_EXECUTE &&
83*4882a593Smuzhiyun 	    mprotect(pgstart, pgsize, PROT_EXEC))
84*4882a593Smuzhiyun 		_exit(1);
85*4882a593Smuzhiyun 	else
86*4882a593Smuzhiyun 		pkey_set_rights(pkey, 0);
87*4882a593Smuzhiyun 
88*4882a593Smuzhiyun 	fault_count++;
89*4882a593Smuzhiyun }
90*4882a593Smuzhiyun 
91*4882a593Smuzhiyun struct region {
92*4882a593Smuzhiyun 	unsigned long rights;
93*4882a593Smuzhiyun 	unsigned int *base;
94*4882a593Smuzhiyun 	size_t size;
95*4882a593Smuzhiyun };
96*4882a593Smuzhiyun 
protect(void * p)97*4882a593Smuzhiyun static void *protect(void *p)
98*4882a593Smuzhiyun {
99*4882a593Smuzhiyun 	unsigned long rights;
100*4882a593Smuzhiyun 	unsigned int *base;
101*4882a593Smuzhiyun 	size_t size;
102*4882a593Smuzhiyun 	int tid, i;
103*4882a593Smuzhiyun 
104*4882a593Smuzhiyun 	tid = gettid();
105*4882a593Smuzhiyun 	base = ((struct region *) p)->base;
106*4882a593Smuzhiyun 	size = ((struct region *) p)->size;
107*4882a593Smuzhiyun 	FAIL_IF_EXIT(!base);
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun 	/* No read, write and execute restrictions */
110*4882a593Smuzhiyun 	rights = 0;
111*4882a593Smuzhiyun 
112*4882a593Smuzhiyun 	printf("tid %d, pkey permissions are %s\n", tid, pkey_rights(rights));
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun 	/* Allocate the permissive pkey */
115*4882a593Smuzhiyun 	perm_pkey = sys_pkey_alloc(0, rights);
116*4882a593Smuzhiyun 	FAIL_IF_EXIT(perm_pkey < 0);
117*4882a593Smuzhiyun 
118*4882a593Smuzhiyun 	/*
119*4882a593Smuzhiyun 	 * Repeatedly try to protect the common region with a permissive
120*4882a593Smuzhiyun 	 * pkey
121*4882a593Smuzhiyun 	 */
122*4882a593Smuzhiyun 	for (i = 0; i < NUM_ITERATIONS; i++) {
123*4882a593Smuzhiyun 		/*
124*4882a593Smuzhiyun 		 * Wait until the other thread has finished allocating the
125*4882a593Smuzhiyun 		 * restrictive pkey or until the next iteration has begun
126*4882a593Smuzhiyun 		 */
127*4882a593Smuzhiyun 		pthread_barrier_wait(&iteration_barrier);
128*4882a593Smuzhiyun 
129*4882a593Smuzhiyun 		/* Try to associate the permissive pkey with the region */
130*4882a593Smuzhiyun 		FAIL_IF_EXIT(sys_pkey_mprotect(base, size, PROT_RWX,
131*4882a593Smuzhiyun 					       perm_pkey));
132*4882a593Smuzhiyun 	}
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun 	/* Free the permissive pkey */
135*4882a593Smuzhiyun 	sys_pkey_free(perm_pkey);
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun 	return NULL;
138*4882a593Smuzhiyun }
139*4882a593Smuzhiyun 
protect_access(void * p)140*4882a593Smuzhiyun static void *protect_access(void *p)
141*4882a593Smuzhiyun {
142*4882a593Smuzhiyun 	size_t size, numinsns;
143*4882a593Smuzhiyun 	unsigned int *base;
144*4882a593Smuzhiyun 	int tid, i;
145*4882a593Smuzhiyun 
146*4882a593Smuzhiyun 	tid = gettid();
147*4882a593Smuzhiyun 	base = ((struct region *) p)->base;
148*4882a593Smuzhiyun 	size = ((struct region *) p)->size;
149*4882a593Smuzhiyun 	rights = ((struct region *) p)->rights;
150*4882a593Smuzhiyun 	numinsns = size / sizeof(base[0]);
151*4882a593Smuzhiyun 	FAIL_IF_EXIT(!base);
152*4882a593Smuzhiyun 
153*4882a593Smuzhiyun 	/* Allocate the restrictive pkey */
154*4882a593Smuzhiyun 	rest_pkey = sys_pkey_alloc(0, rights);
155*4882a593Smuzhiyun 	FAIL_IF_EXIT(rest_pkey < 0);
156*4882a593Smuzhiyun 
157*4882a593Smuzhiyun 	printf("tid %d, pkey permissions are %s\n", tid, pkey_rights(rights));
158*4882a593Smuzhiyun 	printf("tid %d, %s randomly in range [%p, %p]\n", tid,
159*4882a593Smuzhiyun 	       (rights == PKEY_DISABLE_EXECUTE) ? "execute" :
160*4882a593Smuzhiyun 	       (rights == PKEY_DISABLE_WRITE)  ? "write" : "read",
161*4882a593Smuzhiyun 	       base, base + numinsns);
162*4882a593Smuzhiyun 
163*4882a593Smuzhiyun 	/*
164*4882a593Smuzhiyun 	 * Repeatedly try to protect the common region with a restrictive
165*4882a593Smuzhiyun 	 * pkey and read, write or execute from it
166*4882a593Smuzhiyun 	 */
167*4882a593Smuzhiyun 	for (i = 0; i < NUM_ITERATIONS; i++) {
168*4882a593Smuzhiyun 		/*
169*4882a593Smuzhiyun 		 * Wait until the other thread has finished allocating the
170*4882a593Smuzhiyun 		 * permissive pkey or until the next iteration has begun
171*4882a593Smuzhiyun 		 */
172*4882a593Smuzhiyun 		pthread_barrier_wait(&iteration_barrier);
173*4882a593Smuzhiyun 
174*4882a593Smuzhiyun 		/* Try to associate the restrictive pkey with the region */
175*4882a593Smuzhiyun 		FAIL_IF_EXIT(sys_pkey_mprotect(base, size, PROT_RWX,
176*4882a593Smuzhiyun 					       rest_pkey));
177*4882a593Smuzhiyun 
178*4882a593Smuzhiyun 		/* Choose a random instruction word address from the region */
179*4882a593Smuzhiyun 		fault_addr = base + (rand() % numinsns);
180*4882a593Smuzhiyun 		fault_count = 0;
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 		switch (rights) {
183*4882a593Smuzhiyun 		/* Read protection test */
184*4882a593Smuzhiyun 		case PKEY_DISABLE_ACCESS:
185*4882a593Smuzhiyun 			/*
186*4882a593Smuzhiyun 			 * Read an instruction word from the region and
187*4882a593Smuzhiyun 			 * verify if it has not been overwritten to
188*4882a593Smuzhiyun 			 * something unexpected
189*4882a593Smuzhiyun 			 */
190*4882a593Smuzhiyun 			FAIL_IF_EXIT(*fault_addr != PPC_INST_NOP &&
191*4882a593Smuzhiyun 				     *fault_addr != PPC_INST_BLR);
192*4882a593Smuzhiyun 			break;
193*4882a593Smuzhiyun 
194*4882a593Smuzhiyun 		/* Write protection test */
195*4882a593Smuzhiyun 		case PKEY_DISABLE_WRITE:
196*4882a593Smuzhiyun 			/*
197*4882a593Smuzhiyun 			 * Write an instruction word to the region and
198*4882a593Smuzhiyun 			 * verify if the overwrite has succeeded
199*4882a593Smuzhiyun 			 */
200*4882a593Smuzhiyun 			*fault_addr = PPC_INST_BLR;
201*4882a593Smuzhiyun 			FAIL_IF_EXIT(*fault_addr != PPC_INST_BLR);
202*4882a593Smuzhiyun 			break;
203*4882a593Smuzhiyun 
204*4882a593Smuzhiyun 		/* Execute protection test */
205*4882a593Smuzhiyun 		case PKEY_DISABLE_EXECUTE:
206*4882a593Smuzhiyun 			/* Jump to the region and execute instructions */
207*4882a593Smuzhiyun 			asm volatile(
208*4882a593Smuzhiyun 				"mtctr	%0; bctrl"
209*4882a593Smuzhiyun 				: : "r"(fault_addr) : "ctr", "lr");
210*4882a593Smuzhiyun 			break;
211*4882a593Smuzhiyun 		}
212*4882a593Smuzhiyun 
213*4882a593Smuzhiyun 		/*
214*4882a593Smuzhiyun 		 * Restore the restrictions originally imposed by the
215*4882a593Smuzhiyun 		 * restrictive pkey as the signal handler would have
216*4882a593Smuzhiyun 		 * cleared out the corresponding AMR bits
217*4882a593Smuzhiyun 		 */
218*4882a593Smuzhiyun 		pkey_set_rights(rest_pkey, rights);
219*4882a593Smuzhiyun 	}
220*4882a593Smuzhiyun 
221*4882a593Smuzhiyun 	/* Free restrictive pkey */
222*4882a593Smuzhiyun 	sys_pkey_free(rest_pkey);
223*4882a593Smuzhiyun 
224*4882a593Smuzhiyun 	return NULL;
225*4882a593Smuzhiyun }
226*4882a593Smuzhiyun 
reset_pkeys(unsigned long rights)227*4882a593Smuzhiyun static void reset_pkeys(unsigned long rights)
228*4882a593Smuzhiyun {
229*4882a593Smuzhiyun 	int pkeys[NR_PKEYS], i;
230*4882a593Smuzhiyun 
231*4882a593Smuzhiyun 	/* Exhaustively allocate all available pkeys */
232*4882a593Smuzhiyun 	for (i = 0; i < NR_PKEYS; i++)
233*4882a593Smuzhiyun 		pkeys[i] = sys_pkey_alloc(0, rights);
234*4882a593Smuzhiyun 
235*4882a593Smuzhiyun 	/* Free all allocated pkeys */
236*4882a593Smuzhiyun 	for (i = 0; i < NR_PKEYS; i++)
237*4882a593Smuzhiyun 		sys_pkey_free(pkeys[i]);
238*4882a593Smuzhiyun }
239*4882a593Smuzhiyun 
test(void)240*4882a593Smuzhiyun static int test(void)
241*4882a593Smuzhiyun {
242*4882a593Smuzhiyun 	pthread_t prot_thread, pacc_thread;
243*4882a593Smuzhiyun 	struct sigaction act;
244*4882a593Smuzhiyun 	pthread_attr_t attr;
245*4882a593Smuzhiyun 	size_t numinsns;
246*4882a593Smuzhiyun 	struct region r;
247*4882a593Smuzhiyun 	int ret, i;
248*4882a593Smuzhiyun 
249*4882a593Smuzhiyun 	srand(time(NULL));
250*4882a593Smuzhiyun 	ret = pkeys_unsupported();
251*4882a593Smuzhiyun 	if (ret)
252*4882a593Smuzhiyun 		return ret;
253*4882a593Smuzhiyun 
254*4882a593Smuzhiyun 	/* Allocate the region */
255*4882a593Smuzhiyun 	r.size = getpagesize();
256*4882a593Smuzhiyun 	r.base = mmap(NULL, r.size, PROT_RWX,
257*4882a593Smuzhiyun 		      MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
258*4882a593Smuzhiyun 	FAIL_IF(r.base == MAP_FAILED);
259*4882a593Smuzhiyun 
260*4882a593Smuzhiyun 	/*
261*4882a593Smuzhiyun 	 * Fill the region with no-ops with a branch at the end
262*4882a593Smuzhiyun 	 * for returning to the caller
263*4882a593Smuzhiyun 	 */
264*4882a593Smuzhiyun 	numinsns = r.size / sizeof(r.base[0]);
265*4882a593Smuzhiyun 	for (i = 0; i < numinsns - 1; i++)
266*4882a593Smuzhiyun 		r.base[i] = PPC_INST_NOP;
267*4882a593Smuzhiyun 	r.base[i] = PPC_INST_BLR;
268*4882a593Smuzhiyun 
269*4882a593Smuzhiyun 	/* Setup SIGSEGV handler */
270*4882a593Smuzhiyun 	act.sa_handler = 0;
271*4882a593Smuzhiyun 	act.sa_sigaction = segv_handler;
272*4882a593Smuzhiyun 	FAIL_IF(sigprocmask(SIG_SETMASK, 0, &act.sa_mask) != 0);
273*4882a593Smuzhiyun 	act.sa_flags = SA_SIGINFO;
274*4882a593Smuzhiyun 	act.sa_restorer = 0;
275*4882a593Smuzhiyun 	FAIL_IF(sigaction(SIGSEGV, &act, NULL) != 0);
276*4882a593Smuzhiyun 
277*4882a593Smuzhiyun 	/*
278*4882a593Smuzhiyun 	 * For these tests, the parent process should clear all bits of
279*4882a593Smuzhiyun 	 * AMR and IAMR, i.e. impose no restrictions, for all available
280*4882a593Smuzhiyun 	 * pkeys. This will be the base for the initial AMR and IAMR
281*4882a593Smuzhiyun 	 * values for all the test thread pairs.
282*4882a593Smuzhiyun 	 *
283*4882a593Smuzhiyun 	 * If the AMR and IAMR bits of all available pkeys are cleared
284*4882a593Smuzhiyun 	 * before running the tests and a fault is generated when
285*4882a593Smuzhiyun 	 * attempting to read, write or execute instructions from a
286*4882a593Smuzhiyun 	 * pkey protected region, the pkey responsible for this must be
287*4882a593Smuzhiyun 	 * the one from the protect-and-access thread since the other
288*4882a593Smuzhiyun 	 * one is fully permissive. Despite that, if the pkey reported
289*4882a593Smuzhiyun 	 * by siginfo is not the restrictive pkey, then there must be a
290*4882a593Smuzhiyun 	 * kernel bug.
291*4882a593Smuzhiyun 	 */
292*4882a593Smuzhiyun 	reset_pkeys(0);
293*4882a593Smuzhiyun 
294*4882a593Smuzhiyun 	/* Setup barrier for protect and protect-and-access threads */
295*4882a593Smuzhiyun 	FAIL_IF(pthread_attr_init(&attr) != 0);
296*4882a593Smuzhiyun 	FAIL_IF(pthread_barrier_init(&iteration_barrier, NULL, 2) != 0);
297*4882a593Smuzhiyun 
298*4882a593Smuzhiyun 	/* Setup and start protect and protect-and-read threads */
299*4882a593Smuzhiyun 	puts("starting thread pair (protect, protect-and-read)");
300*4882a593Smuzhiyun 	r.rights = PKEY_DISABLE_ACCESS;
301*4882a593Smuzhiyun 	FAIL_IF(pthread_create(&prot_thread, &attr, &protect, &r) != 0);
302*4882a593Smuzhiyun 	FAIL_IF(pthread_create(&pacc_thread, &attr, &protect_access, &r) != 0);
303*4882a593Smuzhiyun 	FAIL_IF(pthread_join(prot_thread, NULL) != 0);
304*4882a593Smuzhiyun 	FAIL_IF(pthread_join(pacc_thread, NULL) != 0);
305*4882a593Smuzhiyun 
306*4882a593Smuzhiyun 	/* Setup and start protect and protect-and-write threads */
307*4882a593Smuzhiyun 	puts("starting thread pair (protect, protect-and-write)");
308*4882a593Smuzhiyun 	r.rights = PKEY_DISABLE_WRITE;
309*4882a593Smuzhiyun 	FAIL_IF(pthread_create(&prot_thread, &attr, &protect, &r) != 0);
310*4882a593Smuzhiyun 	FAIL_IF(pthread_create(&pacc_thread, &attr, &protect_access, &r) != 0);
311*4882a593Smuzhiyun 	FAIL_IF(pthread_join(prot_thread, NULL) != 0);
312*4882a593Smuzhiyun 	FAIL_IF(pthread_join(pacc_thread, NULL) != 0);
313*4882a593Smuzhiyun 
314*4882a593Smuzhiyun 	/* Setup and start protect and protect-and-execute threads */
315*4882a593Smuzhiyun 	puts("starting thread pair (protect, protect-and-execute)");
316*4882a593Smuzhiyun 	r.rights = PKEY_DISABLE_EXECUTE;
317*4882a593Smuzhiyun 	FAIL_IF(pthread_create(&prot_thread, &attr, &protect, &r) != 0);
318*4882a593Smuzhiyun 	FAIL_IF(pthread_create(&pacc_thread, &attr, &protect_access, &r) != 0);
319*4882a593Smuzhiyun 	FAIL_IF(pthread_join(prot_thread, NULL) != 0);
320*4882a593Smuzhiyun 	FAIL_IF(pthread_join(pacc_thread, NULL) != 0);
321*4882a593Smuzhiyun 
322*4882a593Smuzhiyun 	/* Cleanup */
323*4882a593Smuzhiyun 	FAIL_IF(pthread_attr_destroy(&attr) != 0);
324*4882a593Smuzhiyun 	FAIL_IF(pthread_barrier_destroy(&iteration_barrier) != 0);
325*4882a593Smuzhiyun 	munmap(r.base, r.size);
326*4882a593Smuzhiyun 
327*4882a593Smuzhiyun 	return 0;
328*4882a593Smuzhiyun }
329*4882a593Smuzhiyun 
main(void)330*4882a593Smuzhiyun int main(void)
331*4882a593Smuzhiyun {
332*4882a593Smuzhiyun 	return test_harness(test, "pkey_siginfo");
333*4882a593Smuzhiyun }
334