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

TOMOYO Linux Cross Reference
Linux/tools/testing/selftests/powerpc/mm/pkey_siginfo.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
  2 
  3 /*
  4  * Copyright 2020, Sandipan Das, IBM Corp.
  5  *
  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.
 10  */
 11 
 12 #define _GNU_SOURCE
 13 #include <stdio.h>
 14 #include <stdlib.h>
 15 #include <string.h>
 16 #include <signal.h>
 17 
 18 #include <unistd.h>
 19 #include <pthread.h>
 20 #include <sys/mman.h>
 21 
 22 #include "pkeys.h"
 23 
 24 #define PPC_INST_NOP    0x60000000
 25 #define PPC_INST_BLR    0x4e800020
 26 #define PROT_RWX        (PROT_READ | PROT_WRITE | PROT_EXEC)
 27 
 28 #define NUM_ITERATIONS  1000000
 29 
 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;
 34 
 35 static void segv_handler(int signum, siginfo_t *sinfo, void *ctx)
 36 {
 37         void *pgstart;
 38         size_t pgsize;
 39         int pkey;
 40 
 41         pkey = siginfo_pkey(sinfo);
 42 
 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");
 46                 _exit(1);
 47         }
 48 
 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");
 52                 _exit(1);
 53         }
 54 
 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");
 58                 _exit(1);
 59         }
 60 
 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");
 64                 _exit(1);
 65         }
 66 
 67         pgsize = getpagesize();
 68         pgstart = (void *) ((unsigned long) fault_addr & ~(pgsize - 1));
 69 
 70         /*
 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.
 75          *
 76          * Otherwise, if the current fault occurred due to lack of
 77          * read-write rights, change the AMR permission bits for the
 78          * pkey.
 79          *
 80          * This will let the test continue.
 81          */
 82         if (rights == PKEY_DISABLE_EXECUTE &&
 83             mprotect(pgstart, pgsize, PROT_EXEC))
 84                 _exit(1);
 85         else
 86                 pkey_set_rights(pkey, 0);
 87 
 88         fault_count++;
 89 }
 90 
 91 struct region {
 92         unsigned long rights;
 93         unsigned int *base;
 94         size_t size;
 95 };
 96 
 97 static void *protect(void *p)
 98 {
 99         unsigned long rights;
100         unsigned int *base;
101         size_t size;
102         int tid, i;
103 
104         tid = gettid();
105         base = ((struct region *) p)->base;
106         size = ((struct region *) p)->size;
107         FAIL_IF_EXIT(!base);
108 
109         /* No read, write and execute restrictions */
110         rights = 0;
111 
112         printf("tid %d, pkey permissions are %s\n", tid, pkey_rights(rights));
113 
114         /* Allocate the permissive pkey */
115         perm_pkey = sys_pkey_alloc(0, rights);
116         FAIL_IF_EXIT(perm_pkey < 0);
117 
118         /*
119          * Repeatedly try to protect the common region with a permissive
120          * pkey
121          */
122         for (i = 0; i < NUM_ITERATIONS; i++) {
123                 /*
124                  * Wait until the other thread has finished allocating the
125                  * restrictive pkey or until the next iteration has begun
126                  */
127                 pthread_barrier_wait(&iteration_barrier);
128 
129                 /* Try to associate the permissive pkey with the region */
130                 FAIL_IF_EXIT(sys_pkey_mprotect(base, size, PROT_RWX,
131                                                perm_pkey));
132         }
133 
134         /* Free the permissive pkey */
135         sys_pkey_free(perm_pkey);
136 
137         return NULL;
138 }
139 
140 static void *protect_access(void *p)
141 {
142         size_t size, numinsns;
143         unsigned int *base;
144         int tid, i;
145 
146         tid = gettid();
147         base = ((struct region *) p)->base;
148         size = ((struct region *) p)->size;
149         rights = ((struct region *) p)->rights;
150         numinsns = size / sizeof(base[0]);
151         FAIL_IF_EXIT(!base);
152 
153         /* Allocate the restrictive pkey */
154         rest_pkey = sys_pkey_alloc(0, rights);
155         FAIL_IF_EXIT(rest_pkey < 0);
156 
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);
162 
163         /*
164          * Repeatedly try to protect the common region with a restrictive
165          * pkey and read, write or execute from it
166          */
167         for (i = 0; i < NUM_ITERATIONS; i++) {
168                 /*
169                  * Wait until the other thread has finished allocating the
170                  * permissive pkey or until the next iteration has begun
171                  */
172                 pthread_barrier_wait(&iteration_barrier);
173 
174                 /* Try to associate the restrictive pkey with the region */
175                 FAIL_IF_EXIT(sys_pkey_mprotect(base, size, PROT_RWX,
176                                                rest_pkey));
177 
178                 /* Choose a random instruction word address from the region */
179                 fault_addr = base + (rand() % numinsns);
180                 fault_count = 0;
181 
182                 switch (rights) {
183                 /* Read protection test */
184                 case PKEY_DISABLE_ACCESS:
185                         /*
186                          * Read an instruction word from the region and
187                          * verify if it has not been overwritten to
188                          * something unexpected
189                          */
190                         FAIL_IF_EXIT(*fault_addr != PPC_INST_NOP &&
191                                      *fault_addr != PPC_INST_BLR);
192                         break;
193 
194                 /* Write protection test */
195                 case PKEY_DISABLE_WRITE:
196                         /*
197                          * Write an instruction word to the region and
198                          * verify if the overwrite has succeeded
199                          */
200                         *fault_addr = PPC_INST_BLR;
201                         FAIL_IF_EXIT(*fault_addr != PPC_INST_BLR);
202                         break;
203 
204                 /* Execute protection test */
205                 case PKEY_DISABLE_EXECUTE:
206                         /* Jump to the region and execute instructions */
207                         asm volatile(
208                                 "mtctr  %0; bctrl"
209                                 : : "r"(fault_addr) : "ctr", "lr");
210                         break;
211                 }
212 
213                 /*
214                  * Restore the restrictions originally imposed by the
215                  * restrictive pkey as the signal handler would have
216                  * cleared out the corresponding AMR bits
217                  */
218                 pkey_set_rights(rest_pkey, rights);
219         }
220 
221         /* Free restrictive pkey */
222         sys_pkey_free(rest_pkey);
223 
224         return NULL;
225 }
226 
227 static void reset_pkeys(unsigned long rights)
228 {
229         int pkeys[NR_PKEYS], i;
230 
231         /* Exhaustively allocate all available pkeys */
232         for (i = 0; i < NR_PKEYS; i++)
233                 pkeys[i] = sys_pkey_alloc(0, rights);
234 
235         /* Free all allocated pkeys */
236         for (i = 0; i < NR_PKEYS; i++)
237                 sys_pkey_free(pkeys[i]);
238 }
239 
240 static int test(void)
241 {
242         pthread_t prot_thread, pacc_thread;
243         struct sigaction act;
244         pthread_attr_t attr;
245         size_t numinsns;
246         struct region r;
247         int ret, i;
248 
249         srand(time(NULL));
250         ret = pkeys_unsupported();
251         if (ret)
252                 return ret;
253 
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);
259 
260         /*
261          * Fill the region with no-ops with a branch at the end
262          * for returning to the caller
263          */
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;
268 
269         /* Setup SIGSEGV handler */
270         act.sa_handler = 0;
271         act.sa_sigaction = segv_handler;
272         FAIL_IF(sigprocmask(SIG_SETMASK, 0, &act.sa_mask) != 0);
273         act.sa_flags = SA_SIGINFO;
274         act.sa_restorer = 0;
275         FAIL_IF(sigaction(SIGSEGV, &act, NULL) != 0);
276 
277         /*
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.
282          *
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
290          * kernel bug.
291          */
292         reset_pkeys(0);
293 
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);
297 
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);
305 
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);
313 
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);
321 
322         /* Cleanup */
323         FAIL_IF(pthread_attr_destroy(&attr) != 0);
324         FAIL_IF(pthread_barrier_destroy(&iteration_barrier) != 0);
325         munmap(r.base, r.size);
326 
327         return 0;
328 }
329 
330 int main(void)
331 {
332         return test_harness(test, "pkey_siginfo");
333 }
334 

~ [ 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