]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tools/test/stress2/misc/pkru.sh
less: upgrade to v590.
[FreeBSD/FreeBSD.git] / tools / test / stress2 / misc / pkru.sh
1 #/bin/sh
2 #
3 # SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 #
5 # Copyright (c) 2019 Konstantin Belousov <kib@FreeBSD.org>
6 #
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions
9 # are met:
10 # 1. Redistributions of source code must retain the above copyright
11 #    notice, this list of conditions and the following disclaimer.
12 # 2. Redistributions in binary form must reproduce the above copyright
13 #    notice, this list of conditions and the following disclaimer in the
14 #    documentation and/or other materials provided with the distribution.
15 #
16 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 # ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 # SUCH DAMAGE.
27 #
28
29 # Test scenario for Intel userspace protection keys feature on Skylake Xeons
30
31 grep -qw PKU /var/run/dmesg.boot || exit 0
32 cd /tmp
33 cat > /tmp/pkru_exec.c <<EOF
34 /* $Id: pkru_exec.c,v 1.4 2019/01/12 04:55:57 kostik Exp kostik $ */
35 /*
36  * cc -Wall -Wextra -g -O -o pkru_fork64 pkru_fork.c
37  * Run it with LD_BIND_NOW=1.
38  */
39
40 #include <sys/types.h>
41 #include <sys/mman.h>
42 #include <sys/time.h>
43 #include <sys/resource.h>
44 #include <machine/sysarch.h>
45 #include <err.h>
46 #include <signal.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <unistd.h>
51
52 #ifdef TEST_COMPILE
53 int x86_pkru_get_perm(unsigned int keyidx, int *access, int *modify);
54 int x86_pkru_set_perm(unsigned int keyidx, int access, int modify);
55 int x86_pkru_protect_range(void *addr, unsigned long len, unsigned int keyidx,
56     int flag);
57 int x86_pkru_unprotect_range(void *addr, unsigned long len);
58 uint32_t rdpkru(void);
59 void wrpkru(uint32_t);
60 #define AMD64_PKRU_PERSIST      0x0001
61 #endif
62
63 extern char **environ;
64
65 #define OPKEY   1
66
67 int
68 main(void)
69 {
70         char *args[3] = {
71             "/bin/date",
72             NULL
73         };
74         struct rlimit rl;
75
76         if (getrlimit(RLIMIT_STACK, &rl) != 0)
77                 err(1, "getrlimit RLIMIT_STACK");
78         if (x86_pkru_protect_range(0, 0x800000000000 - rl.rlim_max, OPKEY,
79             AMD64_PKRU_PERSIST) != 0)
80                 err(1, "x86_pkru_protect_range");
81         if (x86_pkru_set_perm(1, 1, 0) != 0)
82                 err(1, "x86_pkru_set_perm");
83         execve("/bin/date", args, environ);
84 }
85 EOF
86 cc -Wall -Wextra -g -O -o pkru_exec64 pkru_exec.c || exit 1
87 cc -Wall -Wextra -g -O -o pkru_exec32 pkru_exec.c -m32 || exit 1
88 rm pkru_exec.c
89 echo "Expect: Segmentation fault (core dumped)"
90 LD_BIND_NOW=1 ./pkru_exec64
91 LD_BIND_NOW=1 ./pkru_exec32
92 rm -f pkru_exec64 pkru_exec32 pkru_exec64.core pkru_exec32.core
93
94 cat > /tmp/pkru_fork.c <<EOF
95 /* $Id: pkru_fork.c,v 1.2 2019/01/12 03:39:42 kostik Exp kostik $ */
96 /* cc -Wall -Wextra -g -O -o pkru_fork64 pkru_fork.c */
97
98 #include <sys/types.h>
99 #include <sys/mman.h>
100 #include <sys/wait.h>
101 #include <machine/cpufunc.h>
102 #include <machine/sysarch.h>
103 #include <x86/fpu.h>
104 #include <err.h>
105 #include <signal.h>
106 #include <stdio.h>
107 #include <stdlib.h>
108 #include <string.h>
109 #include <unistd.h>
110
111 #ifdef TEST_COMPILE
112 int x86_pkru_get_perm(unsigned int keyidx, int *access, int *modify);
113 int x86_pkru_set_perm(unsigned int keyidx, int access, int modify);
114 int x86_pkru_protect_range(void *addr, unsigned long len, unsigned int keyidx,
115     int flag);
116 int x86_pkru_unprotect_range(void *addr, unsigned long len);
117 uint32_t rdpkru(void);
118 void wrpkru(uint32_t);
119 #endif
120
121 static volatile char *mapping;
122
123 #define OPKEY   1
124
125 int
126 main(void)
127 {
128         int error;
129         pid_t child;
130
131         mapping = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE,
132             MAP_PRIVATE | MAP_ANON, -1, 0);
133         if (mapping == MAP_FAILED)
134                 err(1, "mmap");
135         error = x86_pkru_protect_range((void *)mapping, getpagesize(),
136             OPKEY, 0);
137         if (error != 0)
138                 err(1, "x86_pkru_protect_range");
139         error = x86_pkru_set_perm(OPKEY, 0, 0);
140         if (error != 0)
141                 err(1, "x86_pkru_set_perm");
142         child = fork();
143         if (child == -1)
144                 err(1, "fork");
145         if (child == 0) {
146                 *mapping = 0;
147                 printf("Still alive, pkru did not worked after fork");
148         }
149         waitpid(child, NULL, 0);
150 }
151 EOF
152 cc -Wall -Wextra -g -O -o pkru_fork64 pkru_fork.c || exit 1
153 cc -Wall -Wextra -g -O -o pkru_fork32 -m32 pkru_fork.c || exit 1
154 rm pkru_fork.c
155 ./pkru_fork64
156 ./pkru_fork32
157 rm -f pkru_fork64 pkru_fork64.core pkru_fork32 pkru_fork32.core
158
159 cat > /tmp/pkru_perm.c <<EOF
160 /* $Id: pkru_perm.c,v 1.6 2019/01/12 04:43:20 kostik Exp kostik $ */
161 /* cc -Wall -Wextra -g -O -o pkru_fork64 pkru_fork.c */
162
163 #include <sys/types.h>
164 #include <sys/mman.h>
165 #include <machine/cpufunc.h>
166 #include <machine/sysarch.h>
167 #include <x86/fpu.h>
168 #include <err.h>
169 #include <signal.h>
170 #include <stdio.h>
171 #include <stdlib.h>
172 #include <string.h>
173 #include <unistd.h>
174
175 #ifdef TEST_COMPILE
176 int x86_pkru_get_perm(unsigned int keyidx, int *access, int *modify);
177 int x86_pkru_set_perm(unsigned int keyidx, int access, int modify);
178 int x86_pkru_protect_range(void *addr, unsigned long len, unsigned int keyidx,
179     int flag);
180 int x86_pkru_unprotect_range(void *addr, unsigned long len);
181 uint32_t rdpkru(void);
182 void wrpkru(uint32_t);
183 #define AMD64_PKRU_PERSIST      0x0001
184 #endif
185
186 static void
187 sighandler(int signo __unused, siginfo_t *si __unused, void *uc1 __unused)
188 {
189
190         exit(0);
191 }
192
193 static volatile char *mapping;
194
195 #define OPKEY   1
196
197 int
198 main(void)
199 {
200         struct sigaction sa;
201         char *mapping1;
202         int error;
203
204         error = x86_pkru_set_perm(OPKEY, 0, 0);
205         if (error != 0)
206                 err(1, "x86_pkru_set_perm");
207         mapping = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE,
208             MAP_PRIVATE | MAP_ANON, -1, 0);
209         if (mapping == MAP_FAILED)
210                 err(1, "mmap");
211         error = x86_pkru_protect_range((void *)mapping, getpagesize(),
212             OPKEY, 0);
213         if (error != 0)
214                 err(1, "x86_pkru_protect_range");
215         error = munmap((void *)mapping, getpagesize());
216         if (error != 0)
217                 err(1, "munmap");
218         mapping1 = mmap((void *)mapping, getpagesize(), PROT_READ | PROT_WRITE,
219             MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_EXCL, -1, 0);
220         if (mapping1 == MAP_FAILED)
221                 err(1, "mmap 2");
222         *mapping = 0;
223         error = x86_pkru_protect_range((void *)mapping, getpagesize(),
224             OPKEY, AMD64_PKRU_PERSIST);
225         mapping1 = mmap((void *)mapping, getpagesize(), PROT_READ | PROT_WRITE,
226             MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
227         if (mapping1 == MAP_FAILED)
228                 err(1, "mmap 3");
229         memset(&sa, 0, sizeof(sa));
230         sa.sa_sigaction = sighandler;
231         sa.sa_flags = SA_SIGINFO;
232         if (sigaction(SIGSEGV, &sa, NULL) == -1)
233                 err(1, "sigaction");
234         *mapping = 0;
235         printf("Still alive, pkru persist did not worked");
236         exit(1);
237 }
238 EOF
239 cc -Wall -Wextra -g -O -o pkru_perm64 pkru_perm.c || exit 1
240 cc -Wall -Wextra -g -O -o pkru_perm32 -m32 pkru_perm.c || exit 1
241 rm pkru_perm.c
242 ./pkru_perm64
243 ./pkru_perm32
244 rm -f pkru_perm64 pkru_perm32
245
246 cat > /tmp/pkru.c <<EOF
247 /* $Id: pkru.c,v 1.27 2019/01/10 12:06:31 kostik Exp $ */
248 /* cc -Wall -Wextra -g -O -o pkru64 pkru.c -lpthread */
249
250 #include <sys/types.h>
251 #include <sys/mman.h>
252 #include <machine/cpufunc.h>
253 #include <machine/sysarch.h>
254 #include <x86/fpu.h>
255 #include <err.h>
256 #include <pthread.h>
257 #include <pthread_np.h>
258 #include <signal.h>
259 #include <stdio.h>
260 #include <stdlib.h>
261 #include <string.h>
262 #include <unistd.h>
263
264 #ifdef TEST_COMPILE
265 int x86_pkru_get_perm(unsigned int keyidx, int *access, int *modify);
266 int x86_pkru_set_perm(unsigned int keyidx, int access, int modify);
267 int x86_pkru_protect_range(void *addr, unsigned long len, unsigned int keyidx,
268     int flag);
269 int x86_pkru_unprotect_range(void *addr, unsigned long len);
270 uint32_t rdpkru(void);
271 void wrpkru(uint32_t);
272 #endif
273
274 static char *mut_region;
275 static size_t mut_region_len;
276 static unsigned *mut_region_keys;
277 static pthread_t bga_thr;
278 static int signal_seen;
279 static siginfo_t si_seen;
280 static ucontext_t *uc_seen;
281 static u_int rpku_offset;
282
283 static void
284 handler(int i __unused) {
285         _exit(0);
286 }
287
288 static void
289 report_sig(int signo, siginfo_t *si, ucontext_t *uc)
290 {
291
292         printf("signal %d %s", signo, strsignal(signo));
293         printf(" si_code %d si_status %d si_addr %p", si->si_code,
294             si->si_status, si->si_addr);
295         printf(" mc_err %#jx", (uintmax_t)uc->uc_mcontext.mc_err);
296         if (uc->uc_mcontext.mc_xfpustate != 0 &&
297             (unsigned long)uc->uc_mcontext.mc_xfpustate_len >=
298             rpku_offset) {
299                 printf(" pkru 0x%08x", *(uint32_t *)(
300                     uc->uc_mcontext.mc_xfpustate + rpku_offset));
301         }
302         printf("\n");
303 }
304
305 static void
306 sighandler(int signo, siginfo_t *si, void *u)
307 {
308         ucontext_t *uc;
309         pthread_t thr;
310         size_t len;
311         uint32_t *pkrup;
312
313         uc = u;
314         thr = pthread_self();
315         if (thr == bga_thr) {
316                 printf("Fault from background access thread\n");
317                 report_sig(signo, si, uc);
318                 exit(1);
319         }
320         signal_seen = signo;
321         si_seen = *si;
322
323         len = sizeof(ucontext_t);
324         if (uc->uc_mcontext.mc_xfpustate != 0)
325                 len += uc->uc_mcontext.mc_xfpustate_len;
326         uc_seen = malloc(len);
327         if (uc_seen == NULL)
328                 err(1, "malloc(%d)", (int)len);
329         memcpy(uc_seen, uc, sizeof(*uc));
330 #if 0
331 printf("signal %d xpfustate %p len %ld rpkuo %u\n", signo, (void *)uc->uc_mcontext.mc_xfpustate, uc->uc_mcontext.mc_xfpustate_len, rpku_offset);
332 #endif
333         if (uc->uc_mcontext.mc_xfpustate != 0) {
334                 uc_seen->uc_mcontext.mc_xfpustate = (uintptr_t)uc_seen +
335                     sizeof(*uc);
336                 memcpy((void *)uc_seen->uc_mcontext.mc_xfpustate,
337                     (void *)uc->uc_mcontext.mc_xfpustate,
338                     uc->uc_mcontext.mc_xfpustate_len);
339
340                 if ((unsigned long)uc->uc_mcontext.mc_xfpustate_len >=
341                     rpku_offset + sizeof(uint32_t)) {
342                         pkrup = (uint32_t *)(rpku_offset +
343                             (char *)uc->uc_mcontext.mc_xfpustate);
344 #if 0
345 printf("signal %d *pkru %08x\n", signo, *pkrup);
346 #endif
347                         *pkrup = 0;
348                 }
349         }
350 }
351
352 static void *
353 bg_access_thread_fn(void *arg __unused)
354 {
355         char *c, x;
356
357         pthread_set_name_np(pthread_self(), "bgaccess");
358         for (x = 0, c = mut_region;;) {
359                 *c = x;
360                 if (++c >= mut_region + mut_region_len) {
361                         c = mut_region;
362                         x++;
363                 }
364         }
365         return (NULL);
366 }
367
368 static void
369 clear_signal_report(void)
370 {
371
372         signal_seen = 0;
373         free(uc_seen);
374         uc_seen = NULL;
375 }
376
377 static void
378 check_signal(unsigned key, int check_access, int check_modify)
379 {
380
381         if (signal_seen == 0) {
382                 printf("Did not get signal, key %d check_access %d "
383                     "check_modify %d\n", key, check_access, check_modify);
384                 printf("pkru 0x%08x\n", rdpkru());
385                 exit(1);
386         }
387 }
388
389 static void
390 check_no_signal(void)
391 {
392
393         if (signal_seen != 0) {
394                 printf("pkru 0x%08x\n", rdpkru());
395                 printf("Got signal\n");
396                 report_sig(signal_seen, &si_seen, uc_seen);
397                 exit(1);
398         }
399 }
400
401 static void
402 check(char *p, unsigned key, int check_access, int check_modify)
403 {
404         int access, error, modify, orig_access, orig_modify;
405
406         error = x86_pkru_get_perm(key, &orig_access, &orig_modify);
407         if (error != 0)
408                 err(1, "x86_pkru_get_perm");
409         access = check_access ? 0 : 1;
410         modify = check_modify ? 0 : 1;
411         error = x86_pkru_set_perm(key, access, modify);
412         if (error != 0)
413                 err(1, "x86_pkru_set_perm access");
414         clear_signal_report();
415         if (check_modify)
416                 *(volatile char *)p = 1;
417         else if (check_access)
418                 *(volatile char *)p;
419         if (key == mut_region_keys[(p - mut_region) / getpagesize()])
420                 check_signal(key, check_access, check_modify);
421         else
422                 check_no_signal();
423         error = x86_pkru_set_perm(key, orig_access, orig_modify);
424         if (error != 0)
425                 err(1, "x86_pkru_set_perm access restore");
426         clear_signal_report();
427         if (check_modify)
428                 *(volatile char *)p = 1;
429         else if (check_access)
430                 *(volatile char *)p;
431         check_no_signal();
432 }
433
434 static void
435 mutate_perms(void)
436 {
437         unsigned key;
438         char *p;
439
440         for (p = mut_region;;) {
441                 for (key = 1; key < 0x10; key++) {
442                         check(p, key, 1, 0);
443                         check(p, key, 0, 1);
444                         check(p, key, 1, 1);
445                 }
446                 p += getpagesize();
447                 if (p >= mut_region + mut_region_len)
448                         p = mut_region;
449         }
450 }
451
452 int
453 main(void)
454 {
455         struct sigaction sa;
456         char *p;
457         unsigned i;
458         u_int regs[4];
459         int error;
460
461         cpuid_count(0xd, 0x9, regs);
462         rpku_offset = regs[1];
463         if (rpku_offset != 0)
464 #if defined(__i386__)
465                 rpku_offset -= sizeof(union savefpu);
466 #else
467                 rpku_offset -= sizeof(struct savefpu);
468 #endif
469
470         memset(&sa, 0, sizeof(sa));
471         sa.sa_sigaction = sighandler;
472         sa.sa_flags = SA_SIGINFO;
473         if (sigaction(SIGSEGV, &sa, NULL) == -1)
474                 err(1, "sigaction SIGSEGV");
475         if (sigaction(SIGBUS, &sa, NULL) == -1)
476                 err(1, "sigaction SIGBUS");
477
478         mut_region_len = getpagesize() * 100;
479         mut_region_keys = calloc(mut_region_len, sizeof(unsigned));
480         if (mut_region_keys == NULL)
481                 err(1, "calloc keys");
482         mut_region = mmap(NULL, mut_region_len, PROT_READ | PROT_WRITE,
483             MAP_ANON | MAP_SHARED, -1, 0);
484         if (mut_region == MAP_FAILED)
485                 err(1, "mmap");
486         for (i = 1, p = mut_region; p < mut_region + mut_region_len;
487              p += getpagesize()) {
488                 error = x86_pkru_protect_range(p, getpagesize(), i, 0);
489                 if (error != 0)
490                         err(1, "x86_pkru_protect_range key %d", i);
491                 mut_region_keys[(p - mut_region) / getpagesize()] = i;
492                 if (++i > 0xf)
493                         i = 1;
494         }
495
496         signal(SIGALRM, handler);
497         alarm(5);
498         error = pthread_create(&bga_thr, NULL, bg_access_thread_fn, NULL);
499         if (error != 0)
500                 errc(1, error, "pthread create background access thread");
501
502         mutate_perms();
503 }
504 EOF
505 cc -Wall -Wextra -g -O -o pkru64 pkru.c -lpthread || exit 1
506 cc -Wall -Wextra -g -O -o pkru32 -m32 pkru.c -lpthread || exit 1
507 rm pkru.c
508 ./pkru64
509 ./pkru32
510 rm -f pkru64 pkru32
511
512 exit