162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Tests Memory Protection Keys (see Documentation/core-api/protection-keys.rst)
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * There are examples in here of:
662306a36Sopenharmony_ci *  * how to set protection keys on memory
762306a36Sopenharmony_ci *  * how to set/clear bits in pkey registers (the rights register)
862306a36Sopenharmony_ci *  * how to handle SEGV_PKUERR signals and extract pkey-relevant
962306a36Sopenharmony_ci *    information from the siginfo
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * Things to add:
1262306a36Sopenharmony_ci *	make sure KSM and KSM COW breaking works
1362306a36Sopenharmony_ci *	prefault pages in at malloc, or not
1462306a36Sopenharmony_ci *	protect MPX bounds tables with protection keys?
1562306a36Sopenharmony_ci *	make sure VMA splitting/merging is working correctly
1662306a36Sopenharmony_ci *	OOMs can destroy mm->mmap (see exit_mmap()), so make sure it is immune to pkeys
1762306a36Sopenharmony_ci *	look for pkey "leaks" where it is still set on a VMA but "freed" back to the kernel
1862306a36Sopenharmony_ci *	do a plain mprotect() to a mprotect_pkey() area and make sure the pkey sticks
1962306a36Sopenharmony_ci *
2062306a36Sopenharmony_ci * Compile like this:
2162306a36Sopenharmony_ci *	gcc -mxsave      -o protection_keys    -O2 -g -std=gnu99 -pthread -Wall protection_keys.c -lrt -ldl -lm
2262306a36Sopenharmony_ci *	gcc -mxsave -m32 -o protection_keys_32 -O2 -g -std=gnu99 -pthread -Wall protection_keys.c -lrt -ldl -lm
2362306a36Sopenharmony_ci */
2462306a36Sopenharmony_ci#define _GNU_SOURCE
2562306a36Sopenharmony_ci#define __SANE_USERSPACE_TYPES__
2662306a36Sopenharmony_ci#include <errno.h>
2762306a36Sopenharmony_ci#include <linux/elf.h>
2862306a36Sopenharmony_ci#include <linux/futex.h>
2962306a36Sopenharmony_ci#include <time.h>
3062306a36Sopenharmony_ci#include <sys/time.h>
3162306a36Sopenharmony_ci#include <sys/syscall.h>
3262306a36Sopenharmony_ci#include <string.h>
3362306a36Sopenharmony_ci#include <stdio.h>
3462306a36Sopenharmony_ci#include <stdint.h>
3562306a36Sopenharmony_ci#include <stdbool.h>
3662306a36Sopenharmony_ci#include <signal.h>
3762306a36Sopenharmony_ci#include <assert.h>
3862306a36Sopenharmony_ci#include <stdlib.h>
3962306a36Sopenharmony_ci#include <ucontext.h>
4062306a36Sopenharmony_ci#include <sys/mman.h>
4162306a36Sopenharmony_ci#include <sys/types.h>
4262306a36Sopenharmony_ci#include <sys/wait.h>
4362306a36Sopenharmony_ci#include <sys/stat.h>
4462306a36Sopenharmony_ci#include <fcntl.h>
4562306a36Sopenharmony_ci#include <unistd.h>
4662306a36Sopenharmony_ci#include <sys/ptrace.h>
4762306a36Sopenharmony_ci#include <setjmp.h>
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#include "pkey-helpers.h"
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ciint iteration_nr = 1;
5262306a36Sopenharmony_ciint test_nr;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ciu64 shadow_pkey_reg;
5562306a36Sopenharmony_ciint dprint_in_signal;
5662306a36Sopenharmony_cichar dprint_in_signal_buffer[DPRINT_IN_SIGNAL_BUF_SIZE];
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_civoid cat_into_file(char *str, char *file)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	int fd = open(file, O_RDWR);
6162306a36Sopenharmony_ci	int ret;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	dprintf2("%s(): writing '%s' to '%s'\n", __func__, str, file);
6462306a36Sopenharmony_ci	/*
6562306a36Sopenharmony_ci	 * these need to be raw because they are called under
6662306a36Sopenharmony_ci	 * pkey_assert()
6762306a36Sopenharmony_ci	 */
6862306a36Sopenharmony_ci	if (fd < 0) {
6962306a36Sopenharmony_ci		fprintf(stderr, "error opening '%s'\n", str);
7062306a36Sopenharmony_ci		perror("error: ");
7162306a36Sopenharmony_ci		exit(__LINE__);
7262306a36Sopenharmony_ci	}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	ret = write(fd, str, strlen(str));
7562306a36Sopenharmony_ci	if (ret != strlen(str)) {
7662306a36Sopenharmony_ci		perror("write to file failed");
7762306a36Sopenharmony_ci		fprintf(stderr, "filename: '%s' str: '%s'\n", file, str);
7862306a36Sopenharmony_ci		exit(__LINE__);
7962306a36Sopenharmony_ci	}
8062306a36Sopenharmony_ci	close(fd);
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci#if CONTROL_TRACING > 0
8462306a36Sopenharmony_cistatic int warned_tracing;
8562306a36Sopenharmony_ciint tracing_root_ok(void)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	if (geteuid() != 0) {
8862306a36Sopenharmony_ci		if (!warned_tracing)
8962306a36Sopenharmony_ci			fprintf(stderr, "WARNING: not run as root, "
9062306a36Sopenharmony_ci					"can not do tracing control\n");
9162306a36Sopenharmony_ci		warned_tracing = 1;
9262306a36Sopenharmony_ci		return 0;
9362306a36Sopenharmony_ci	}
9462306a36Sopenharmony_ci	return 1;
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci#endif
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_civoid tracing_on(void)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci#if CONTROL_TRACING > 0
10162306a36Sopenharmony_ci#define TRACEDIR "/sys/kernel/tracing"
10262306a36Sopenharmony_ci	char pidstr[32];
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	if (!tracing_root_ok())
10562306a36Sopenharmony_ci		return;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	sprintf(pidstr, "%d", getpid());
10862306a36Sopenharmony_ci	cat_into_file("0", TRACEDIR "/tracing_on");
10962306a36Sopenharmony_ci	cat_into_file("\n", TRACEDIR "/trace");
11062306a36Sopenharmony_ci	if (1) {
11162306a36Sopenharmony_ci		cat_into_file("function_graph", TRACEDIR "/current_tracer");
11262306a36Sopenharmony_ci		cat_into_file("1", TRACEDIR "/options/funcgraph-proc");
11362306a36Sopenharmony_ci	} else {
11462306a36Sopenharmony_ci		cat_into_file("nop", TRACEDIR "/current_tracer");
11562306a36Sopenharmony_ci	}
11662306a36Sopenharmony_ci	cat_into_file(pidstr, TRACEDIR "/set_ftrace_pid");
11762306a36Sopenharmony_ci	cat_into_file("1", TRACEDIR "/tracing_on");
11862306a36Sopenharmony_ci	dprintf1("enabled tracing\n");
11962306a36Sopenharmony_ci#endif
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_civoid tracing_off(void)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci#if CONTROL_TRACING > 0
12562306a36Sopenharmony_ci	if (!tracing_root_ok())
12662306a36Sopenharmony_ci		return;
12762306a36Sopenharmony_ci	cat_into_file("0", "/sys/kernel/tracing/tracing_on");
12862306a36Sopenharmony_ci#endif
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_civoid abort_hooks(void)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	fprintf(stderr, "running %s()...\n", __func__);
13462306a36Sopenharmony_ci	tracing_off();
13562306a36Sopenharmony_ci#ifdef SLEEP_ON_ABORT
13662306a36Sopenharmony_ci	sleep(SLEEP_ON_ABORT);
13762306a36Sopenharmony_ci#endif
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci/*
14162306a36Sopenharmony_ci * This attempts to have roughly a page of instructions followed by a few
14262306a36Sopenharmony_ci * instructions that do a write, and another page of instructions.  That
14362306a36Sopenharmony_ci * way, we are pretty sure that the write is in the second page of
14462306a36Sopenharmony_ci * instructions and has at least a page of padding behind it.
14562306a36Sopenharmony_ci *
14662306a36Sopenharmony_ci * *That* lets us be sure to madvise() away the write instruction, which
14762306a36Sopenharmony_ci * will then fault, which makes sure that the fault code handles
14862306a36Sopenharmony_ci * execute-only memory properly.
14962306a36Sopenharmony_ci */
15062306a36Sopenharmony_ci#ifdef __powerpc64__
15162306a36Sopenharmony_ci/* This way, both 4K and 64K alignment are maintained */
15262306a36Sopenharmony_ci__attribute__((__aligned__(65536)))
15362306a36Sopenharmony_ci#else
15462306a36Sopenharmony_ci__attribute__((__aligned__(PAGE_SIZE)))
15562306a36Sopenharmony_ci#endif
15662306a36Sopenharmony_civoid lots_o_noops_around_write(int *write_to_me)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	dprintf3("running %s()\n", __func__);
15962306a36Sopenharmony_ci	__page_o_noops();
16062306a36Sopenharmony_ci	/* Assume this happens in the second page of instructions: */
16162306a36Sopenharmony_ci	*write_to_me = __LINE__;
16262306a36Sopenharmony_ci	/* pad out by another page: */
16362306a36Sopenharmony_ci	__page_o_noops();
16462306a36Sopenharmony_ci	dprintf3("%s() done\n", __func__);
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_civoid dump_mem(void *dumpme, int len_bytes)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	char *c = (void *)dumpme;
17062306a36Sopenharmony_ci	int i;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	for (i = 0; i < len_bytes; i += sizeof(u64)) {
17362306a36Sopenharmony_ci		u64 *ptr = (u64 *)(c + i);
17462306a36Sopenharmony_ci		dprintf1("dump[%03d][@%p]: %016llx\n", i, ptr, *ptr);
17562306a36Sopenharmony_ci	}
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cistatic u32 hw_pkey_get(int pkey, unsigned long flags)
17962306a36Sopenharmony_ci{
18062306a36Sopenharmony_ci	u64 pkey_reg = __read_pkey_reg();
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	dprintf1("%s(pkey=%d, flags=%lx) = %x / %d\n",
18362306a36Sopenharmony_ci			__func__, pkey, flags, 0, 0);
18462306a36Sopenharmony_ci	dprintf2("%s() raw pkey_reg: %016llx\n", __func__, pkey_reg);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	return (u32) get_pkey_bits(pkey_reg, pkey);
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cistatic int hw_pkey_set(int pkey, unsigned long rights, unsigned long flags)
19062306a36Sopenharmony_ci{
19162306a36Sopenharmony_ci	u32 mask = (PKEY_DISABLE_ACCESS|PKEY_DISABLE_WRITE);
19262306a36Sopenharmony_ci	u64 old_pkey_reg = __read_pkey_reg();
19362306a36Sopenharmony_ci	u64 new_pkey_reg;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	/* make sure that 'rights' only contains the bits we expect: */
19662306a36Sopenharmony_ci	assert(!(rights & ~mask));
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	/* modify bits accordingly in old pkey_reg and assign it */
19962306a36Sopenharmony_ci	new_pkey_reg = set_pkey_bits(old_pkey_reg, pkey, rights);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	__write_pkey_reg(new_pkey_reg);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	dprintf3("%s(pkey=%d, rights=%lx, flags=%lx) = %x"
20462306a36Sopenharmony_ci		" pkey_reg now: %016llx old_pkey_reg: %016llx\n",
20562306a36Sopenharmony_ci		__func__, pkey, rights, flags, 0, __read_pkey_reg(),
20662306a36Sopenharmony_ci		old_pkey_reg);
20762306a36Sopenharmony_ci	return 0;
20862306a36Sopenharmony_ci}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_civoid pkey_disable_set(int pkey, int flags)
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	unsigned long syscall_flags = 0;
21362306a36Sopenharmony_ci	int ret;
21462306a36Sopenharmony_ci	int pkey_rights;
21562306a36Sopenharmony_ci	u64 orig_pkey_reg = read_pkey_reg();
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	dprintf1("START->%s(%d, 0x%x)\n", __func__,
21862306a36Sopenharmony_ci		pkey, flags);
21962306a36Sopenharmony_ci	pkey_assert(flags & (PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE));
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	pkey_rights = hw_pkey_get(pkey, syscall_flags);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	dprintf1("%s(%d) hw_pkey_get(%d): %x\n", __func__,
22462306a36Sopenharmony_ci			pkey, pkey, pkey_rights);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	pkey_assert(pkey_rights >= 0);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	pkey_rights |= flags;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	ret = hw_pkey_set(pkey, pkey_rights, syscall_flags);
23162306a36Sopenharmony_ci	assert(!ret);
23262306a36Sopenharmony_ci	/* pkey_reg and flags have the same format */
23362306a36Sopenharmony_ci	shadow_pkey_reg = set_pkey_bits(shadow_pkey_reg, pkey, pkey_rights);
23462306a36Sopenharmony_ci	dprintf1("%s(%d) shadow: 0x%016llx\n",
23562306a36Sopenharmony_ci		__func__, pkey, shadow_pkey_reg);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	pkey_assert(ret >= 0);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	pkey_rights = hw_pkey_get(pkey, syscall_flags);
24062306a36Sopenharmony_ci	dprintf1("%s(%d) hw_pkey_get(%d): %x\n", __func__,
24162306a36Sopenharmony_ci			pkey, pkey, pkey_rights);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	dprintf1("%s(%d) pkey_reg: 0x%016llx\n",
24462306a36Sopenharmony_ci		__func__, pkey, read_pkey_reg());
24562306a36Sopenharmony_ci	if (flags)
24662306a36Sopenharmony_ci		pkey_assert(read_pkey_reg() >= orig_pkey_reg);
24762306a36Sopenharmony_ci	dprintf1("END<---%s(%d, 0x%x)\n", __func__,
24862306a36Sopenharmony_ci		pkey, flags);
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_civoid pkey_disable_clear(int pkey, int flags)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	unsigned long syscall_flags = 0;
25462306a36Sopenharmony_ci	int ret;
25562306a36Sopenharmony_ci	int pkey_rights = hw_pkey_get(pkey, syscall_flags);
25662306a36Sopenharmony_ci	u64 orig_pkey_reg = read_pkey_reg();
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	pkey_assert(flags & (PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE));
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	dprintf1("%s(%d) hw_pkey_get(%d): %x\n", __func__,
26162306a36Sopenharmony_ci			pkey, pkey, pkey_rights);
26262306a36Sopenharmony_ci	pkey_assert(pkey_rights >= 0);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	pkey_rights &= ~flags;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	ret = hw_pkey_set(pkey, pkey_rights, 0);
26762306a36Sopenharmony_ci	shadow_pkey_reg = set_pkey_bits(shadow_pkey_reg, pkey, pkey_rights);
26862306a36Sopenharmony_ci	pkey_assert(ret >= 0);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	pkey_rights = hw_pkey_get(pkey, syscall_flags);
27162306a36Sopenharmony_ci	dprintf1("%s(%d) hw_pkey_get(%d): %x\n", __func__,
27262306a36Sopenharmony_ci			pkey, pkey, pkey_rights);
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	dprintf1("%s(%d) pkey_reg: 0x%016llx\n", __func__,
27562306a36Sopenharmony_ci			pkey, read_pkey_reg());
27662306a36Sopenharmony_ci	if (flags)
27762306a36Sopenharmony_ci		assert(read_pkey_reg() <= orig_pkey_reg);
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_civoid pkey_write_allow(int pkey)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	pkey_disable_clear(pkey, PKEY_DISABLE_WRITE);
28362306a36Sopenharmony_ci}
28462306a36Sopenharmony_civoid pkey_write_deny(int pkey)
28562306a36Sopenharmony_ci{
28662306a36Sopenharmony_ci	pkey_disable_set(pkey, PKEY_DISABLE_WRITE);
28762306a36Sopenharmony_ci}
28862306a36Sopenharmony_civoid pkey_access_allow(int pkey)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	pkey_disable_clear(pkey, PKEY_DISABLE_ACCESS);
29162306a36Sopenharmony_ci}
29262306a36Sopenharmony_civoid pkey_access_deny(int pkey)
29362306a36Sopenharmony_ci{
29462306a36Sopenharmony_ci	pkey_disable_set(pkey, PKEY_DISABLE_ACCESS);
29562306a36Sopenharmony_ci}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_cistatic char *si_code_str(int si_code)
29862306a36Sopenharmony_ci{
29962306a36Sopenharmony_ci	if (si_code == SEGV_MAPERR)
30062306a36Sopenharmony_ci		return "SEGV_MAPERR";
30162306a36Sopenharmony_ci	if (si_code == SEGV_ACCERR)
30262306a36Sopenharmony_ci		return "SEGV_ACCERR";
30362306a36Sopenharmony_ci	if (si_code == SEGV_BNDERR)
30462306a36Sopenharmony_ci		return "SEGV_BNDERR";
30562306a36Sopenharmony_ci	if (si_code == SEGV_PKUERR)
30662306a36Sopenharmony_ci		return "SEGV_PKUERR";
30762306a36Sopenharmony_ci	return "UNKNOWN";
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ciint pkey_faults;
31162306a36Sopenharmony_ciint last_si_pkey = -1;
31262306a36Sopenharmony_civoid signal_handler(int signum, siginfo_t *si, void *vucontext)
31362306a36Sopenharmony_ci{
31462306a36Sopenharmony_ci	ucontext_t *uctxt = vucontext;
31562306a36Sopenharmony_ci	int trapno;
31662306a36Sopenharmony_ci	unsigned long ip;
31762306a36Sopenharmony_ci	char *fpregs;
31862306a36Sopenharmony_ci#if defined(__i386__) || defined(__x86_64__) /* arch */
31962306a36Sopenharmony_ci	u32 *pkey_reg_ptr;
32062306a36Sopenharmony_ci	int pkey_reg_offset;
32162306a36Sopenharmony_ci#endif /* arch */
32262306a36Sopenharmony_ci	u64 siginfo_pkey;
32362306a36Sopenharmony_ci	u32 *si_pkey_ptr;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	dprint_in_signal = 1;
32662306a36Sopenharmony_ci	dprintf1(">>>>===============SIGSEGV============================\n");
32762306a36Sopenharmony_ci	dprintf1("%s()::%d, pkey_reg: 0x%016llx shadow: %016llx\n",
32862306a36Sopenharmony_ci			__func__, __LINE__,
32962306a36Sopenharmony_ci			__read_pkey_reg(), shadow_pkey_reg);
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	trapno = uctxt->uc_mcontext.gregs[REG_TRAPNO];
33262306a36Sopenharmony_ci	ip = uctxt->uc_mcontext.gregs[REG_IP_IDX];
33362306a36Sopenharmony_ci	fpregs = (char *) uctxt->uc_mcontext.fpregs;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	dprintf2("%s() trapno: %d ip: 0x%016lx info->si_code: %s/%d\n",
33662306a36Sopenharmony_ci			__func__, trapno, ip, si_code_str(si->si_code),
33762306a36Sopenharmony_ci			si->si_code);
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci#if defined(__i386__) || defined(__x86_64__) /* arch */
34062306a36Sopenharmony_ci#ifdef __i386__
34162306a36Sopenharmony_ci	/*
34262306a36Sopenharmony_ci	 * 32-bit has some extra padding so that userspace can tell whether
34362306a36Sopenharmony_ci	 * the XSTATE header is present in addition to the "legacy" FPU
34462306a36Sopenharmony_ci	 * state.  We just assume that it is here.
34562306a36Sopenharmony_ci	 */
34662306a36Sopenharmony_ci	fpregs += 0x70;
34762306a36Sopenharmony_ci#endif /* i386 */
34862306a36Sopenharmony_ci	pkey_reg_offset = pkey_reg_xstate_offset();
34962306a36Sopenharmony_ci	pkey_reg_ptr = (void *)(&fpregs[pkey_reg_offset]);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	/*
35262306a36Sopenharmony_ci	 * If we got a PKEY fault, we *HAVE* to have at least one bit set in
35362306a36Sopenharmony_ci	 * here.
35462306a36Sopenharmony_ci	 */
35562306a36Sopenharmony_ci	dprintf1("pkey_reg_xstate_offset: %d\n", pkey_reg_xstate_offset());
35662306a36Sopenharmony_ci	if (DEBUG_LEVEL > 4)
35762306a36Sopenharmony_ci		dump_mem(pkey_reg_ptr - 128, 256);
35862306a36Sopenharmony_ci	pkey_assert(*pkey_reg_ptr);
35962306a36Sopenharmony_ci#endif /* arch */
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	dprintf1("siginfo: %p\n", si);
36262306a36Sopenharmony_ci	dprintf1(" fpregs: %p\n", fpregs);
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	if ((si->si_code == SEGV_MAPERR) ||
36562306a36Sopenharmony_ci	    (si->si_code == SEGV_ACCERR) ||
36662306a36Sopenharmony_ci	    (si->si_code == SEGV_BNDERR)) {
36762306a36Sopenharmony_ci		printf("non-PK si_code, exiting...\n");
36862306a36Sopenharmony_ci		exit(4);
36962306a36Sopenharmony_ci	}
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	si_pkey_ptr = siginfo_get_pkey_ptr(si);
37262306a36Sopenharmony_ci	dprintf1("si_pkey_ptr: %p\n", si_pkey_ptr);
37362306a36Sopenharmony_ci	dump_mem((u8 *)si_pkey_ptr - 8, 24);
37462306a36Sopenharmony_ci	siginfo_pkey = *si_pkey_ptr;
37562306a36Sopenharmony_ci	pkey_assert(siginfo_pkey < NR_PKEYS);
37662306a36Sopenharmony_ci	last_si_pkey = siginfo_pkey;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	/*
37962306a36Sopenharmony_ci	 * need __read_pkey_reg() version so we do not do shadow_pkey_reg
38062306a36Sopenharmony_ci	 * checking
38162306a36Sopenharmony_ci	 */
38262306a36Sopenharmony_ci	dprintf1("signal pkey_reg from  pkey_reg: %016llx\n",
38362306a36Sopenharmony_ci			__read_pkey_reg());
38462306a36Sopenharmony_ci	dprintf1("pkey from siginfo: %016llx\n", siginfo_pkey);
38562306a36Sopenharmony_ci#if defined(__i386__) || defined(__x86_64__) /* arch */
38662306a36Sopenharmony_ci	dprintf1("signal pkey_reg from xsave: %08x\n", *pkey_reg_ptr);
38762306a36Sopenharmony_ci	*(u64 *)pkey_reg_ptr = 0x00000000;
38862306a36Sopenharmony_ci	dprintf1("WARNING: set PKEY_REG=0 to allow faulting instruction to continue\n");
38962306a36Sopenharmony_ci#elif defined(__powerpc64__) /* arch */
39062306a36Sopenharmony_ci	/* restore access and let the faulting instruction continue */
39162306a36Sopenharmony_ci	pkey_access_allow(siginfo_pkey);
39262306a36Sopenharmony_ci#endif /* arch */
39362306a36Sopenharmony_ci	pkey_faults++;
39462306a36Sopenharmony_ci	dprintf1("<<<<==================================================\n");
39562306a36Sopenharmony_ci	dprint_in_signal = 0;
39662306a36Sopenharmony_ci}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ciint wait_all_children(void)
39962306a36Sopenharmony_ci{
40062306a36Sopenharmony_ci	int status;
40162306a36Sopenharmony_ci	return waitpid(-1, &status, 0);
40262306a36Sopenharmony_ci}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_civoid sig_chld(int x)
40562306a36Sopenharmony_ci{
40662306a36Sopenharmony_ci	dprint_in_signal = 1;
40762306a36Sopenharmony_ci	dprintf2("[%d] SIGCHLD: %d\n", getpid(), x);
40862306a36Sopenharmony_ci	dprint_in_signal = 0;
40962306a36Sopenharmony_ci}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_civoid setup_sigsegv_handler(void)
41262306a36Sopenharmony_ci{
41362306a36Sopenharmony_ci	int r, rs;
41462306a36Sopenharmony_ci	struct sigaction newact;
41562306a36Sopenharmony_ci	struct sigaction oldact;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	/* #PF is mapped to sigsegv */
41862306a36Sopenharmony_ci	int signum  = SIGSEGV;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	newact.sa_handler = 0;
42162306a36Sopenharmony_ci	newact.sa_sigaction = signal_handler;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	/*sigset_t - signals to block while in the handler */
42462306a36Sopenharmony_ci	/* get the old signal mask. */
42562306a36Sopenharmony_ci	rs = sigprocmask(SIG_SETMASK, 0, &newact.sa_mask);
42662306a36Sopenharmony_ci	pkey_assert(rs == 0);
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	/* call sa_sigaction, not sa_handler*/
42962306a36Sopenharmony_ci	newact.sa_flags = SA_SIGINFO;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	newact.sa_restorer = 0;  /* void(*)(), obsolete */
43262306a36Sopenharmony_ci	r = sigaction(signum, &newact, &oldact);
43362306a36Sopenharmony_ci	r = sigaction(SIGALRM, &newact, &oldact);
43462306a36Sopenharmony_ci	pkey_assert(r == 0);
43562306a36Sopenharmony_ci}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_civoid setup_handlers(void)
43862306a36Sopenharmony_ci{
43962306a36Sopenharmony_ci	signal(SIGCHLD, &sig_chld);
44062306a36Sopenharmony_ci	setup_sigsegv_handler();
44162306a36Sopenharmony_ci}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_cipid_t fork_lazy_child(void)
44462306a36Sopenharmony_ci{
44562306a36Sopenharmony_ci	pid_t forkret;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	forkret = fork();
44862306a36Sopenharmony_ci	pkey_assert(forkret >= 0);
44962306a36Sopenharmony_ci	dprintf3("[%d] fork() ret: %d\n", getpid(), forkret);
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	if (!forkret) {
45262306a36Sopenharmony_ci		/* in the child */
45362306a36Sopenharmony_ci		while (1) {
45462306a36Sopenharmony_ci			dprintf1("child sleeping...\n");
45562306a36Sopenharmony_ci			sleep(30);
45662306a36Sopenharmony_ci		}
45762306a36Sopenharmony_ci	}
45862306a36Sopenharmony_ci	return forkret;
45962306a36Sopenharmony_ci}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ciint sys_mprotect_pkey(void *ptr, size_t size, unsigned long orig_prot,
46262306a36Sopenharmony_ci		unsigned long pkey)
46362306a36Sopenharmony_ci{
46462306a36Sopenharmony_ci	int sret;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	dprintf2("%s(0x%p, %zx, prot=%lx, pkey=%lx)\n", __func__,
46762306a36Sopenharmony_ci			ptr, size, orig_prot, pkey);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	errno = 0;
47062306a36Sopenharmony_ci	sret = syscall(__NR_pkey_mprotect, ptr, size, orig_prot, pkey);
47162306a36Sopenharmony_ci	if (errno) {
47262306a36Sopenharmony_ci		dprintf2("SYS_mprotect_key sret: %d\n", sret);
47362306a36Sopenharmony_ci		dprintf2("SYS_mprotect_key prot: 0x%lx\n", orig_prot);
47462306a36Sopenharmony_ci		dprintf2("SYS_mprotect_key failed, errno: %d\n", errno);
47562306a36Sopenharmony_ci		if (DEBUG_LEVEL >= 2)
47662306a36Sopenharmony_ci			perror("SYS_mprotect_pkey");
47762306a36Sopenharmony_ci	}
47862306a36Sopenharmony_ci	return sret;
47962306a36Sopenharmony_ci}
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ciint sys_pkey_alloc(unsigned long flags, unsigned long init_val)
48262306a36Sopenharmony_ci{
48362306a36Sopenharmony_ci	int ret = syscall(SYS_pkey_alloc, flags, init_val);
48462306a36Sopenharmony_ci	dprintf1("%s(flags=%lx, init_val=%lx) syscall ret: %d errno: %d\n",
48562306a36Sopenharmony_ci			__func__, flags, init_val, ret, errno);
48662306a36Sopenharmony_ci	return ret;
48762306a36Sopenharmony_ci}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ciint alloc_pkey(void)
49062306a36Sopenharmony_ci{
49162306a36Sopenharmony_ci	int ret;
49262306a36Sopenharmony_ci	unsigned long init_val = 0x0;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	dprintf1("%s()::%d, pkey_reg: 0x%016llx shadow: %016llx\n",
49562306a36Sopenharmony_ci			__func__, __LINE__, __read_pkey_reg(), shadow_pkey_reg);
49662306a36Sopenharmony_ci	ret = sys_pkey_alloc(0, init_val);
49762306a36Sopenharmony_ci	/*
49862306a36Sopenharmony_ci	 * pkey_alloc() sets PKEY register, so we need to reflect it in
49962306a36Sopenharmony_ci	 * shadow_pkey_reg:
50062306a36Sopenharmony_ci	 */
50162306a36Sopenharmony_ci	dprintf4("%s()::%d, ret: %d pkey_reg: 0x%016llx"
50262306a36Sopenharmony_ci			" shadow: 0x%016llx\n",
50362306a36Sopenharmony_ci			__func__, __LINE__, ret, __read_pkey_reg(),
50462306a36Sopenharmony_ci			shadow_pkey_reg);
50562306a36Sopenharmony_ci	if (ret > 0) {
50662306a36Sopenharmony_ci		/* clear both the bits: */
50762306a36Sopenharmony_ci		shadow_pkey_reg = set_pkey_bits(shadow_pkey_reg, ret,
50862306a36Sopenharmony_ci						~PKEY_MASK);
50962306a36Sopenharmony_ci		dprintf4("%s()::%d, ret: %d pkey_reg: 0x%016llx"
51062306a36Sopenharmony_ci				" shadow: 0x%016llx\n",
51162306a36Sopenharmony_ci				__func__,
51262306a36Sopenharmony_ci				__LINE__, ret, __read_pkey_reg(),
51362306a36Sopenharmony_ci				shadow_pkey_reg);
51462306a36Sopenharmony_ci		/*
51562306a36Sopenharmony_ci		 * move the new state in from init_val
51662306a36Sopenharmony_ci		 * (remember, we cheated and init_val == pkey_reg format)
51762306a36Sopenharmony_ci		 */
51862306a36Sopenharmony_ci		shadow_pkey_reg = set_pkey_bits(shadow_pkey_reg, ret,
51962306a36Sopenharmony_ci						init_val);
52062306a36Sopenharmony_ci	}
52162306a36Sopenharmony_ci	dprintf4("%s()::%d, ret: %d pkey_reg: 0x%016llx"
52262306a36Sopenharmony_ci			" shadow: 0x%016llx\n",
52362306a36Sopenharmony_ci			__func__, __LINE__, ret, __read_pkey_reg(),
52462306a36Sopenharmony_ci			shadow_pkey_reg);
52562306a36Sopenharmony_ci	dprintf1("%s()::%d errno: %d\n", __func__, __LINE__, errno);
52662306a36Sopenharmony_ci	/* for shadow checking: */
52762306a36Sopenharmony_ci	read_pkey_reg();
52862306a36Sopenharmony_ci	dprintf4("%s()::%d, ret: %d pkey_reg: 0x%016llx"
52962306a36Sopenharmony_ci		 " shadow: 0x%016llx\n",
53062306a36Sopenharmony_ci		__func__, __LINE__, ret, __read_pkey_reg(),
53162306a36Sopenharmony_ci		shadow_pkey_reg);
53262306a36Sopenharmony_ci	return ret;
53362306a36Sopenharmony_ci}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ciint sys_pkey_free(unsigned long pkey)
53662306a36Sopenharmony_ci{
53762306a36Sopenharmony_ci	int ret = syscall(SYS_pkey_free, pkey);
53862306a36Sopenharmony_ci	dprintf1("%s(pkey=%ld) syscall ret: %d\n", __func__, pkey, ret);
53962306a36Sopenharmony_ci	return ret;
54062306a36Sopenharmony_ci}
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci/*
54362306a36Sopenharmony_ci * I had a bug where pkey bits could be set by mprotect() but
54462306a36Sopenharmony_ci * not cleared.  This ensures we get lots of random bit sets
54562306a36Sopenharmony_ci * and clears on the vma and pte pkey bits.
54662306a36Sopenharmony_ci */
54762306a36Sopenharmony_ciint alloc_random_pkey(void)
54862306a36Sopenharmony_ci{
54962306a36Sopenharmony_ci	int max_nr_pkey_allocs;
55062306a36Sopenharmony_ci	int ret;
55162306a36Sopenharmony_ci	int i;
55262306a36Sopenharmony_ci	int alloced_pkeys[NR_PKEYS];
55362306a36Sopenharmony_ci	int nr_alloced = 0;
55462306a36Sopenharmony_ci	int random_index;
55562306a36Sopenharmony_ci	memset(alloced_pkeys, 0, sizeof(alloced_pkeys));
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	/* allocate every possible key and make a note of which ones we got */
55862306a36Sopenharmony_ci	max_nr_pkey_allocs = NR_PKEYS;
55962306a36Sopenharmony_ci	for (i = 0; i < max_nr_pkey_allocs; i++) {
56062306a36Sopenharmony_ci		int new_pkey = alloc_pkey();
56162306a36Sopenharmony_ci		if (new_pkey < 0)
56262306a36Sopenharmony_ci			break;
56362306a36Sopenharmony_ci		alloced_pkeys[nr_alloced++] = new_pkey;
56462306a36Sopenharmony_ci	}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	pkey_assert(nr_alloced > 0);
56762306a36Sopenharmony_ci	/* select a random one out of the allocated ones */
56862306a36Sopenharmony_ci	random_index = rand() % nr_alloced;
56962306a36Sopenharmony_ci	ret = alloced_pkeys[random_index];
57062306a36Sopenharmony_ci	/* now zero it out so we don't free it next */
57162306a36Sopenharmony_ci	alloced_pkeys[random_index] = 0;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	/* go through the allocated ones that we did not want and free them */
57462306a36Sopenharmony_ci	for (i = 0; i < nr_alloced; i++) {
57562306a36Sopenharmony_ci		int free_ret;
57662306a36Sopenharmony_ci		if (!alloced_pkeys[i])
57762306a36Sopenharmony_ci			continue;
57862306a36Sopenharmony_ci		free_ret = sys_pkey_free(alloced_pkeys[i]);
57962306a36Sopenharmony_ci		pkey_assert(!free_ret);
58062306a36Sopenharmony_ci	}
58162306a36Sopenharmony_ci	dprintf1("%s()::%d, ret: %d pkey_reg: 0x%016llx"
58262306a36Sopenharmony_ci			 " shadow: 0x%016llx\n", __func__,
58362306a36Sopenharmony_ci			__LINE__, ret, __read_pkey_reg(), shadow_pkey_reg);
58462306a36Sopenharmony_ci	return ret;
58562306a36Sopenharmony_ci}
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ciint mprotect_pkey(void *ptr, size_t size, unsigned long orig_prot,
58862306a36Sopenharmony_ci		unsigned long pkey)
58962306a36Sopenharmony_ci{
59062306a36Sopenharmony_ci	int nr_iterations = random() % 100;
59162306a36Sopenharmony_ci	int ret;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	while (0) {
59462306a36Sopenharmony_ci		int rpkey = alloc_random_pkey();
59562306a36Sopenharmony_ci		ret = sys_mprotect_pkey(ptr, size, orig_prot, pkey);
59662306a36Sopenharmony_ci		dprintf1("sys_mprotect_pkey(%p, %zx, prot=0x%lx, pkey=%ld) ret: %d\n",
59762306a36Sopenharmony_ci				ptr, size, orig_prot, pkey, ret);
59862306a36Sopenharmony_ci		if (nr_iterations-- < 0)
59962306a36Sopenharmony_ci			break;
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci		dprintf1("%s()::%d, ret: %d pkey_reg: 0x%016llx"
60262306a36Sopenharmony_ci			" shadow: 0x%016llx\n",
60362306a36Sopenharmony_ci			__func__, __LINE__, ret, __read_pkey_reg(),
60462306a36Sopenharmony_ci			shadow_pkey_reg);
60562306a36Sopenharmony_ci		sys_pkey_free(rpkey);
60662306a36Sopenharmony_ci		dprintf1("%s()::%d, ret: %d pkey_reg: 0x%016llx"
60762306a36Sopenharmony_ci			" shadow: 0x%016llx\n",
60862306a36Sopenharmony_ci			__func__, __LINE__, ret, __read_pkey_reg(),
60962306a36Sopenharmony_ci			shadow_pkey_reg);
61062306a36Sopenharmony_ci	}
61162306a36Sopenharmony_ci	pkey_assert(pkey < NR_PKEYS);
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	ret = sys_mprotect_pkey(ptr, size, orig_prot, pkey);
61462306a36Sopenharmony_ci	dprintf1("mprotect_pkey(%p, %zx, prot=0x%lx, pkey=%ld) ret: %d\n",
61562306a36Sopenharmony_ci			ptr, size, orig_prot, pkey, ret);
61662306a36Sopenharmony_ci	pkey_assert(!ret);
61762306a36Sopenharmony_ci	dprintf1("%s()::%d, ret: %d pkey_reg: 0x%016llx"
61862306a36Sopenharmony_ci			" shadow: 0x%016llx\n", __func__,
61962306a36Sopenharmony_ci			__LINE__, ret, __read_pkey_reg(), shadow_pkey_reg);
62062306a36Sopenharmony_ci	return ret;
62162306a36Sopenharmony_ci}
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_cistruct pkey_malloc_record {
62462306a36Sopenharmony_ci	void *ptr;
62562306a36Sopenharmony_ci	long size;
62662306a36Sopenharmony_ci	int prot;
62762306a36Sopenharmony_ci};
62862306a36Sopenharmony_cistruct pkey_malloc_record *pkey_malloc_records;
62962306a36Sopenharmony_cistruct pkey_malloc_record *pkey_last_malloc_record;
63062306a36Sopenharmony_cilong nr_pkey_malloc_records;
63162306a36Sopenharmony_civoid record_pkey_malloc(void *ptr, long size, int prot)
63262306a36Sopenharmony_ci{
63362306a36Sopenharmony_ci	long i;
63462306a36Sopenharmony_ci	struct pkey_malloc_record *rec = NULL;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	for (i = 0; i < nr_pkey_malloc_records; i++) {
63762306a36Sopenharmony_ci		rec = &pkey_malloc_records[i];
63862306a36Sopenharmony_ci		/* find a free record */
63962306a36Sopenharmony_ci		if (rec)
64062306a36Sopenharmony_ci			break;
64162306a36Sopenharmony_ci	}
64262306a36Sopenharmony_ci	if (!rec) {
64362306a36Sopenharmony_ci		/* every record is full */
64462306a36Sopenharmony_ci		size_t old_nr_records = nr_pkey_malloc_records;
64562306a36Sopenharmony_ci		size_t new_nr_records = (nr_pkey_malloc_records * 2 + 1);
64662306a36Sopenharmony_ci		size_t new_size = new_nr_records * sizeof(struct pkey_malloc_record);
64762306a36Sopenharmony_ci		dprintf2("new_nr_records: %zd\n", new_nr_records);
64862306a36Sopenharmony_ci		dprintf2("new_size: %zd\n", new_size);
64962306a36Sopenharmony_ci		pkey_malloc_records = realloc(pkey_malloc_records, new_size);
65062306a36Sopenharmony_ci		pkey_assert(pkey_malloc_records != NULL);
65162306a36Sopenharmony_ci		rec = &pkey_malloc_records[nr_pkey_malloc_records];
65262306a36Sopenharmony_ci		/*
65362306a36Sopenharmony_ci		 * realloc() does not initialize memory, so zero it from
65462306a36Sopenharmony_ci		 * the first new record all the way to the end.
65562306a36Sopenharmony_ci		 */
65662306a36Sopenharmony_ci		for (i = 0; i < new_nr_records - old_nr_records; i++)
65762306a36Sopenharmony_ci			memset(rec + i, 0, sizeof(*rec));
65862306a36Sopenharmony_ci	}
65962306a36Sopenharmony_ci	dprintf3("filling malloc record[%d/%p]: {%p, %ld}\n",
66062306a36Sopenharmony_ci		(int)(rec - pkey_malloc_records), rec, ptr, size);
66162306a36Sopenharmony_ci	rec->ptr = ptr;
66262306a36Sopenharmony_ci	rec->size = size;
66362306a36Sopenharmony_ci	rec->prot = prot;
66462306a36Sopenharmony_ci	pkey_last_malloc_record = rec;
66562306a36Sopenharmony_ci	nr_pkey_malloc_records++;
66662306a36Sopenharmony_ci}
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_civoid free_pkey_malloc(void *ptr)
66962306a36Sopenharmony_ci{
67062306a36Sopenharmony_ci	long i;
67162306a36Sopenharmony_ci	int ret;
67262306a36Sopenharmony_ci	dprintf3("%s(%p)\n", __func__, ptr);
67362306a36Sopenharmony_ci	for (i = 0; i < nr_pkey_malloc_records; i++) {
67462306a36Sopenharmony_ci		struct pkey_malloc_record *rec = &pkey_malloc_records[i];
67562306a36Sopenharmony_ci		dprintf4("looking for ptr %p at record[%ld/%p]: {%p, %ld}\n",
67662306a36Sopenharmony_ci				ptr, i, rec, rec->ptr, rec->size);
67762306a36Sopenharmony_ci		if ((ptr <  rec->ptr) ||
67862306a36Sopenharmony_ci		    (ptr >= rec->ptr + rec->size))
67962306a36Sopenharmony_ci			continue;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci		dprintf3("found ptr %p at record[%ld/%p]: {%p, %ld}\n",
68262306a36Sopenharmony_ci				ptr, i, rec, rec->ptr, rec->size);
68362306a36Sopenharmony_ci		nr_pkey_malloc_records--;
68462306a36Sopenharmony_ci		ret = munmap(rec->ptr, rec->size);
68562306a36Sopenharmony_ci		dprintf3("munmap ret: %d\n", ret);
68662306a36Sopenharmony_ci		pkey_assert(!ret);
68762306a36Sopenharmony_ci		dprintf3("clearing rec->ptr, rec: %p\n", rec);
68862306a36Sopenharmony_ci		rec->ptr = NULL;
68962306a36Sopenharmony_ci		dprintf3("done clearing rec->ptr, rec: %p\n", rec);
69062306a36Sopenharmony_ci		return;
69162306a36Sopenharmony_ci	}
69262306a36Sopenharmony_ci	pkey_assert(false);
69362306a36Sopenharmony_ci}
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_civoid *malloc_pkey_with_mprotect(long size, int prot, u16 pkey)
69762306a36Sopenharmony_ci{
69862306a36Sopenharmony_ci	void *ptr;
69962306a36Sopenharmony_ci	int ret;
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	read_pkey_reg();
70262306a36Sopenharmony_ci	dprintf1("doing %s(size=%ld, prot=0x%x, pkey=%d)\n", __func__,
70362306a36Sopenharmony_ci			size, prot, pkey);
70462306a36Sopenharmony_ci	pkey_assert(pkey < NR_PKEYS);
70562306a36Sopenharmony_ci	ptr = mmap(NULL, size, prot, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
70662306a36Sopenharmony_ci	pkey_assert(ptr != (void *)-1);
70762306a36Sopenharmony_ci	ret = mprotect_pkey((void *)ptr, PAGE_SIZE, prot, pkey);
70862306a36Sopenharmony_ci	pkey_assert(!ret);
70962306a36Sopenharmony_ci	record_pkey_malloc(ptr, size, prot);
71062306a36Sopenharmony_ci	read_pkey_reg();
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	dprintf1("%s() for pkey %d @ %p\n", __func__, pkey, ptr);
71362306a36Sopenharmony_ci	return ptr;
71462306a36Sopenharmony_ci}
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_civoid *malloc_pkey_anon_huge(long size, int prot, u16 pkey)
71762306a36Sopenharmony_ci{
71862306a36Sopenharmony_ci	int ret;
71962306a36Sopenharmony_ci	void *ptr;
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	dprintf1("doing %s(size=%ld, prot=0x%x, pkey=%d)\n", __func__,
72262306a36Sopenharmony_ci			size, prot, pkey);
72362306a36Sopenharmony_ci	/*
72462306a36Sopenharmony_ci	 * Guarantee we can fit at least one huge page in the resulting
72562306a36Sopenharmony_ci	 * allocation by allocating space for 2:
72662306a36Sopenharmony_ci	 */
72762306a36Sopenharmony_ci	size = ALIGN_UP(size, HPAGE_SIZE * 2);
72862306a36Sopenharmony_ci	ptr = mmap(NULL, size, PROT_NONE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
72962306a36Sopenharmony_ci	pkey_assert(ptr != (void *)-1);
73062306a36Sopenharmony_ci	record_pkey_malloc(ptr, size, prot);
73162306a36Sopenharmony_ci	mprotect_pkey(ptr, size, prot, pkey);
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	dprintf1("unaligned ptr: %p\n", ptr);
73462306a36Sopenharmony_ci	ptr = ALIGN_PTR_UP(ptr, HPAGE_SIZE);
73562306a36Sopenharmony_ci	dprintf1("  aligned ptr: %p\n", ptr);
73662306a36Sopenharmony_ci	ret = madvise(ptr, HPAGE_SIZE, MADV_HUGEPAGE);
73762306a36Sopenharmony_ci	dprintf1("MADV_HUGEPAGE ret: %d\n", ret);
73862306a36Sopenharmony_ci	ret = madvise(ptr, HPAGE_SIZE, MADV_WILLNEED);
73962306a36Sopenharmony_ci	dprintf1("MADV_WILLNEED ret: %d\n", ret);
74062306a36Sopenharmony_ci	memset(ptr, 0, HPAGE_SIZE);
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	dprintf1("mmap()'d thp for pkey %d @ %p\n", pkey, ptr);
74362306a36Sopenharmony_ci	return ptr;
74462306a36Sopenharmony_ci}
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ciint hugetlb_setup_ok;
74762306a36Sopenharmony_ci#define SYSFS_FMT_NR_HUGE_PAGES "/sys/kernel/mm/hugepages/hugepages-%ldkB/nr_hugepages"
74862306a36Sopenharmony_ci#define GET_NR_HUGE_PAGES 10
74962306a36Sopenharmony_civoid setup_hugetlbfs(void)
75062306a36Sopenharmony_ci{
75162306a36Sopenharmony_ci	int err;
75262306a36Sopenharmony_ci	int fd;
75362306a36Sopenharmony_ci	char buf[256];
75462306a36Sopenharmony_ci	long hpagesz_kb;
75562306a36Sopenharmony_ci	long hpagesz_mb;
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	if (geteuid() != 0) {
75862306a36Sopenharmony_ci		fprintf(stderr, "WARNING: not run as root, can not do hugetlb test\n");
75962306a36Sopenharmony_ci		return;
76062306a36Sopenharmony_ci	}
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	cat_into_file(__stringify(GET_NR_HUGE_PAGES), "/proc/sys/vm/nr_hugepages");
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	/*
76562306a36Sopenharmony_ci	 * Now go make sure that we got the pages and that they
76662306a36Sopenharmony_ci	 * are PMD-level pages. Someone might have made PUD-level
76762306a36Sopenharmony_ci	 * pages the default.
76862306a36Sopenharmony_ci	 */
76962306a36Sopenharmony_ci	hpagesz_kb = HPAGE_SIZE / 1024;
77062306a36Sopenharmony_ci	hpagesz_mb = hpagesz_kb / 1024;
77162306a36Sopenharmony_ci	sprintf(buf, SYSFS_FMT_NR_HUGE_PAGES, hpagesz_kb);
77262306a36Sopenharmony_ci	fd = open(buf, O_RDONLY);
77362306a36Sopenharmony_ci	if (fd < 0) {
77462306a36Sopenharmony_ci		fprintf(stderr, "opening sysfs %ldM hugetlb config: %s\n",
77562306a36Sopenharmony_ci			hpagesz_mb, strerror(errno));
77662306a36Sopenharmony_ci		return;
77762306a36Sopenharmony_ci	}
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	/* -1 to guarantee leaving the trailing \0 */
78062306a36Sopenharmony_ci	err = read(fd, buf, sizeof(buf)-1);
78162306a36Sopenharmony_ci	close(fd);
78262306a36Sopenharmony_ci	if (err <= 0) {
78362306a36Sopenharmony_ci		fprintf(stderr, "reading sysfs %ldM hugetlb config: %s\n",
78462306a36Sopenharmony_ci			hpagesz_mb, strerror(errno));
78562306a36Sopenharmony_ci		return;
78662306a36Sopenharmony_ci	}
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	if (atoi(buf) != GET_NR_HUGE_PAGES) {
78962306a36Sopenharmony_ci		fprintf(stderr, "could not confirm %ldM pages, got: '%s' expected %d\n",
79062306a36Sopenharmony_ci			hpagesz_mb, buf, GET_NR_HUGE_PAGES);
79162306a36Sopenharmony_ci		return;
79262306a36Sopenharmony_ci	}
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	hugetlb_setup_ok = 1;
79562306a36Sopenharmony_ci}
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_civoid *malloc_pkey_hugetlb(long size, int prot, u16 pkey)
79862306a36Sopenharmony_ci{
79962306a36Sopenharmony_ci	void *ptr;
80062306a36Sopenharmony_ci	int flags = MAP_ANONYMOUS|MAP_PRIVATE|MAP_HUGETLB;
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	if (!hugetlb_setup_ok)
80362306a36Sopenharmony_ci		return PTR_ERR_ENOTSUP;
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	dprintf1("doing %s(%ld, %x, %x)\n", __func__, size, prot, pkey);
80662306a36Sopenharmony_ci	size = ALIGN_UP(size, HPAGE_SIZE * 2);
80762306a36Sopenharmony_ci	pkey_assert(pkey < NR_PKEYS);
80862306a36Sopenharmony_ci	ptr = mmap(NULL, size, PROT_NONE, flags, -1, 0);
80962306a36Sopenharmony_ci	pkey_assert(ptr != (void *)-1);
81062306a36Sopenharmony_ci	mprotect_pkey(ptr, size, prot, pkey);
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	record_pkey_malloc(ptr, size, prot);
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	dprintf1("mmap()'d hugetlbfs for pkey %d @ %p\n", pkey, ptr);
81562306a36Sopenharmony_ci	return ptr;
81662306a36Sopenharmony_ci}
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_civoid *malloc_pkey_mmap_dax(long size, int prot, u16 pkey)
81962306a36Sopenharmony_ci{
82062306a36Sopenharmony_ci	void *ptr;
82162306a36Sopenharmony_ci	int fd;
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	dprintf1("doing %s(size=%ld, prot=0x%x, pkey=%d)\n", __func__,
82462306a36Sopenharmony_ci			size, prot, pkey);
82562306a36Sopenharmony_ci	pkey_assert(pkey < NR_PKEYS);
82662306a36Sopenharmony_ci	fd = open("/dax/foo", O_RDWR);
82762306a36Sopenharmony_ci	pkey_assert(fd >= 0);
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	ptr = mmap(0, size, prot, MAP_SHARED, fd, 0);
83062306a36Sopenharmony_ci	pkey_assert(ptr != (void *)-1);
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	mprotect_pkey(ptr, size, prot, pkey);
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	record_pkey_malloc(ptr, size, prot);
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	dprintf1("mmap()'d for pkey %d @ %p\n", pkey, ptr);
83762306a36Sopenharmony_ci	close(fd);
83862306a36Sopenharmony_ci	return ptr;
83962306a36Sopenharmony_ci}
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_civoid *(*pkey_malloc[])(long size, int prot, u16 pkey) = {
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	malloc_pkey_with_mprotect,
84462306a36Sopenharmony_ci	malloc_pkey_with_mprotect_subpage,
84562306a36Sopenharmony_ci	malloc_pkey_anon_huge,
84662306a36Sopenharmony_ci	malloc_pkey_hugetlb
84762306a36Sopenharmony_ci/* can not do direct with the pkey_mprotect() API:
84862306a36Sopenharmony_ci	malloc_pkey_mmap_direct,
84962306a36Sopenharmony_ci	malloc_pkey_mmap_dax,
85062306a36Sopenharmony_ci*/
85162306a36Sopenharmony_ci};
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_civoid *malloc_pkey(long size, int prot, u16 pkey)
85462306a36Sopenharmony_ci{
85562306a36Sopenharmony_ci	void *ret;
85662306a36Sopenharmony_ci	static int malloc_type;
85762306a36Sopenharmony_ci	int nr_malloc_types = ARRAY_SIZE(pkey_malloc);
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	pkey_assert(pkey < NR_PKEYS);
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	while (1) {
86262306a36Sopenharmony_ci		pkey_assert(malloc_type < nr_malloc_types);
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci		ret = pkey_malloc[malloc_type](size, prot, pkey);
86562306a36Sopenharmony_ci		pkey_assert(ret != (void *)-1);
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci		malloc_type++;
86862306a36Sopenharmony_ci		if (malloc_type >= nr_malloc_types)
86962306a36Sopenharmony_ci			malloc_type = (random()%nr_malloc_types);
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci		/* try again if the malloc_type we tried is unsupported */
87262306a36Sopenharmony_ci		if (ret == PTR_ERR_ENOTSUP)
87362306a36Sopenharmony_ci			continue;
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci		break;
87662306a36Sopenharmony_ci	}
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	dprintf3("%s(%ld, prot=%x, pkey=%x) returning: %p\n", __func__,
87962306a36Sopenharmony_ci			size, prot, pkey, ret);
88062306a36Sopenharmony_ci	return ret;
88162306a36Sopenharmony_ci}
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ciint last_pkey_faults;
88462306a36Sopenharmony_ci#define UNKNOWN_PKEY -2
88562306a36Sopenharmony_civoid expected_pkey_fault(int pkey)
88662306a36Sopenharmony_ci{
88762306a36Sopenharmony_ci	dprintf2("%s(): last_pkey_faults: %d pkey_faults: %d\n",
88862306a36Sopenharmony_ci			__func__, last_pkey_faults, pkey_faults);
88962306a36Sopenharmony_ci	dprintf2("%s(%d): last_si_pkey: %d\n", __func__, pkey, last_si_pkey);
89062306a36Sopenharmony_ci	pkey_assert(last_pkey_faults + 1 == pkey_faults);
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci       /*
89362306a36Sopenharmony_ci	* For exec-only memory, we do not know the pkey in
89462306a36Sopenharmony_ci	* advance, so skip this check.
89562306a36Sopenharmony_ci	*/
89662306a36Sopenharmony_ci	if (pkey != UNKNOWN_PKEY)
89762306a36Sopenharmony_ci		pkey_assert(last_si_pkey == pkey);
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci#if defined(__i386__) || defined(__x86_64__) /* arch */
90062306a36Sopenharmony_ci	/*
90162306a36Sopenharmony_ci	 * The signal handler shold have cleared out PKEY register to let the
90262306a36Sopenharmony_ci	 * test program continue.  We now have to restore it.
90362306a36Sopenharmony_ci	 */
90462306a36Sopenharmony_ci	if (__read_pkey_reg() != 0)
90562306a36Sopenharmony_ci#else /* arch */
90662306a36Sopenharmony_ci	if (__read_pkey_reg() != shadow_pkey_reg)
90762306a36Sopenharmony_ci#endif /* arch */
90862306a36Sopenharmony_ci		pkey_assert(0);
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	__write_pkey_reg(shadow_pkey_reg);
91162306a36Sopenharmony_ci	dprintf1("%s() set pkey_reg=%016llx to restore state after signal "
91262306a36Sopenharmony_ci		       "nuked it\n", __func__, shadow_pkey_reg);
91362306a36Sopenharmony_ci	last_pkey_faults = pkey_faults;
91462306a36Sopenharmony_ci	last_si_pkey = -1;
91562306a36Sopenharmony_ci}
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci#define do_not_expect_pkey_fault(msg)	do {			\
91862306a36Sopenharmony_ci	if (last_pkey_faults != pkey_faults)			\
91962306a36Sopenharmony_ci		dprintf0("unexpected PKey fault: %s\n", msg);	\
92062306a36Sopenharmony_ci	pkey_assert(last_pkey_faults == pkey_faults);		\
92162306a36Sopenharmony_ci} while (0)
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ciint test_fds[10] = { -1 };
92462306a36Sopenharmony_ciint nr_test_fds;
92562306a36Sopenharmony_civoid __save_test_fd(int fd)
92662306a36Sopenharmony_ci{
92762306a36Sopenharmony_ci	pkey_assert(fd >= 0);
92862306a36Sopenharmony_ci	pkey_assert(nr_test_fds < ARRAY_SIZE(test_fds));
92962306a36Sopenharmony_ci	test_fds[nr_test_fds] = fd;
93062306a36Sopenharmony_ci	nr_test_fds++;
93162306a36Sopenharmony_ci}
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ciint get_test_read_fd(void)
93462306a36Sopenharmony_ci{
93562306a36Sopenharmony_ci	int test_fd = open("/etc/passwd", O_RDONLY);
93662306a36Sopenharmony_ci	__save_test_fd(test_fd);
93762306a36Sopenharmony_ci	return test_fd;
93862306a36Sopenharmony_ci}
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_civoid close_test_fds(void)
94162306a36Sopenharmony_ci{
94262306a36Sopenharmony_ci	int i;
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci	for (i = 0; i < nr_test_fds; i++) {
94562306a36Sopenharmony_ci		if (test_fds[i] < 0)
94662306a36Sopenharmony_ci			continue;
94762306a36Sopenharmony_ci		close(test_fds[i]);
94862306a36Sopenharmony_ci		test_fds[i] = -1;
94962306a36Sopenharmony_ci	}
95062306a36Sopenharmony_ci	nr_test_fds = 0;
95162306a36Sopenharmony_ci}
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci#define barrier() __asm__ __volatile__("": : :"memory")
95462306a36Sopenharmony_ci__attribute__((noinline)) int read_ptr(int *ptr)
95562306a36Sopenharmony_ci{
95662306a36Sopenharmony_ci	/*
95762306a36Sopenharmony_ci	 * Keep GCC from optimizing this away somehow
95862306a36Sopenharmony_ci	 */
95962306a36Sopenharmony_ci	barrier();
96062306a36Sopenharmony_ci	return *ptr;
96162306a36Sopenharmony_ci}
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_civoid test_pkey_alloc_free_attach_pkey0(int *ptr, u16 pkey)
96462306a36Sopenharmony_ci{
96562306a36Sopenharmony_ci	int i, err;
96662306a36Sopenharmony_ci	int max_nr_pkey_allocs;
96762306a36Sopenharmony_ci	int alloced_pkeys[NR_PKEYS];
96862306a36Sopenharmony_ci	int nr_alloced = 0;
96962306a36Sopenharmony_ci	long size;
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	pkey_assert(pkey_last_malloc_record);
97262306a36Sopenharmony_ci	size = pkey_last_malloc_record->size;
97362306a36Sopenharmony_ci	/*
97462306a36Sopenharmony_ci	 * This is a bit of a hack.  But mprotect() requires
97562306a36Sopenharmony_ci	 * huge-page-aligned sizes when operating on hugetlbfs.
97662306a36Sopenharmony_ci	 * So, make sure that we use something that's a multiple
97762306a36Sopenharmony_ci	 * of a huge page when we can.
97862306a36Sopenharmony_ci	 */
97962306a36Sopenharmony_ci	if (size >= HPAGE_SIZE)
98062306a36Sopenharmony_ci		size = HPAGE_SIZE;
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	/* allocate every possible key and make sure key-0 never got allocated */
98362306a36Sopenharmony_ci	max_nr_pkey_allocs = NR_PKEYS;
98462306a36Sopenharmony_ci	for (i = 0; i < max_nr_pkey_allocs; i++) {
98562306a36Sopenharmony_ci		int new_pkey = alloc_pkey();
98662306a36Sopenharmony_ci		pkey_assert(new_pkey != 0);
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci		if (new_pkey < 0)
98962306a36Sopenharmony_ci			break;
99062306a36Sopenharmony_ci		alloced_pkeys[nr_alloced++] = new_pkey;
99162306a36Sopenharmony_ci	}
99262306a36Sopenharmony_ci	/* free all the allocated keys */
99362306a36Sopenharmony_ci	for (i = 0; i < nr_alloced; i++) {
99462306a36Sopenharmony_ci		int free_ret;
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci		if (!alloced_pkeys[i])
99762306a36Sopenharmony_ci			continue;
99862306a36Sopenharmony_ci		free_ret = sys_pkey_free(alloced_pkeys[i]);
99962306a36Sopenharmony_ci		pkey_assert(!free_ret);
100062306a36Sopenharmony_ci	}
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	/* attach key-0 in various modes */
100362306a36Sopenharmony_ci	err = sys_mprotect_pkey(ptr, size, PROT_READ, 0);
100462306a36Sopenharmony_ci	pkey_assert(!err);
100562306a36Sopenharmony_ci	err = sys_mprotect_pkey(ptr, size, PROT_WRITE, 0);
100662306a36Sopenharmony_ci	pkey_assert(!err);
100762306a36Sopenharmony_ci	err = sys_mprotect_pkey(ptr, size, PROT_EXEC, 0);
100862306a36Sopenharmony_ci	pkey_assert(!err);
100962306a36Sopenharmony_ci	err = sys_mprotect_pkey(ptr, size, PROT_READ|PROT_WRITE, 0);
101062306a36Sopenharmony_ci	pkey_assert(!err);
101162306a36Sopenharmony_ci	err = sys_mprotect_pkey(ptr, size, PROT_READ|PROT_WRITE|PROT_EXEC, 0);
101262306a36Sopenharmony_ci	pkey_assert(!err);
101362306a36Sopenharmony_ci}
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_civoid test_read_of_write_disabled_region(int *ptr, u16 pkey)
101662306a36Sopenharmony_ci{
101762306a36Sopenharmony_ci	int ptr_contents;
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	dprintf1("disabling write access to PKEY[1], doing read\n");
102062306a36Sopenharmony_ci	pkey_write_deny(pkey);
102162306a36Sopenharmony_ci	ptr_contents = read_ptr(ptr);
102262306a36Sopenharmony_ci	dprintf1("*ptr: %d\n", ptr_contents);
102362306a36Sopenharmony_ci	dprintf1("\n");
102462306a36Sopenharmony_ci}
102562306a36Sopenharmony_civoid test_read_of_access_disabled_region(int *ptr, u16 pkey)
102662306a36Sopenharmony_ci{
102762306a36Sopenharmony_ci	int ptr_contents;
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	dprintf1("disabling access to PKEY[%02d], doing read @ %p\n", pkey, ptr);
103062306a36Sopenharmony_ci	read_pkey_reg();
103162306a36Sopenharmony_ci	pkey_access_deny(pkey);
103262306a36Sopenharmony_ci	ptr_contents = read_ptr(ptr);
103362306a36Sopenharmony_ci	dprintf1("*ptr: %d\n", ptr_contents);
103462306a36Sopenharmony_ci	expected_pkey_fault(pkey);
103562306a36Sopenharmony_ci}
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_civoid test_read_of_access_disabled_region_with_page_already_mapped(int *ptr,
103862306a36Sopenharmony_ci		u16 pkey)
103962306a36Sopenharmony_ci{
104062306a36Sopenharmony_ci	int ptr_contents;
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	dprintf1("disabling access to PKEY[%02d], doing read @ %p\n",
104362306a36Sopenharmony_ci				pkey, ptr);
104462306a36Sopenharmony_ci	ptr_contents = read_ptr(ptr);
104562306a36Sopenharmony_ci	dprintf1("reading ptr before disabling the read : %d\n",
104662306a36Sopenharmony_ci			ptr_contents);
104762306a36Sopenharmony_ci	read_pkey_reg();
104862306a36Sopenharmony_ci	pkey_access_deny(pkey);
104962306a36Sopenharmony_ci	ptr_contents = read_ptr(ptr);
105062306a36Sopenharmony_ci	dprintf1("*ptr: %d\n", ptr_contents);
105162306a36Sopenharmony_ci	expected_pkey_fault(pkey);
105262306a36Sopenharmony_ci}
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_civoid test_write_of_write_disabled_region_with_page_already_mapped(int *ptr,
105562306a36Sopenharmony_ci		u16 pkey)
105662306a36Sopenharmony_ci{
105762306a36Sopenharmony_ci	*ptr = __LINE__;
105862306a36Sopenharmony_ci	dprintf1("disabling write access; after accessing the page, "
105962306a36Sopenharmony_ci		"to PKEY[%02d], doing write\n", pkey);
106062306a36Sopenharmony_ci	pkey_write_deny(pkey);
106162306a36Sopenharmony_ci	*ptr = __LINE__;
106262306a36Sopenharmony_ci	expected_pkey_fault(pkey);
106362306a36Sopenharmony_ci}
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_civoid test_write_of_write_disabled_region(int *ptr, u16 pkey)
106662306a36Sopenharmony_ci{
106762306a36Sopenharmony_ci	dprintf1("disabling write access to PKEY[%02d], doing write\n", pkey);
106862306a36Sopenharmony_ci	pkey_write_deny(pkey);
106962306a36Sopenharmony_ci	*ptr = __LINE__;
107062306a36Sopenharmony_ci	expected_pkey_fault(pkey);
107162306a36Sopenharmony_ci}
107262306a36Sopenharmony_civoid test_write_of_access_disabled_region(int *ptr, u16 pkey)
107362306a36Sopenharmony_ci{
107462306a36Sopenharmony_ci	dprintf1("disabling access to PKEY[%02d], doing write\n", pkey);
107562306a36Sopenharmony_ci	pkey_access_deny(pkey);
107662306a36Sopenharmony_ci	*ptr = __LINE__;
107762306a36Sopenharmony_ci	expected_pkey_fault(pkey);
107862306a36Sopenharmony_ci}
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_civoid test_write_of_access_disabled_region_with_page_already_mapped(int *ptr,
108162306a36Sopenharmony_ci			u16 pkey)
108262306a36Sopenharmony_ci{
108362306a36Sopenharmony_ci	*ptr = __LINE__;
108462306a36Sopenharmony_ci	dprintf1("disabling access; after accessing the page, "
108562306a36Sopenharmony_ci		" to PKEY[%02d], doing write\n", pkey);
108662306a36Sopenharmony_ci	pkey_access_deny(pkey);
108762306a36Sopenharmony_ci	*ptr = __LINE__;
108862306a36Sopenharmony_ci	expected_pkey_fault(pkey);
108962306a36Sopenharmony_ci}
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_civoid test_kernel_write_of_access_disabled_region(int *ptr, u16 pkey)
109262306a36Sopenharmony_ci{
109362306a36Sopenharmony_ci	int ret;
109462306a36Sopenharmony_ci	int test_fd = get_test_read_fd();
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	dprintf1("disabling access to PKEY[%02d], "
109762306a36Sopenharmony_ci		 "having kernel read() to buffer\n", pkey);
109862306a36Sopenharmony_ci	pkey_access_deny(pkey);
109962306a36Sopenharmony_ci	ret = read(test_fd, ptr, 1);
110062306a36Sopenharmony_ci	dprintf1("read ret: %d\n", ret);
110162306a36Sopenharmony_ci	pkey_assert(ret);
110262306a36Sopenharmony_ci}
110362306a36Sopenharmony_civoid test_kernel_write_of_write_disabled_region(int *ptr, u16 pkey)
110462306a36Sopenharmony_ci{
110562306a36Sopenharmony_ci	int ret;
110662306a36Sopenharmony_ci	int test_fd = get_test_read_fd();
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci	pkey_write_deny(pkey);
110962306a36Sopenharmony_ci	ret = read(test_fd, ptr, 100);
111062306a36Sopenharmony_ci	dprintf1("read ret: %d\n", ret);
111162306a36Sopenharmony_ci	if (ret < 0 && (DEBUG_LEVEL > 0))
111262306a36Sopenharmony_ci		perror("verbose read result (OK for this to be bad)");
111362306a36Sopenharmony_ci	pkey_assert(ret);
111462306a36Sopenharmony_ci}
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_civoid test_kernel_gup_of_access_disabled_region(int *ptr, u16 pkey)
111762306a36Sopenharmony_ci{
111862306a36Sopenharmony_ci	int pipe_ret, vmsplice_ret;
111962306a36Sopenharmony_ci	struct iovec iov;
112062306a36Sopenharmony_ci	int pipe_fds[2];
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci	pipe_ret = pipe(pipe_fds);
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci	pkey_assert(pipe_ret == 0);
112562306a36Sopenharmony_ci	dprintf1("disabling access to PKEY[%02d], "
112662306a36Sopenharmony_ci		 "having kernel vmsplice from buffer\n", pkey);
112762306a36Sopenharmony_ci	pkey_access_deny(pkey);
112862306a36Sopenharmony_ci	iov.iov_base = ptr;
112962306a36Sopenharmony_ci	iov.iov_len = PAGE_SIZE;
113062306a36Sopenharmony_ci	vmsplice_ret = vmsplice(pipe_fds[1], &iov, 1, SPLICE_F_GIFT);
113162306a36Sopenharmony_ci	dprintf1("vmsplice() ret: %d\n", vmsplice_ret);
113262306a36Sopenharmony_ci	pkey_assert(vmsplice_ret == -1);
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci	close(pipe_fds[0]);
113562306a36Sopenharmony_ci	close(pipe_fds[1]);
113662306a36Sopenharmony_ci}
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_civoid test_kernel_gup_write_to_write_disabled_region(int *ptr, u16 pkey)
113962306a36Sopenharmony_ci{
114062306a36Sopenharmony_ci	int ignored = 0xdada;
114162306a36Sopenharmony_ci	int futex_ret;
114262306a36Sopenharmony_ci	int some_int = __LINE__;
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	dprintf1("disabling write to PKEY[%02d], "
114562306a36Sopenharmony_ci		 "doing futex gunk in buffer\n", pkey);
114662306a36Sopenharmony_ci	*ptr = some_int;
114762306a36Sopenharmony_ci	pkey_write_deny(pkey);
114862306a36Sopenharmony_ci	futex_ret = syscall(SYS_futex, ptr, FUTEX_WAIT, some_int-1, NULL,
114962306a36Sopenharmony_ci			&ignored, ignored);
115062306a36Sopenharmony_ci	if (DEBUG_LEVEL > 0)
115162306a36Sopenharmony_ci		perror("futex");
115262306a36Sopenharmony_ci	dprintf1("futex() ret: %d\n", futex_ret);
115362306a36Sopenharmony_ci}
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci/* Assumes that all pkeys other than 'pkey' are unallocated */
115662306a36Sopenharmony_civoid test_pkey_syscalls_on_non_allocated_pkey(int *ptr, u16 pkey)
115762306a36Sopenharmony_ci{
115862306a36Sopenharmony_ci	int err;
115962306a36Sopenharmony_ci	int i;
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci	/* Note: 0 is the default pkey, so don't mess with it */
116262306a36Sopenharmony_ci	for (i = 1; i < NR_PKEYS; i++) {
116362306a36Sopenharmony_ci		if (pkey == i)
116462306a36Sopenharmony_ci			continue;
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci		dprintf1("trying get/set/free to non-allocated pkey: %2d\n", i);
116762306a36Sopenharmony_ci		err = sys_pkey_free(i);
116862306a36Sopenharmony_ci		pkey_assert(err);
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci		err = sys_pkey_free(i);
117162306a36Sopenharmony_ci		pkey_assert(err);
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci		err = sys_mprotect_pkey(ptr, PAGE_SIZE, PROT_READ, i);
117462306a36Sopenharmony_ci		pkey_assert(err);
117562306a36Sopenharmony_ci	}
117662306a36Sopenharmony_ci}
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci/* Assumes that all pkeys other than 'pkey' are unallocated */
117962306a36Sopenharmony_civoid test_pkey_syscalls_bad_args(int *ptr, u16 pkey)
118062306a36Sopenharmony_ci{
118162306a36Sopenharmony_ci	int err;
118262306a36Sopenharmony_ci	int bad_pkey = NR_PKEYS+99;
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	/* pass a known-invalid pkey in: */
118562306a36Sopenharmony_ci	err = sys_mprotect_pkey(ptr, PAGE_SIZE, PROT_READ, bad_pkey);
118662306a36Sopenharmony_ci	pkey_assert(err);
118762306a36Sopenharmony_ci}
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_civoid become_child(void)
119062306a36Sopenharmony_ci{
119162306a36Sopenharmony_ci	pid_t forkret;
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	forkret = fork();
119462306a36Sopenharmony_ci	pkey_assert(forkret >= 0);
119562306a36Sopenharmony_ci	dprintf3("[%d] fork() ret: %d\n", getpid(), forkret);
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci	if (!forkret) {
119862306a36Sopenharmony_ci		/* in the child */
119962306a36Sopenharmony_ci		return;
120062306a36Sopenharmony_ci	}
120162306a36Sopenharmony_ci	exit(0);
120262306a36Sopenharmony_ci}
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci/* Assumes that all pkeys other than 'pkey' are unallocated */
120562306a36Sopenharmony_civoid test_pkey_alloc_exhaust(int *ptr, u16 pkey)
120662306a36Sopenharmony_ci{
120762306a36Sopenharmony_ci	int err;
120862306a36Sopenharmony_ci	int allocated_pkeys[NR_PKEYS] = {0};
120962306a36Sopenharmony_ci	int nr_allocated_pkeys = 0;
121062306a36Sopenharmony_ci	int i;
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	for (i = 0; i < NR_PKEYS*3; i++) {
121362306a36Sopenharmony_ci		int new_pkey;
121462306a36Sopenharmony_ci		dprintf1("%s() alloc loop: %d\n", __func__, i);
121562306a36Sopenharmony_ci		new_pkey = alloc_pkey();
121662306a36Sopenharmony_ci		dprintf4("%s()::%d, err: %d pkey_reg: 0x%016llx"
121762306a36Sopenharmony_ci				" shadow: 0x%016llx\n",
121862306a36Sopenharmony_ci				__func__, __LINE__, err, __read_pkey_reg(),
121962306a36Sopenharmony_ci				shadow_pkey_reg);
122062306a36Sopenharmony_ci		read_pkey_reg(); /* for shadow checking */
122162306a36Sopenharmony_ci		dprintf2("%s() errno: %d ENOSPC: %d\n", __func__, errno, ENOSPC);
122262306a36Sopenharmony_ci		if ((new_pkey == -1) && (errno == ENOSPC)) {
122362306a36Sopenharmony_ci			dprintf2("%s() failed to allocate pkey after %d tries\n",
122462306a36Sopenharmony_ci				__func__, nr_allocated_pkeys);
122562306a36Sopenharmony_ci		} else {
122662306a36Sopenharmony_ci			/*
122762306a36Sopenharmony_ci			 * Ensure the number of successes never
122862306a36Sopenharmony_ci			 * exceeds the number of keys supported
122962306a36Sopenharmony_ci			 * in the hardware.
123062306a36Sopenharmony_ci			 */
123162306a36Sopenharmony_ci			pkey_assert(nr_allocated_pkeys < NR_PKEYS);
123262306a36Sopenharmony_ci			allocated_pkeys[nr_allocated_pkeys++] = new_pkey;
123362306a36Sopenharmony_ci		}
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci		/*
123662306a36Sopenharmony_ci		 * Make sure that allocation state is properly
123762306a36Sopenharmony_ci		 * preserved across fork().
123862306a36Sopenharmony_ci		 */
123962306a36Sopenharmony_ci		if (i == NR_PKEYS*2)
124062306a36Sopenharmony_ci			become_child();
124162306a36Sopenharmony_ci	}
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	dprintf3("%s()::%d\n", __func__, __LINE__);
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci	/*
124662306a36Sopenharmony_ci	 * On x86:
124762306a36Sopenharmony_ci	 * There are 16 pkeys supported in hardware.  Three are
124862306a36Sopenharmony_ci	 * allocated by the time we get here:
124962306a36Sopenharmony_ci	 *   1. The default key (0)
125062306a36Sopenharmony_ci	 *   2. One possibly consumed by an execute-only mapping.
125162306a36Sopenharmony_ci	 *   3. One allocated by the test code and passed in via
125262306a36Sopenharmony_ci	 *      'pkey' to this function.
125362306a36Sopenharmony_ci	 * Ensure that we can allocate at least another 13 (16-3).
125462306a36Sopenharmony_ci	 *
125562306a36Sopenharmony_ci	 * On powerpc:
125662306a36Sopenharmony_ci	 * There are either 5, 28, 29 or 32 pkeys supported in
125762306a36Sopenharmony_ci	 * hardware depending on the page size (4K or 64K) and
125862306a36Sopenharmony_ci	 * platform (powernv or powervm). Four are allocated by
125962306a36Sopenharmony_ci	 * the time we get here. These include pkey-0, pkey-1,
126062306a36Sopenharmony_ci	 * exec-only pkey and the one allocated by the test code.
126162306a36Sopenharmony_ci	 * Ensure that we can allocate the remaining.
126262306a36Sopenharmony_ci	 */
126362306a36Sopenharmony_ci	pkey_assert(i >= (NR_PKEYS - get_arch_reserved_keys() - 1));
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ci	for (i = 0; i < nr_allocated_pkeys; i++) {
126662306a36Sopenharmony_ci		err = sys_pkey_free(allocated_pkeys[i]);
126762306a36Sopenharmony_ci		pkey_assert(!err);
126862306a36Sopenharmony_ci		read_pkey_reg(); /* for shadow checking */
126962306a36Sopenharmony_ci	}
127062306a36Sopenharmony_ci}
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_civoid arch_force_pkey_reg_init(void)
127362306a36Sopenharmony_ci{
127462306a36Sopenharmony_ci#if defined(__i386__) || defined(__x86_64__) /* arch */
127562306a36Sopenharmony_ci	u64 *buf;
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_ci	/*
127862306a36Sopenharmony_ci	 * All keys should be allocated and set to allow reads and
127962306a36Sopenharmony_ci	 * writes, so the register should be all 0.  If not, just
128062306a36Sopenharmony_ci	 * skip the test.
128162306a36Sopenharmony_ci	 */
128262306a36Sopenharmony_ci	if (read_pkey_reg())
128362306a36Sopenharmony_ci		return;
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci	/*
128662306a36Sopenharmony_ci	 * Just allocate an absurd about of memory rather than
128762306a36Sopenharmony_ci	 * doing the XSAVE size enumeration dance.
128862306a36Sopenharmony_ci	 */
128962306a36Sopenharmony_ci	buf = mmap(NULL, 1*MB, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci	/* These __builtins require compiling with -mxsave */
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci	/* XSAVE to build a valid buffer: */
129462306a36Sopenharmony_ci	__builtin_ia32_xsave(buf, XSTATE_PKEY);
129562306a36Sopenharmony_ci	/* Clear XSTATE_BV[PKRU]: */
129662306a36Sopenharmony_ci	buf[XSTATE_BV_OFFSET/sizeof(u64)] &= ~XSTATE_PKEY;
129762306a36Sopenharmony_ci	/* XRSTOR will likely get PKRU back to the init state: */
129862306a36Sopenharmony_ci	__builtin_ia32_xrstor(buf, XSTATE_PKEY);
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci	munmap(buf, 1*MB);
130162306a36Sopenharmony_ci#endif
130262306a36Sopenharmony_ci}
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci/*
130662306a36Sopenharmony_ci * This is mostly useless on ppc for now.  But it will not
130762306a36Sopenharmony_ci * hurt anything and should give some better coverage as
130862306a36Sopenharmony_ci * a long-running test that continually checks the pkey
130962306a36Sopenharmony_ci * register.
131062306a36Sopenharmony_ci */
131162306a36Sopenharmony_civoid test_pkey_init_state(int *ptr, u16 pkey)
131262306a36Sopenharmony_ci{
131362306a36Sopenharmony_ci	int err;
131462306a36Sopenharmony_ci	int allocated_pkeys[NR_PKEYS] = {0};
131562306a36Sopenharmony_ci	int nr_allocated_pkeys = 0;
131662306a36Sopenharmony_ci	int i;
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci	for (i = 0; i < NR_PKEYS; i++) {
131962306a36Sopenharmony_ci		int new_pkey = alloc_pkey();
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci		if (new_pkey < 0)
132262306a36Sopenharmony_ci			continue;
132362306a36Sopenharmony_ci		allocated_pkeys[nr_allocated_pkeys++] = new_pkey;
132462306a36Sopenharmony_ci	}
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	dprintf3("%s()::%d\n", __func__, __LINE__);
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci	arch_force_pkey_reg_init();
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci	/*
133162306a36Sopenharmony_ci	 * Loop for a bit, hoping to get exercise the kernel
133262306a36Sopenharmony_ci	 * context switch code.
133362306a36Sopenharmony_ci	 */
133462306a36Sopenharmony_ci	for (i = 0; i < 1000000; i++)
133562306a36Sopenharmony_ci		read_pkey_reg();
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci	for (i = 0; i < nr_allocated_pkeys; i++) {
133862306a36Sopenharmony_ci		err = sys_pkey_free(allocated_pkeys[i]);
133962306a36Sopenharmony_ci		pkey_assert(!err);
134062306a36Sopenharmony_ci		read_pkey_reg(); /* for shadow checking */
134162306a36Sopenharmony_ci	}
134262306a36Sopenharmony_ci}
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci/*
134562306a36Sopenharmony_ci * pkey 0 is special.  It is allocated by default, so you do not
134662306a36Sopenharmony_ci * have to call pkey_alloc() to use it first.  Make sure that it
134762306a36Sopenharmony_ci * is usable.
134862306a36Sopenharmony_ci */
134962306a36Sopenharmony_civoid test_mprotect_with_pkey_0(int *ptr, u16 pkey)
135062306a36Sopenharmony_ci{
135162306a36Sopenharmony_ci	long size;
135262306a36Sopenharmony_ci	int prot;
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci	assert(pkey_last_malloc_record);
135562306a36Sopenharmony_ci	size = pkey_last_malloc_record->size;
135662306a36Sopenharmony_ci	/*
135762306a36Sopenharmony_ci	 * This is a bit of a hack.  But mprotect() requires
135862306a36Sopenharmony_ci	 * huge-page-aligned sizes when operating on hugetlbfs.
135962306a36Sopenharmony_ci	 * So, make sure that we use something that's a multiple
136062306a36Sopenharmony_ci	 * of a huge page when we can.
136162306a36Sopenharmony_ci	 */
136262306a36Sopenharmony_ci	if (size >= HPAGE_SIZE)
136362306a36Sopenharmony_ci		size = HPAGE_SIZE;
136462306a36Sopenharmony_ci	prot = pkey_last_malloc_record->prot;
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	/* Use pkey 0 */
136762306a36Sopenharmony_ci	mprotect_pkey(ptr, size, prot, 0);
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci	/* Make sure that we can set it back to the original pkey. */
137062306a36Sopenharmony_ci	mprotect_pkey(ptr, size, prot, pkey);
137162306a36Sopenharmony_ci}
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_civoid test_ptrace_of_child(int *ptr, u16 pkey)
137462306a36Sopenharmony_ci{
137562306a36Sopenharmony_ci	__attribute__((__unused__)) int peek_result;
137662306a36Sopenharmony_ci	pid_t child_pid;
137762306a36Sopenharmony_ci	void *ignored = 0;
137862306a36Sopenharmony_ci	long ret;
137962306a36Sopenharmony_ci	int status;
138062306a36Sopenharmony_ci	/*
138162306a36Sopenharmony_ci	 * This is the "control" for our little expermient.  Make sure
138262306a36Sopenharmony_ci	 * we can always access it when ptracing.
138362306a36Sopenharmony_ci	 */
138462306a36Sopenharmony_ci	int *plain_ptr_unaligned = malloc(HPAGE_SIZE);
138562306a36Sopenharmony_ci	int *plain_ptr = ALIGN_PTR_UP(plain_ptr_unaligned, PAGE_SIZE);
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci	/*
138862306a36Sopenharmony_ci	 * Fork a child which is an exact copy of this process, of course.
138962306a36Sopenharmony_ci	 * That means we can do all of our tests via ptrace() and then plain
139062306a36Sopenharmony_ci	 * memory access and ensure they work differently.
139162306a36Sopenharmony_ci	 */
139262306a36Sopenharmony_ci	child_pid = fork_lazy_child();
139362306a36Sopenharmony_ci	dprintf1("[%d] child pid: %d\n", getpid(), child_pid);
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci	ret = ptrace(PTRACE_ATTACH, child_pid, ignored, ignored);
139662306a36Sopenharmony_ci	if (ret)
139762306a36Sopenharmony_ci		perror("attach");
139862306a36Sopenharmony_ci	dprintf1("[%d] attach ret: %ld %d\n", getpid(), ret, __LINE__);
139962306a36Sopenharmony_ci	pkey_assert(ret != -1);
140062306a36Sopenharmony_ci	ret = waitpid(child_pid, &status, WUNTRACED);
140162306a36Sopenharmony_ci	if ((ret != child_pid) || !(WIFSTOPPED(status))) {
140262306a36Sopenharmony_ci		fprintf(stderr, "weird waitpid result %ld stat %x\n",
140362306a36Sopenharmony_ci				ret, status);
140462306a36Sopenharmony_ci		pkey_assert(0);
140562306a36Sopenharmony_ci	}
140662306a36Sopenharmony_ci	dprintf2("waitpid ret: %ld\n", ret);
140762306a36Sopenharmony_ci	dprintf2("waitpid status: %d\n", status);
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci	pkey_access_deny(pkey);
141062306a36Sopenharmony_ci	pkey_write_deny(pkey);
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci	/* Write access, untested for now:
141362306a36Sopenharmony_ci	ret = ptrace(PTRACE_POKEDATA, child_pid, peek_at, data);
141462306a36Sopenharmony_ci	pkey_assert(ret != -1);
141562306a36Sopenharmony_ci	dprintf1("poke at %p: %ld\n", peek_at, ret);
141662306a36Sopenharmony_ci	*/
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_ci	/*
141962306a36Sopenharmony_ci	 * Try to access the pkey-protected "ptr" via ptrace:
142062306a36Sopenharmony_ci	 */
142162306a36Sopenharmony_ci	ret = ptrace(PTRACE_PEEKDATA, child_pid, ptr, ignored);
142262306a36Sopenharmony_ci	/* expect it to work, without an error: */
142362306a36Sopenharmony_ci	pkey_assert(ret != -1);
142462306a36Sopenharmony_ci	/* Now access from the current task, and expect an exception: */
142562306a36Sopenharmony_ci	peek_result = read_ptr(ptr);
142662306a36Sopenharmony_ci	expected_pkey_fault(pkey);
142762306a36Sopenharmony_ci
142862306a36Sopenharmony_ci	/*
142962306a36Sopenharmony_ci	 * Try to access the NON-pkey-protected "plain_ptr" via ptrace:
143062306a36Sopenharmony_ci	 */
143162306a36Sopenharmony_ci	ret = ptrace(PTRACE_PEEKDATA, child_pid, plain_ptr, ignored);
143262306a36Sopenharmony_ci	/* expect it to work, without an error: */
143362306a36Sopenharmony_ci	pkey_assert(ret != -1);
143462306a36Sopenharmony_ci	/* Now access from the current task, and expect NO exception: */
143562306a36Sopenharmony_ci	peek_result = read_ptr(plain_ptr);
143662306a36Sopenharmony_ci	do_not_expect_pkey_fault("read plain pointer after ptrace");
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	ret = ptrace(PTRACE_DETACH, child_pid, ignored, 0);
143962306a36Sopenharmony_ci	pkey_assert(ret != -1);
144062306a36Sopenharmony_ci
144162306a36Sopenharmony_ci	ret = kill(child_pid, SIGKILL);
144262306a36Sopenharmony_ci	pkey_assert(ret != -1);
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_ci	wait(&status);
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_ci	free(plain_ptr_unaligned);
144762306a36Sopenharmony_ci}
144862306a36Sopenharmony_ci
144962306a36Sopenharmony_civoid *get_pointer_to_instructions(void)
145062306a36Sopenharmony_ci{
145162306a36Sopenharmony_ci	void *p1;
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_ci	p1 = ALIGN_PTR_UP(&lots_o_noops_around_write, PAGE_SIZE);
145462306a36Sopenharmony_ci	dprintf3("&lots_o_noops: %p\n", &lots_o_noops_around_write);
145562306a36Sopenharmony_ci	/* lots_o_noops_around_write should be page-aligned already */
145662306a36Sopenharmony_ci	assert(p1 == &lots_o_noops_around_write);
145762306a36Sopenharmony_ci
145862306a36Sopenharmony_ci	/* Point 'p1' at the *second* page of the function: */
145962306a36Sopenharmony_ci	p1 += PAGE_SIZE;
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_ci	/*
146262306a36Sopenharmony_ci	 * Try to ensure we fault this in on next touch to ensure
146362306a36Sopenharmony_ci	 * we get an instruction fault as opposed to a data one
146462306a36Sopenharmony_ci	 */
146562306a36Sopenharmony_ci	madvise(p1, PAGE_SIZE, MADV_DONTNEED);
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci	return p1;
146862306a36Sopenharmony_ci}
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_civoid test_executing_on_unreadable_memory(int *ptr, u16 pkey)
147162306a36Sopenharmony_ci{
147262306a36Sopenharmony_ci	void *p1;
147362306a36Sopenharmony_ci	int scratch;
147462306a36Sopenharmony_ci	int ptr_contents;
147562306a36Sopenharmony_ci	int ret;
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_ci	p1 = get_pointer_to_instructions();
147862306a36Sopenharmony_ci	lots_o_noops_around_write(&scratch);
147962306a36Sopenharmony_ci	ptr_contents = read_ptr(p1);
148062306a36Sopenharmony_ci	dprintf2("ptr (%p) contents@%d: %x\n", p1, __LINE__, ptr_contents);
148162306a36Sopenharmony_ci
148262306a36Sopenharmony_ci	ret = mprotect_pkey(p1, PAGE_SIZE, PROT_EXEC, (u64)pkey);
148362306a36Sopenharmony_ci	pkey_assert(!ret);
148462306a36Sopenharmony_ci	pkey_access_deny(pkey);
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_ci	dprintf2("pkey_reg: %016llx\n", read_pkey_reg());
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_ci	/*
148962306a36Sopenharmony_ci	 * Make sure this is an *instruction* fault
149062306a36Sopenharmony_ci	 */
149162306a36Sopenharmony_ci	madvise(p1, PAGE_SIZE, MADV_DONTNEED);
149262306a36Sopenharmony_ci	lots_o_noops_around_write(&scratch);
149362306a36Sopenharmony_ci	do_not_expect_pkey_fault("executing on PROT_EXEC memory");
149462306a36Sopenharmony_ci	expect_fault_on_read_execonly_key(p1, pkey);
149562306a36Sopenharmony_ci}
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_civoid test_implicit_mprotect_exec_only_memory(int *ptr, u16 pkey)
149862306a36Sopenharmony_ci{
149962306a36Sopenharmony_ci	void *p1;
150062306a36Sopenharmony_ci	int scratch;
150162306a36Sopenharmony_ci	int ptr_contents;
150262306a36Sopenharmony_ci	int ret;
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_ci	dprintf1("%s() start\n", __func__);
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_ci	p1 = get_pointer_to_instructions();
150762306a36Sopenharmony_ci	lots_o_noops_around_write(&scratch);
150862306a36Sopenharmony_ci	ptr_contents = read_ptr(p1);
150962306a36Sopenharmony_ci	dprintf2("ptr (%p) contents@%d: %x\n", p1, __LINE__, ptr_contents);
151062306a36Sopenharmony_ci
151162306a36Sopenharmony_ci	/* Use a *normal* mprotect(), not mprotect_pkey(): */
151262306a36Sopenharmony_ci	ret = mprotect(p1, PAGE_SIZE, PROT_EXEC);
151362306a36Sopenharmony_ci	pkey_assert(!ret);
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_ci	/*
151662306a36Sopenharmony_ci	 * Reset the shadow, assuming that the above mprotect()
151762306a36Sopenharmony_ci	 * correctly changed PKRU, but to an unknown value since
151862306a36Sopenharmony_ci	 * the actual allocated pkey is unknown.
151962306a36Sopenharmony_ci	 */
152062306a36Sopenharmony_ci	shadow_pkey_reg = __read_pkey_reg();
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_ci	dprintf2("pkey_reg: %016llx\n", read_pkey_reg());
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_ci	/* Make sure this is an *instruction* fault */
152562306a36Sopenharmony_ci	madvise(p1, PAGE_SIZE, MADV_DONTNEED);
152662306a36Sopenharmony_ci	lots_o_noops_around_write(&scratch);
152762306a36Sopenharmony_ci	do_not_expect_pkey_fault("executing on PROT_EXEC memory");
152862306a36Sopenharmony_ci	expect_fault_on_read_execonly_key(p1, UNKNOWN_PKEY);
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_ci	/*
153162306a36Sopenharmony_ci	 * Put the memory back to non-PROT_EXEC.  Should clear the
153262306a36Sopenharmony_ci	 * exec-only pkey off the VMA and allow it to be readable
153362306a36Sopenharmony_ci	 * again.  Go to PROT_NONE first to check for a kernel bug
153462306a36Sopenharmony_ci	 * that did not clear the pkey when doing PROT_NONE.
153562306a36Sopenharmony_ci	 */
153662306a36Sopenharmony_ci	ret = mprotect(p1, PAGE_SIZE, PROT_NONE);
153762306a36Sopenharmony_ci	pkey_assert(!ret);
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_ci	ret = mprotect(p1, PAGE_SIZE, PROT_READ|PROT_EXEC);
154062306a36Sopenharmony_ci	pkey_assert(!ret);
154162306a36Sopenharmony_ci	ptr_contents = read_ptr(p1);
154262306a36Sopenharmony_ci	do_not_expect_pkey_fault("plain read on recently PROT_EXEC area");
154362306a36Sopenharmony_ci}
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_ci#if defined(__i386__) || defined(__x86_64__)
154662306a36Sopenharmony_civoid test_ptrace_modifies_pkru(int *ptr, u16 pkey)
154762306a36Sopenharmony_ci{
154862306a36Sopenharmony_ci	u32 new_pkru;
154962306a36Sopenharmony_ci	pid_t child;
155062306a36Sopenharmony_ci	int status, ret;
155162306a36Sopenharmony_ci	int pkey_offset = pkey_reg_xstate_offset();
155262306a36Sopenharmony_ci	size_t xsave_size = cpu_max_xsave_size();
155362306a36Sopenharmony_ci	void *xsave;
155462306a36Sopenharmony_ci	u32 *pkey_register;
155562306a36Sopenharmony_ci	u64 *xstate_bv;
155662306a36Sopenharmony_ci	struct iovec iov;
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_ci	new_pkru = ~read_pkey_reg();
155962306a36Sopenharmony_ci	/* Don't make PROT_EXEC mappings inaccessible */
156062306a36Sopenharmony_ci	new_pkru &= ~3;
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci	child = fork();
156362306a36Sopenharmony_ci	pkey_assert(child >= 0);
156462306a36Sopenharmony_ci	dprintf3("[%d] fork() ret: %d\n", getpid(), child);
156562306a36Sopenharmony_ci	if (!child) {
156662306a36Sopenharmony_ci		ptrace(PTRACE_TRACEME, 0, 0, 0);
156762306a36Sopenharmony_ci		/* Stop and allow the tracer to modify PKRU directly */
156862306a36Sopenharmony_ci		raise(SIGSTOP);
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_ci		/*
157162306a36Sopenharmony_ci		 * need __read_pkey_reg() version so we do not do shadow_pkey_reg
157262306a36Sopenharmony_ci		 * checking
157362306a36Sopenharmony_ci		 */
157462306a36Sopenharmony_ci		if (__read_pkey_reg() != new_pkru)
157562306a36Sopenharmony_ci			exit(1);
157662306a36Sopenharmony_ci
157762306a36Sopenharmony_ci		/* Stop and allow the tracer to clear XSTATE_BV for PKRU */
157862306a36Sopenharmony_ci		raise(SIGSTOP);
157962306a36Sopenharmony_ci
158062306a36Sopenharmony_ci		if (__read_pkey_reg() != 0)
158162306a36Sopenharmony_ci			exit(1);
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_ci		/* Stop and allow the tracer to examine PKRU */
158462306a36Sopenharmony_ci		raise(SIGSTOP);
158562306a36Sopenharmony_ci
158662306a36Sopenharmony_ci		exit(0);
158762306a36Sopenharmony_ci	}
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci	pkey_assert(child == waitpid(child, &status, 0));
159062306a36Sopenharmony_ci	dprintf3("[%d] waitpid(%d) status: %x\n", getpid(), child, status);
159162306a36Sopenharmony_ci	pkey_assert(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP);
159262306a36Sopenharmony_ci
159362306a36Sopenharmony_ci	xsave = (void *)malloc(xsave_size);
159462306a36Sopenharmony_ci	pkey_assert(xsave > 0);
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_ci	/* Modify the PKRU register directly */
159762306a36Sopenharmony_ci	iov.iov_base = xsave;
159862306a36Sopenharmony_ci	iov.iov_len = xsave_size;
159962306a36Sopenharmony_ci	ret = ptrace(PTRACE_GETREGSET, child, (void *)NT_X86_XSTATE, &iov);
160062306a36Sopenharmony_ci	pkey_assert(ret == 0);
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_ci	pkey_register = (u32 *)(xsave + pkey_offset);
160362306a36Sopenharmony_ci	pkey_assert(*pkey_register == read_pkey_reg());
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_ci	*pkey_register = new_pkru;
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_ci	ret = ptrace(PTRACE_SETREGSET, child, (void *)NT_X86_XSTATE, &iov);
160862306a36Sopenharmony_ci	pkey_assert(ret == 0);
160962306a36Sopenharmony_ci
161062306a36Sopenharmony_ci	/* Test that the modification is visible in ptrace before any execution */
161162306a36Sopenharmony_ci	memset(xsave, 0xCC, xsave_size);
161262306a36Sopenharmony_ci	ret = ptrace(PTRACE_GETREGSET, child, (void *)NT_X86_XSTATE, &iov);
161362306a36Sopenharmony_ci	pkey_assert(ret == 0);
161462306a36Sopenharmony_ci	pkey_assert(*pkey_register == new_pkru);
161562306a36Sopenharmony_ci
161662306a36Sopenharmony_ci	/* Execute the tracee */
161762306a36Sopenharmony_ci	ret = ptrace(PTRACE_CONT, child, 0, 0);
161862306a36Sopenharmony_ci	pkey_assert(ret == 0);
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_ci	/* Test that the tracee saw the PKRU value change */
162162306a36Sopenharmony_ci	pkey_assert(child == waitpid(child, &status, 0));
162262306a36Sopenharmony_ci	dprintf3("[%d] waitpid(%d) status: %x\n", getpid(), child, status);
162362306a36Sopenharmony_ci	pkey_assert(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP);
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ci	/* Test that the modification is visible in ptrace after execution */
162662306a36Sopenharmony_ci	memset(xsave, 0xCC, xsave_size);
162762306a36Sopenharmony_ci	ret = ptrace(PTRACE_GETREGSET, child, (void *)NT_X86_XSTATE, &iov);
162862306a36Sopenharmony_ci	pkey_assert(ret == 0);
162962306a36Sopenharmony_ci	pkey_assert(*pkey_register == new_pkru);
163062306a36Sopenharmony_ci
163162306a36Sopenharmony_ci	/* Clear the PKRU bit from XSTATE_BV */
163262306a36Sopenharmony_ci	xstate_bv = (u64 *)(xsave + 512);
163362306a36Sopenharmony_ci	*xstate_bv &= ~(1 << 9);
163462306a36Sopenharmony_ci
163562306a36Sopenharmony_ci	ret = ptrace(PTRACE_SETREGSET, child, (void *)NT_X86_XSTATE, &iov);
163662306a36Sopenharmony_ci	pkey_assert(ret == 0);
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_ci	/* Test that the modification is visible in ptrace before any execution */
163962306a36Sopenharmony_ci	memset(xsave, 0xCC, xsave_size);
164062306a36Sopenharmony_ci	ret = ptrace(PTRACE_GETREGSET, child, (void *)NT_X86_XSTATE, &iov);
164162306a36Sopenharmony_ci	pkey_assert(ret == 0);
164262306a36Sopenharmony_ci	pkey_assert(*pkey_register == 0);
164362306a36Sopenharmony_ci
164462306a36Sopenharmony_ci	ret = ptrace(PTRACE_CONT, child, 0, 0);
164562306a36Sopenharmony_ci	pkey_assert(ret == 0);
164662306a36Sopenharmony_ci
164762306a36Sopenharmony_ci	/* Test that the tracee saw the PKRU value go to 0 */
164862306a36Sopenharmony_ci	pkey_assert(child == waitpid(child, &status, 0));
164962306a36Sopenharmony_ci	dprintf3("[%d] waitpid(%d) status: %x\n", getpid(), child, status);
165062306a36Sopenharmony_ci	pkey_assert(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP);
165162306a36Sopenharmony_ci
165262306a36Sopenharmony_ci	/* Test that the modification is visible in ptrace after execution */
165362306a36Sopenharmony_ci	memset(xsave, 0xCC, xsave_size);
165462306a36Sopenharmony_ci	ret = ptrace(PTRACE_GETREGSET, child, (void *)NT_X86_XSTATE, &iov);
165562306a36Sopenharmony_ci	pkey_assert(ret == 0);
165662306a36Sopenharmony_ci	pkey_assert(*pkey_register == 0);
165762306a36Sopenharmony_ci
165862306a36Sopenharmony_ci	ret = ptrace(PTRACE_CONT, child, 0, 0);
165962306a36Sopenharmony_ci	pkey_assert(ret == 0);
166062306a36Sopenharmony_ci	pkey_assert(child == waitpid(child, &status, 0));
166162306a36Sopenharmony_ci	dprintf3("[%d] waitpid(%d) status: %x\n", getpid(), child, status);
166262306a36Sopenharmony_ci	pkey_assert(WIFEXITED(status));
166362306a36Sopenharmony_ci	pkey_assert(WEXITSTATUS(status) == 0);
166462306a36Sopenharmony_ci	free(xsave);
166562306a36Sopenharmony_ci}
166662306a36Sopenharmony_ci#endif
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_civoid test_mprotect_pkey_on_unsupported_cpu(int *ptr, u16 pkey)
166962306a36Sopenharmony_ci{
167062306a36Sopenharmony_ci	int size = PAGE_SIZE;
167162306a36Sopenharmony_ci	int sret;
167262306a36Sopenharmony_ci
167362306a36Sopenharmony_ci	if (cpu_has_pkeys()) {
167462306a36Sopenharmony_ci		dprintf1("SKIP: %s: no CPU support\n", __func__);
167562306a36Sopenharmony_ci		return;
167662306a36Sopenharmony_ci	}
167762306a36Sopenharmony_ci
167862306a36Sopenharmony_ci	sret = syscall(__NR_pkey_mprotect, ptr, size, PROT_READ, pkey);
167962306a36Sopenharmony_ci	pkey_assert(sret < 0);
168062306a36Sopenharmony_ci}
168162306a36Sopenharmony_ci
168262306a36Sopenharmony_civoid (*pkey_tests[])(int *ptr, u16 pkey) = {
168362306a36Sopenharmony_ci	test_read_of_write_disabled_region,
168462306a36Sopenharmony_ci	test_read_of_access_disabled_region,
168562306a36Sopenharmony_ci	test_read_of_access_disabled_region_with_page_already_mapped,
168662306a36Sopenharmony_ci	test_write_of_write_disabled_region,
168762306a36Sopenharmony_ci	test_write_of_write_disabled_region_with_page_already_mapped,
168862306a36Sopenharmony_ci	test_write_of_access_disabled_region,
168962306a36Sopenharmony_ci	test_write_of_access_disabled_region_with_page_already_mapped,
169062306a36Sopenharmony_ci	test_kernel_write_of_access_disabled_region,
169162306a36Sopenharmony_ci	test_kernel_write_of_write_disabled_region,
169262306a36Sopenharmony_ci	test_kernel_gup_of_access_disabled_region,
169362306a36Sopenharmony_ci	test_kernel_gup_write_to_write_disabled_region,
169462306a36Sopenharmony_ci	test_executing_on_unreadable_memory,
169562306a36Sopenharmony_ci	test_implicit_mprotect_exec_only_memory,
169662306a36Sopenharmony_ci	test_mprotect_with_pkey_0,
169762306a36Sopenharmony_ci	test_ptrace_of_child,
169862306a36Sopenharmony_ci	test_pkey_init_state,
169962306a36Sopenharmony_ci	test_pkey_syscalls_on_non_allocated_pkey,
170062306a36Sopenharmony_ci	test_pkey_syscalls_bad_args,
170162306a36Sopenharmony_ci	test_pkey_alloc_exhaust,
170262306a36Sopenharmony_ci	test_pkey_alloc_free_attach_pkey0,
170362306a36Sopenharmony_ci#if defined(__i386__) || defined(__x86_64__)
170462306a36Sopenharmony_ci	test_ptrace_modifies_pkru,
170562306a36Sopenharmony_ci#endif
170662306a36Sopenharmony_ci};
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_civoid run_tests_once(void)
170962306a36Sopenharmony_ci{
171062306a36Sopenharmony_ci	int *ptr;
171162306a36Sopenharmony_ci	int prot = PROT_READ|PROT_WRITE;
171262306a36Sopenharmony_ci
171362306a36Sopenharmony_ci	for (test_nr = 0; test_nr < ARRAY_SIZE(pkey_tests); test_nr++) {
171462306a36Sopenharmony_ci		int pkey;
171562306a36Sopenharmony_ci		int orig_pkey_faults = pkey_faults;
171662306a36Sopenharmony_ci
171762306a36Sopenharmony_ci		dprintf1("======================\n");
171862306a36Sopenharmony_ci		dprintf1("test %d preparing...\n", test_nr);
171962306a36Sopenharmony_ci
172062306a36Sopenharmony_ci		tracing_on();
172162306a36Sopenharmony_ci		pkey = alloc_random_pkey();
172262306a36Sopenharmony_ci		dprintf1("test %d starting with pkey: %d\n", test_nr, pkey);
172362306a36Sopenharmony_ci		ptr = malloc_pkey(PAGE_SIZE, prot, pkey);
172462306a36Sopenharmony_ci		dprintf1("test %d starting...\n", test_nr);
172562306a36Sopenharmony_ci		pkey_tests[test_nr](ptr, pkey);
172662306a36Sopenharmony_ci		dprintf1("freeing test memory: %p\n", ptr);
172762306a36Sopenharmony_ci		free_pkey_malloc(ptr);
172862306a36Sopenharmony_ci		sys_pkey_free(pkey);
172962306a36Sopenharmony_ci
173062306a36Sopenharmony_ci		dprintf1("pkey_faults: %d\n", pkey_faults);
173162306a36Sopenharmony_ci		dprintf1("orig_pkey_faults: %d\n", orig_pkey_faults);
173262306a36Sopenharmony_ci
173362306a36Sopenharmony_ci		tracing_off();
173462306a36Sopenharmony_ci		close_test_fds();
173562306a36Sopenharmony_ci
173662306a36Sopenharmony_ci		printf("test %2d PASSED (iteration %d)\n", test_nr, iteration_nr);
173762306a36Sopenharmony_ci		dprintf1("======================\n\n");
173862306a36Sopenharmony_ci	}
173962306a36Sopenharmony_ci	iteration_nr++;
174062306a36Sopenharmony_ci}
174162306a36Sopenharmony_ci
174262306a36Sopenharmony_civoid pkey_setup_shadow(void)
174362306a36Sopenharmony_ci{
174462306a36Sopenharmony_ci	shadow_pkey_reg = __read_pkey_reg();
174562306a36Sopenharmony_ci}
174662306a36Sopenharmony_ci
174762306a36Sopenharmony_ciint main(void)
174862306a36Sopenharmony_ci{
174962306a36Sopenharmony_ci	int nr_iterations = 22;
175062306a36Sopenharmony_ci	int pkeys_supported = is_pkeys_supported();
175162306a36Sopenharmony_ci
175262306a36Sopenharmony_ci	srand((unsigned int)time(NULL));
175362306a36Sopenharmony_ci
175462306a36Sopenharmony_ci	setup_handlers();
175562306a36Sopenharmony_ci
175662306a36Sopenharmony_ci	printf("has pkeys: %d\n", pkeys_supported);
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_ci	if (!pkeys_supported) {
175962306a36Sopenharmony_ci		int size = PAGE_SIZE;
176062306a36Sopenharmony_ci		int *ptr;
176162306a36Sopenharmony_ci
176262306a36Sopenharmony_ci		printf("running PKEY tests for unsupported CPU/OS\n");
176362306a36Sopenharmony_ci
176462306a36Sopenharmony_ci		ptr  = mmap(NULL, size, PROT_NONE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
176562306a36Sopenharmony_ci		assert(ptr != (void *)-1);
176662306a36Sopenharmony_ci		test_mprotect_pkey_on_unsupported_cpu(ptr, 1);
176762306a36Sopenharmony_ci		exit(0);
176862306a36Sopenharmony_ci	}
176962306a36Sopenharmony_ci
177062306a36Sopenharmony_ci	pkey_setup_shadow();
177162306a36Sopenharmony_ci	printf("startup pkey_reg: %016llx\n", read_pkey_reg());
177262306a36Sopenharmony_ci	setup_hugetlbfs();
177362306a36Sopenharmony_ci
177462306a36Sopenharmony_ci	while (nr_iterations-- > 0)
177562306a36Sopenharmony_ci		run_tests_once();
177662306a36Sopenharmony_ci
177762306a36Sopenharmony_ci	printf("done (all tests OK)\n");
177862306a36Sopenharmony_ci	return 0;
177962306a36Sopenharmony_ci}
1780