1
2
3
4
5
6
7
8
9
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
30static volatile sig_atomic_t perm_pkey, rest_pkey;
31static volatile sig_atomic_t rights, fault_count;
32static volatile unsigned int *volatile fault_addr;
33static pthread_barrier_t iteration_barrier;
34
35static 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
44 if (sinfo->si_code != SEGV_PKUERR) {
45 sigsafe_err("got a fault for an unexpected reason\n");
46 _exit(1);
47 }
48
49
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
56 if (pkey != rest_pkey) {
57 sigsafe_err("got a fault for an unexpected pkey\n");
58 _exit(1);
59 }
60
61
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
72
73
74
75
76
77
78
79
80
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
91struct region {
92 unsigned long rights;
93 unsigned int *base;
94 size_t size;
95};
96
97static 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
110 rights = 0;
111
112 printf("tid %d, pkey permissions are %s\n", tid, pkey_rights(rights));
113
114
115 perm_pkey = sys_pkey_alloc(0, rights);
116 FAIL_IF_EXIT(perm_pkey < 0);
117
118
119
120
121
122 for (i = 0; i < NUM_ITERATIONS; i++) {
123
124
125
126
127 pthread_barrier_wait(&iteration_barrier);
128
129
130 FAIL_IF_EXIT(sys_pkey_mprotect(base, size, PROT_RWX,
131 perm_pkey));
132 }
133
134
135 sys_pkey_free(perm_pkey);
136
137 return NULL;
138}
139
140static 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
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
165
166
167 for (i = 0; i < NUM_ITERATIONS; i++) {
168
169
170
171
172 pthread_barrier_wait(&iteration_barrier);
173
174
175 FAIL_IF_EXIT(sys_pkey_mprotect(base, size, PROT_RWX,
176 rest_pkey));
177
178
179 fault_addr = base + (rand() % numinsns);
180 fault_count = 0;
181
182 switch (rights) {
183
184 case PKEY_DISABLE_ACCESS:
185
186
187
188
189
190 FAIL_IF_EXIT(*fault_addr != PPC_INST_NOP &&
191 *fault_addr != PPC_INST_BLR);
192 break;
193
194
195 case PKEY_DISABLE_WRITE:
196
197
198
199
200 *fault_addr = PPC_INST_BLR;
201 FAIL_IF_EXIT(*fault_addr != PPC_INST_BLR);
202 break;
203
204
205 case PKEY_DISABLE_EXECUTE:
206
207 asm volatile(
208 "mtctr %0; bctrl"
209 : : "r"(fault_addr) : "ctr", "lr");
210 break;
211 }
212
213
214
215
216
217
218 pkey_set_rights(rest_pkey, rights);
219 }
220
221
222 sys_pkey_free(rest_pkey);
223
224 return NULL;
225}
226
227static void reset_pkeys(unsigned long rights)
228{
229 int pkeys[NR_PKEYS], i;
230
231
232 for (i = 0; i < NR_PKEYS; i++)
233 pkeys[i] = sys_pkey_alloc(0, rights);
234
235
236 for (i = 0; i < NR_PKEYS; i++)
237 sys_pkey_free(pkeys[i]);
238}
239
240static 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
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
262
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
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
279
280
281
282
283
284
285
286
287
288
289
290
291
292 reset_pkeys(0);
293
294
295 FAIL_IF(pthread_attr_init(&attr) != 0);
296 FAIL_IF(pthread_barrier_init(&iteration_barrier, NULL, 2) != 0);
297
298
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
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
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
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
330int main(void)
331{
332 return test_harness(test, "pkey_siginfo");
333}
334