1#/bin/sh 2# 3# SPDX-License-Identifier: BSD-2-Clause 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 31grep -qw PKU /var/run/dmesg.boot || exit 0 32cd /tmp 33cat > /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 53int x86_pkru_get_perm(unsigned int keyidx, int *access, int *modify); 54int x86_pkru_set_perm(unsigned int keyidx, int access, int modify); 55int x86_pkru_protect_range(void *addr, unsigned long len, unsigned int keyidx, 56 int flag); 57int x86_pkru_unprotect_range(void *addr, unsigned long len); 58uint32_t rdpkru(void); 59void wrpkru(uint32_t); 60#define AMD64_PKRU_PERSIST 0x0001 61#endif 62 63extern char **environ; 64 65#define OPKEY 1 66 67int 68main(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} 85EOF 86cc -Wall -Wextra -g -O -o pkru_exec64 pkru_exec.c || exit 1 87cc -Wall -Wextra -g -O -o pkru_exec32 pkru_exec.c -m32 || exit 1 88rm pkru_exec.c 89echo "Expect: Segmentation fault (core dumped)" 90LD_BIND_NOW=1 ./pkru_exec64 91LD_BIND_NOW=1 ./pkru_exec32 92rm -f pkru_exec64 pkru_exec32 pkru_exec64.core pkru_exec32.core 93 94cat > /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 112int x86_pkru_get_perm(unsigned int keyidx, int *access, int *modify); 113int x86_pkru_set_perm(unsigned int keyidx, int access, int modify); 114int x86_pkru_protect_range(void *addr, unsigned long len, unsigned int keyidx, 115 int flag); 116int x86_pkru_unprotect_range(void *addr, unsigned long len); 117uint32_t rdpkru(void); 118void wrpkru(uint32_t); 119#endif 120 121static volatile char *mapping; 122 123#define OPKEY 1 124 125int 126main(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} 151EOF 152cc -Wall -Wextra -g -O -o pkru_fork64 pkru_fork.c || exit 1 153cc -Wall -Wextra -g -O -o pkru_fork32 -m32 pkru_fork.c || exit 1 154rm pkru_fork.c 155./pkru_fork64 156./pkru_fork32 157rm -f pkru_fork64 pkru_fork64.core pkru_fork32 pkru_fork32.core 158 159cat > /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 176int x86_pkru_get_perm(unsigned int keyidx, int *access, int *modify); 177int x86_pkru_set_perm(unsigned int keyidx, int access, int modify); 178int x86_pkru_protect_range(void *addr, unsigned long len, unsigned int keyidx, 179 int flag); 180int x86_pkru_unprotect_range(void *addr, unsigned long len); 181uint32_t rdpkru(void); 182void wrpkru(uint32_t); 183#define AMD64_PKRU_PERSIST 0x0001 184#endif 185 186static void 187sighandler(int signo __unused, siginfo_t *si __unused, void *uc1 __unused) 188{ 189 190 exit(0); 191} 192 193static volatile char *mapping; 194 195#define OPKEY 1 196 197int 198main(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} 238EOF 239cc -Wall -Wextra -g -O -o pkru_perm64 pkru_perm.c || exit 1 240cc -Wall -Wextra -g -O -o pkru_perm32 -m32 pkru_perm.c || exit 1 241rm pkru_perm.c 242./pkru_perm64 243./pkru_perm32 244rm -f pkru_perm64 pkru_perm32 245 246cat > /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 265int x86_pkru_get_perm(unsigned int keyidx, int *access, int *modify); 266int x86_pkru_set_perm(unsigned int keyidx, int access, int modify); 267int x86_pkru_protect_range(void *addr, unsigned long len, unsigned int keyidx, 268 int flag); 269int x86_pkru_unprotect_range(void *addr, unsigned long len); 270uint32_t rdpkru(void); 271void wrpkru(uint32_t); 272#endif 273 274static char *mut_region; 275static size_t mut_region_len; 276static unsigned *mut_region_keys; 277static pthread_t bga_thr; 278static int signal_seen; 279static siginfo_t si_seen; 280static ucontext_t *uc_seen; 281static u_int rpku_offset; 282 283static void 284handler(int i __unused) { 285 _exit(0); 286} 287 288static void 289report_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 305static void 306sighandler(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 331printf("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 345printf("signal %d *pkru %08x\n", signo, *pkrup); 346#endif 347 *pkrup = 0; 348 } 349 } 350} 351 352static void * 353bg_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 368static void 369clear_signal_report(void) 370{ 371 372 signal_seen = 0; 373 free(uc_seen); 374 uc_seen = NULL; 375} 376 377static void 378check_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 389static void 390check_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 401static void 402check(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 434static void 435mutate_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 452int 453main(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} 504EOF 505cc -Wall -Wextra -g -O -o pkru64 pkru.c -lpthread || exit 1 506cc -Wall -Wextra -g -O -o pkru32 -m32 pkru.c -lpthread || exit 1 507rm pkru.c 508./pkru64 509./pkru32 510rm -f pkru64 pkru32 511 512exit 513