brintos

brintos / linux-shallow public Read only

0
0
Text · 8.7 KiB · d894743 Raw
331 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Ptrace test for Memory Protection Key registers4 *5 * Copyright (C) 2015 Anshuman Khandual, IBM Corporation.6 * Copyright (C) 2018 IBM Corporation.7 */8#include "ptrace.h"9#include "child.h"10 11#ifndef __NR_pkey_alloc12#define __NR_pkey_alloc		38413#endif14 15#ifndef __NR_pkey_free16#define __NR_pkey_free		38517#endif18 19#ifndef NT_PPC_PKEY20#define NT_PPC_PKEY		0x11021#endif22 23#ifndef PKEY_DISABLE_EXECUTE24#define PKEY_DISABLE_EXECUTE	0x425#endif26 27#define AMR_BITS_PER_PKEY 228#define PKEY_REG_BITS (sizeof(u64) * 8)29#define pkeyshift(pkey) (PKEY_REG_BITS - ((pkey + 1) * AMR_BITS_PER_PKEY))30 31static const char user_read[] = "[User Read (Running)]";32static const char user_write[] = "[User Write (Running)]";33static const char ptrace_read_running[] = "[Ptrace Read (Running)]";34static const char ptrace_write_running[] = "[Ptrace Write (Running)]";35 36/* Information shared between the parent and the child. */37struct shared_info {38	struct child_sync child_sync;39 40	/* AMR value the parent expects to read from the child. */41	unsigned long amr1;42 43	/* AMR value the parent is expected to write to the child. */44	unsigned long amr2;45 46	/* AMR value that ptrace should refuse to write to the child. */47	unsigned long invalid_amr;48 49	/* IAMR value the parent expects to read from the child. */50	unsigned long expected_iamr;51 52	/* UAMOR value the parent expects to read from the child. */53	unsigned long expected_uamor;54 55	/*56	 * IAMR and UAMOR values that ptrace should refuse to write to the child57	 * (even though they're valid ones) because userspace doesn't have58	 * access to those registers.59	 */60	unsigned long invalid_iamr;61	unsigned long invalid_uamor;62};63 64static int sys_pkey_alloc(unsigned long flags, unsigned long init_access_rights)65{66	return syscall(__NR_pkey_alloc, flags, init_access_rights);67}68 69static int child(struct shared_info *info)70{71	unsigned long reg;72	bool disable_execute = true;73	int pkey1, pkey2, pkey3;74	int ret;75 76	/* Wait until parent fills out the initial register values. */77	ret = wait_parent(&info->child_sync);78	if (ret)79		return ret;80 81	/* Get some pkeys so that we can change their bits in the AMR. */82	pkey1 = sys_pkey_alloc(0, PKEY_DISABLE_EXECUTE);83	if (pkey1 < 0) {84		pkey1 = sys_pkey_alloc(0, 0);85		CHILD_FAIL_IF(pkey1 < 0, &info->child_sync);86 87		disable_execute = false;88	}89 90	pkey2 = sys_pkey_alloc(0, 0);91	CHILD_FAIL_IF(pkey2 < 0, &info->child_sync);92 93	pkey3 = sys_pkey_alloc(0, 0);94	CHILD_FAIL_IF(pkey3 < 0, &info->child_sync);95 96	info->amr1 |= 3ul << pkeyshift(pkey1);97	info->amr2 |= 3ul << pkeyshift(pkey2);98	/*99	 * invalid amr value where we try to force write100	 * things which are deined by a uamor setting.101	 */102	info->invalid_amr = info->amr2 | (~0x0UL & ~info->expected_uamor);103 104	/*105	 * if PKEY_DISABLE_EXECUTE succeeded we should update the expected_iamr106	 */107	if (disable_execute)108		info->expected_iamr |= 1ul << pkeyshift(pkey1);109	else110		info->expected_iamr &= ~(1ul << pkeyshift(pkey1));111 112	/*113	 * We allocated pkey2 and pkey 3 above. Clear the IAMR bits.114	 */115	info->expected_iamr &= ~(1ul << pkeyshift(pkey2));116	info->expected_iamr &= ~(1ul << pkeyshift(pkey3));117 118	/*119	 * Create an IAMR value different from expected value.120	 * Kernel will reject an IAMR and UAMOR change.121	 */122	info->invalid_iamr = info->expected_iamr | (1ul << pkeyshift(pkey1) | 1ul << pkeyshift(pkey2));123	info->invalid_uamor = info->expected_uamor & ~(0x3ul << pkeyshift(pkey1));124 125	printf("%-30s AMR: %016lx pkey1: %d pkey2: %d pkey3: %d\n",126	       user_write, info->amr1, pkey1, pkey2, pkey3);127 128	set_amr(info->amr1);129 130	/* Wait for parent to read our AMR value and write a new one. */131	ret = prod_parent(&info->child_sync);132	CHILD_FAIL_IF(ret, &info->child_sync);133 134	ret = wait_parent(&info->child_sync);135	if (ret)136		return ret;137 138	reg = mfspr(SPRN_AMR);139 140	printf("%-30s AMR: %016lx\n", user_read, reg);141 142	CHILD_FAIL_IF(reg != info->amr2, &info->child_sync);143 144	/*145	 * Wait for parent to try to write an invalid AMR value.146	 */147	ret = prod_parent(&info->child_sync);148	CHILD_FAIL_IF(ret, &info->child_sync);149 150	ret = wait_parent(&info->child_sync);151	if (ret)152		return ret;153 154	reg = mfspr(SPRN_AMR);155 156	printf("%-30s AMR: %016lx\n", user_read, reg);157 158	CHILD_FAIL_IF(reg != info->amr2, &info->child_sync);159 160	/*161	 * Wait for parent to try to write an IAMR and a UAMOR value. We can't162	 * verify them, but we can verify that the AMR didn't change.163	 */164	ret = prod_parent(&info->child_sync);165	CHILD_FAIL_IF(ret, &info->child_sync);166 167	ret = wait_parent(&info->child_sync);168	if (ret)169		return ret;170 171	reg = mfspr(SPRN_AMR);172 173	printf("%-30s AMR: %016lx\n", user_read, reg);174 175	CHILD_FAIL_IF(reg != info->amr2, &info->child_sync);176 177	/* Now let parent now that we are finished. */178 179	ret = prod_parent(&info->child_sync);180	CHILD_FAIL_IF(ret, &info->child_sync);181 182	return TEST_PASS;183}184 185static int parent(struct shared_info *info, pid_t pid)186{187	unsigned long regs[3];188	int ret, status;189 190	/*191	 * Get the initial values for AMR, IAMR and UAMOR and communicate them192	 * to the child.193	 */194	ret = ptrace_read_regs(pid, NT_PPC_PKEY, regs, 3);195	PARENT_SKIP_IF_UNSUPPORTED(ret, &info->child_sync, "PKEYs not supported");196	PARENT_FAIL_IF(ret, &info->child_sync);197 198	info->amr1 = info->amr2 = regs[0];199	info->expected_iamr = regs[1];200	info->expected_uamor = regs[2];201 202	/* Wake up child so that it can set itself up. */203	ret = prod_child(&info->child_sync);204	PARENT_FAIL_IF(ret, &info->child_sync);205 206	ret = wait_child(&info->child_sync);207	if (ret)208		return ret;209 210	/* Verify that we can read the pkey registers from the child. */211	ret = ptrace_read_regs(pid, NT_PPC_PKEY, regs, 3);212	PARENT_FAIL_IF(ret, &info->child_sync);213 214	printf("%-30s AMR: %016lx IAMR: %016lx UAMOR: %016lx\n",215	       ptrace_read_running, regs[0], regs[1], regs[2]);216 217	PARENT_FAIL_IF(regs[0] != info->amr1, &info->child_sync);218	PARENT_FAIL_IF(regs[1] != info->expected_iamr, &info->child_sync);219	PARENT_FAIL_IF(regs[2] != info->expected_uamor, &info->child_sync);220 221	/* Write valid AMR value in child. */222	ret = ptrace_write_regs(pid, NT_PPC_PKEY, &info->amr2, 1);223	PARENT_FAIL_IF(ret, &info->child_sync);224 225	printf("%-30s AMR: %016lx\n", ptrace_write_running, info->amr2);226 227	/* Wake up child so that it can verify it changed. */228	ret = prod_child(&info->child_sync);229	PARENT_FAIL_IF(ret, &info->child_sync);230 231	ret = wait_child(&info->child_sync);232	if (ret)233		return ret;234 235	/* Write invalid AMR value in child. */236	ret = ptrace_write_regs(pid, NT_PPC_PKEY, &info->invalid_amr, 1);237	PARENT_FAIL_IF(ret, &info->child_sync);238 239	printf("%-30s AMR: %016lx\n", ptrace_write_running, info->invalid_amr);240 241	/* Wake up child so that it can verify it didn't change. */242	ret = prod_child(&info->child_sync);243	PARENT_FAIL_IF(ret, &info->child_sync);244 245	ret = wait_child(&info->child_sync);246	if (ret)247		return ret;248 249	/* Try to write to IAMR. */250	regs[0] = info->amr1;251	regs[1] = info->invalid_iamr;252	ret = ptrace_write_regs(pid, NT_PPC_PKEY, regs, 2);253	PARENT_FAIL_IF(!ret, &info->child_sync);254 255	printf("%-30s AMR: %016lx IAMR: %016lx\n",256	       ptrace_write_running, regs[0], regs[1]);257 258	/* Try to write to IAMR and UAMOR. */259	regs[2] = info->invalid_uamor;260	ret = ptrace_write_regs(pid, NT_PPC_PKEY, regs, 3);261	PARENT_FAIL_IF(!ret, &info->child_sync);262 263	printf("%-30s AMR: %016lx IAMR: %016lx UAMOR: %016lx\n",264	       ptrace_write_running, regs[0], regs[1], regs[2]);265 266	/* Verify that all registers still have their expected values. */267	ret = ptrace_read_regs(pid, NT_PPC_PKEY, regs, 3);268	PARENT_FAIL_IF(ret, &info->child_sync);269 270	printf("%-30s AMR: %016lx IAMR: %016lx UAMOR: %016lx\n",271	       ptrace_read_running, regs[0], regs[1], regs[2]);272 273	PARENT_FAIL_IF(regs[0] != info->amr2, &info->child_sync);274	PARENT_FAIL_IF(regs[1] != info->expected_iamr, &info->child_sync);275	PARENT_FAIL_IF(regs[2] != info->expected_uamor, &info->child_sync);276 277	/* Wake up child so that it can verify AMR didn't change and wrap up. */278	ret = prod_child(&info->child_sync);279	PARENT_FAIL_IF(ret, &info->child_sync);280 281	ret = wait(&status);282	if (ret != pid) {283		printf("Child's exit status not captured\n");284		ret = TEST_PASS;285	} else if (!WIFEXITED(status)) {286		printf("Child exited abnormally\n");287		ret = TEST_FAIL;288	} else289		ret = WEXITSTATUS(status) ? TEST_FAIL : TEST_PASS;290 291	return ret;292}293 294static int ptrace_pkey(void)295{296	struct shared_info *info;297	int shm_id;298	int ret;299	pid_t pid;300 301	shm_id = shmget(IPC_PRIVATE, sizeof(*info), 0777 | IPC_CREAT);302	info = shmat(shm_id, NULL, 0);303 304	ret = init_child_sync(&info->child_sync);305	if (ret)306		return ret;307 308	pid = fork();309	if (pid < 0) {310		perror("fork() failed");311		ret = TEST_FAIL;312	} else if (pid == 0)313		ret = child(info);314	else315		ret = parent(info, pid);316 317	shmdt(info);318 319	if (pid) {320		destroy_child_sync(&info->child_sync);321		shmctl(shm_id, IPC_RMID, NULL);322	}323 324	return ret;325}326 327int main(int argc, char *argv[])328{329	return test_harness(ptrace_pkey, "ptrace_pkey");330}331