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