xref: /freebsd/tools/test/stress2/misc/pkru.sh (revision 19fae0f66023a97a9b464b3beeeabb2081f575b3)
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