1 // SPDX-License-Identifier: GPL-2.0
4 * Copyright 2020, Sandipan Das, IBM Corp.
6 * Test if the signal information reports the correct memory protection
7 * key upon getting a key access violation fault for a page that was
8 * attempted to be protected by two different keys from two competing
9 * threads at the same time.
24 #define PPC_INST_NOP 0x60000000
25 #define PPC_INST_BLR 0x4e800020
26 #define PROT_RWX (PROT_READ | PROT_WRITE | PROT_EXEC)
28 #define NUM_ITERATIONS 1000000
30 static volatile sig_atomic_t perm_pkey
, rest_pkey
;
31 static volatile sig_atomic_t rights
, fault_count
;
32 static volatile unsigned int *volatile fault_addr
;
33 static pthread_barrier_t iteration_barrier
;
35 static void segv_handler(int signum
, siginfo_t
*sinfo
, void *ctx
)
41 pkey
= siginfo_pkey(sinfo
);
43 /* Check if this fault originated from a pkey access violation */
44 if (sinfo
->si_code
!= SEGV_PKUERR
) {
45 sigsafe_err("got a fault for an unexpected reason\n");
49 /* Check if this fault originated from the expected address */
50 if (sinfo
->si_addr
!= (void *) fault_addr
) {
51 sigsafe_err("got a fault for an unexpected address\n");
55 /* Check if this fault originated from the restrictive pkey */
56 if (pkey
!= rest_pkey
) {
57 sigsafe_err("got a fault for an unexpected pkey\n");
61 /* Check if too many faults have occurred for the same iteration */
62 if (fault_count
> 0) {
63 sigsafe_err("got too many faults for the same address\n");
67 pgsize
= getpagesize();
68 pgstart
= (void *) ((unsigned long) fault_addr
& ~(pgsize
- 1));
71 * If the current fault occurred due to lack of execute rights,
72 * reassociate the page with the exec-only pkey since execute
73 * rights cannot be changed directly for the faulting pkey as
74 * IAMR is inaccessible from userspace.
76 * Otherwise, if the current fault occurred due to lack of
77 * read-write rights, change the AMR permission bits for the
80 * This will let the test continue.
82 if (rights
== PKEY_DISABLE_EXECUTE
&&
83 mprotect(pgstart
, pgsize
, PROT_EXEC
))
86 pkey_set_rights(pkey
, 0);
97 static void *protect(void *p
)
105 base
= ((struct region
*) p
)->base
;
106 size
= ((struct region
*) p
)->size
;
109 /* No read, write and execute restrictions */
112 printf("tid %d, pkey permissions are %s\n", tid
, pkey_rights(rights
));
114 /* Allocate the permissive pkey */
115 perm_pkey
= sys_pkey_alloc(0, rights
);
116 FAIL_IF_EXIT(perm_pkey
< 0);
119 * Repeatedly try to protect the common region with a permissive
122 for (i
= 0; i
< NUM_ITERATIONS
; i
++) {
124 * Wait until the other thread has finished allocating the
125 * restrictive pkey or until the next iteration has begun
127 pthread_barrier_wait(&iteration_barrier
);
129 /* Try to associate the permissive pkey with the region */
130 FAIL_IF_EXIT(sys_pkey_mprotect(base
, size
, PROT_RWX
,
134 /* Free the permissive pkey */
135 sys_pkey_free(perm_pkey
);
140 static void *protect_access(void *p
)
142 size_t size
, numinsns
;
147 base
= ((struct region
*) p
)->base
;
148 size
= ((struct region
*) p
)->size
;
149 rights
= ((struct region
*) p
)->rights
;
150 numinsns
= size
/ sizeof(base
[0]);
153 /* Allocate the restrictive pkey */
154 rest_pkey
= sys_pkey_alloc(0, rights
);
155 FAIL_IF_EXIT(rest_pkey
< 0);
157 printf("tid %d, pkey permissions are %s\n", tid
, pkey_rights(rights
));
158 printf("tid %d, %s randomly in range [%p, %p]\n", tid
,
159 (rights
== PKEY_DISABLE_EXECUTE
) ? "execute" :
160 (rights
== PKEY_DISABLE_WRITE
) ? "write" : "read",
161 base
, base
+ numinsns
);
164 * Repeatedly try to protect the common region with a restrictive
165 * pkey and read, write or execute from it
167 for (i
= 0; i
< NUM_ITERATIONS
; i
++) {
169 * Wait until the other thread has finished allocating the
170 * permissive pkey or until the next iteration has begun
172 pthread_barrier_wait(&iteration_barrier
);
174 /* Try to associate the restrictive pkey with the region */
175 FAIL_IF_EXIT(sys_pkey_mprotect(base
, size
, PROT_RWX
,
178 /* Choose a random instruction word address from the region */
179 fault_addr
= base
+ (rand() % numinsns
);
183 /* Read protection test */
184 case PKEY_DISABLE_ACCESS
:
186 * Read an instruction word from the region and
187 * verify if it has not been overwritten to
188 * something unexpected
190 FAIL_IF_EXIT(*fault_addr
!= PPC_INST_NOP
&&
191 *fault_addr
!= PPC_INST_BLR
);
194 /* Write protection test */
195 case PKEY_DISABLE_WRITE
:
197 * Write an instruction word to the region and
198 * verify if the overwrite has succeeded
200 *fault_addr
= PPC_INST_BLR
;
201 FAIL_IF_EXIT(*fault_addr
!= PPC_INST_BLR
);
204 /* Execute protection test */
205 case PKEY_DISABLE_EXECUTE
:
206 /* Jump to the region and execute instructions */
209 : : "r"(fault_addr
) : "ctr", "lr");
214 * Restore the restrictions originally imposed by the
215 * restrictive pkey as the signal handler would have
216 * cleared out the corresponding AMR bits
218 pkey_set_rights(rest_pkey
, rights
);
221 /* Free restrictive pkey */
222 sys_pkey_free(rest_pkey
);
227 static void reset_pkeys(unsigned long rights
)
229 int pkeys
[NR_PKEYS
], i
;
231 /* Exhaustively allocate all available pkeys */
232 for (i
= 0; i
< NR_PKEYS
; i
++)
233 pkeys
[i
] = sys_pkey_alloc(0, rights
);
235 /* Free all allocated pkeys */
236 for (i
= 0; i
< NR_PKEYS
; i
++)
237 sys_pkey_free(pkeys
[i
]);
240 static int test(void)
242 pthread_t prot_thread
, pacc_thread
;
243 struct sigaction act
;
250 ret
= pkeys_unsupported();
254 /* Allocate the region */
255 r
.size
= getpagesize();
256 r
.base
= mmap(NULL
, r
.size
, PROT_RWX
,
257 MAP_PRIVATE
| MAP_ANONYMOUS
, -1, 0);
258 FAIL_IF(r
.base
== MAP_FAILED
);
261 * Fill the region with no-ops with a branch at the end
262 * for returning to the caller
264 numinsns
= r
.size
/ sizeof(r
.base
[0]);
265 for (i
= 0; i
< numinsns
- 1; i
++)
266 r
.base
[i
] = PPC_INST_NOP
;
267 r
.base
[i
] = PPC_INST_BLR
;
269 /* Setup SIGSEGV handler */
271 act
.sa_sigaction
= segv_handler
;
272 FAIL_IF(sigprocmask(SIG_SETMASK
, 0, &act
.sa_mask
) != 0);
273 act
.sa_flags
= SA_SIGINFO
;
275 FAIL_IF(sigaction(SIGSEGV
, &act
, NULL
) != 0);
278 * For these tests, the parent process should clear all bits of
279 * AMR and IAMR, i.e. impose no restrictions, for all available
280 * pkeys. This will be the base for the initial AMR and IAMR
281 * values for all the test thread pairs.
283 * If the AMR and IAMR bits of all available pkeys are cleared
284 * before running the tests and a fault is generated when
285 * attempting to read, write or execute instructions from a
286 * pkey protected region, the pkey responsible for this must be
287 * the one from the protect-and-access thread since the other
288 * one is fully permissive. Despite that, if the pkey reported
289 * by siginfo is not the restrictive pkey, then there must be a
294 /* Setup barrier for protect and protect-and-access threads */
295 FAIL_IF(pthread_attr_init(&attr
) != 0);
296 FAIL_IF(pthread_barrier_init(&iteration_barrier
, NULL
, 2) != 0);
298 /* Setup and start protect and protect-and-read threads */
299 puts("starting thread pair (protect, protect-and-read)");
300 r
.rights
= PKEY_DISABLE_ACCESS
;
301 FAIL_IF(pthread_create(&prot_thread
, &attr
, &protect
, &r
) != 0);
302 FAIL_IF(pthread_create(&pacc_thread
, &attr
, &protect_access
, &r
) != 0);
303 FAIL_IF(pthread_join(prot_thread
, NULL
) != 0);
304 FAIL_IF(pthread_join(pacc_thread
, NULL
) != 0);
306 /* Setup and start protect and protect-and-write threads */
307 puts("starting thread pair (protect, protect-and-write)");
308 r
.rights
= PKEY_DISABLE_WRITE
;
309 FAIL_IF(pthread_create(&prot_thread
, &attr
, &protect
, &r
) != 0);
310 FAIL_IF(pthread_create(&pacc_thread
, &attr
, &protect_access
, &r
) != 0);
311 FAIL_IF(pthread_join(prot_thread
, NULL
) != 0);
312 FAIL_IF(pthread_join(pacc_thread
, NULL
) != 0);
314 /* Setup and start protect and protect-and-execute threads */
315 puts("starting thread pair (protect, protect-and-execute)");
316 r
.rights
= PKEY_DISABLE_EXECUTE
;
317 FAIL_IF(pthread_create(&prot_thread
, &attr
, &protect
, &r
) != 0);
318 FAIL_IF(pthread_create(&pacc_thread
, &attr
, &protect_access
, &r
) != 0);
319 FAIL_IF(pthread_join(prot_thread
, NULL
) != 0);
320 FAIL_IF(pthread_join(pacc_thread
, NULL
) != 0);
323 FAIL_IF(pthread_attr_destroy(&attr
) != 0);
324 FAIL_IF(pthread_barrier_destroy(&iteration_barrier
) != 0);
325 munmap(r
.base
, r
.size
);
332 test_harness(test
, "pkey_siginfo");