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