3 # SPDX-License-Identifier: BSD-2-Clause-FreeBSD
5 # Copyright (c) 2019 Konstantin Belousov <kib@FreeBSD.org>
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions
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.
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
29 # Test scenario for Intel userspace protection keys feature on Skylake Xeons
31 grep -qw PKU /var/run/dmesg.boot || exit 0
33 cat > /tmp/pkru_exec.c <<EOF
34 /* $Id: pkru_exec.c,v 1.4 2019/01/12 04:55:57 kostik Exp kostik $ */
36 * cc -Wall -Wextra -g -O -o pkru_fork64 pkru_fork.c
37 * Run it with LD_BIND_NOW=1.
40 #include <sys/types.h>
43 #include <sys/resource.h>
44 #include <machine/sysarch.h>
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,
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
63 extern char **environ;
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);
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
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
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 */
98 #include <sys/types.h>
100 #include <sys/wait.h>
101 #include <machine/cpufunc.h>
102 #include <machine/sysarch.h>
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,
116 int x86_pkru_unprotect_range(void *addr, unsigned long len);
117 uint32_t rdpkru(void);
118 void wrpkru(uint32_t);
121 static volatile char *mapping;
131 mapping = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE,
132 MAP_PRIVATE | MAP_ANON, -1, 0);
133 if (mapping == MAP_FAILED)
135 error = x86_pkru_protect_range((void *)mapping, getpagesize(),
138 err(1, "x86_pkru_protect_range");
139 error = x86_pkru_set_perm(OPKEY, 0, 0);
141 err(1, "x86_pkru_set_perm");
147 printf("Still alive, pkru did not worked after fork");
149 waitpid(child, NULL, 0);
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
157 rm -f pkru_fork64 pkru_fork64.core pkru_fork32 pkru_fork32.core
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 */
163 #include <sys/types.h>
164 #include <sys/mman.h>
165 #include <machine/cpufunc.h>
166 #include <machine/sysarch.h>
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,
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
187 sighandler(int signo __unused, siginfo_t *si __unused, void *uc1 __unused)
193 static volatile char *mapping;
204 error = x86_pkru_set_perm(OPKEY, 0, 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)
211 error = x86_pkru_protect_range((void *)mapping, getpagesize(),
214 err(1, "x86_pkru_protect_range");
215 error = munmap((void *)mapping, getpagesize());
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)
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)
229 memset(&sa, 0, sizeof(sa));
230 sa.sa_sigaction = sighandler;
231 sa.sa_flags = SA_SIGINFO;
232 if (sigaction(SIGSEGV, &sa, NULL) == -1)
235 printf("Still alive, pkru persist did not worked");
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
244 rm -f pkru_perm64 pkru_perm32
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 */
250 #include <sys/types.h>
251 #include <sys/mman.h>
252 #include <machine/cpufunc.h>
253 #include <machine/sysarch.h>
257 #include <pthread_np.h>
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,
269 int x86_pkru_unprotect_range(void *addr, unsigned long len);
270 uint32_t rdpkru(void);
271 void wrpkru(uint32_t);
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;
284 handler(int i __unused) {
289 report_sig(int signo, siginfo_t *si, ucontext_t *uc)
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 >=
299 printf(" pkru 0x%08x", *(uint32_t *)(
300 uc->uc_mcontext.mc_xfpustate + rpku_offset));
306 sighandler(int signo, siginfo_t *si, void *u)
314 thr = pthread_self();
315 if (thr == bga_thr) {
316 printf("Fault from background access thread\n");
317 report_sig(signo, si, uc);
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);
328 err(1, "malloc(%d)", (int)len);
329 memcpy(uc_seen, uc, sizeof(*uc));
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);
333 if (uc->uc_mcontext.mc_xfpustate != 0) {
334 uc_seen->uc_mcontext.mc_xfpustate = (uintptr_t)uc_seen +
336 memcpy((void *)uc_seen->uc_mcontext.mc_xfpustate,
337 (void *)uc->uc_mcontext.mc_xfpustate,
338 uc->uc_mcontext.mc_xfpustate_len);
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);
345 printf("signal %d *pkru %08x\n", signo, *pkrup);
353 bg_access_thread_fn(void *arg __unused)
357 pthread_set_name_np(pthread_self(), "bgaccess");
358 for (x = 0, c = mut_region;;) {
360 if (++c >= mut_region + mut_region_len) {
369 clear_signal_report(void)
378 check_signal(unsigned key, int check_access, int check_modify)
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());
390 check_no_signal(void)
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);
402 check(char *p, unsigned key, int check_access, int check_modify)
404 int access, error, modify, orig_access, orig_modify;
406 error = x86_pkru_get_perm(key, &orig_access, &orig_modify);
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);
413 err(1, "x86_pkru_set_perm access");
414 clear_signal_report();
416 *(volatile char *)p = 1;
417 else if (check_access)
419 if (key == mut_region_keys[(p - mut_region) / getpagesize()])
420 check_signal(key, check_access, check_modify);
423 error = x86_pkru_set_perm(key, orig_access, orig_modify);
425 err(1, "x86_pkru_set_perm access restore");
426 clear_signal_report();
428 *(volatile char *)p = 1;
429 else if (check_access)
440 for (p = mut_region;;) {
441 for (key = 1; key < 0x10; key++) {
447 if (p >= mut_region + mut_region_len)
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);
467 rpku_offset -= sizeof(struct savefpu);
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");
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)
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);
490 err(1, "x86_pkru_protect_range key %d", i);
491 mut_region_keys[(p - mut_region) / getpagesize()] = i;
496 signal(SIGALRM, handler);
498 error = pthread_create(&bga_thr, NULL, bg_access_thread_fn, NULL);
500 errc(1, error, "pthread create background access thread");
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