~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c

Version: ~ [ linux-5.6-rc1 ] ~ [ linux-5.5.2 ] ~ [ linux-5.4.17 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.102 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.170 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.213 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.213 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.81 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-3.9.11 ] ~ [ linux-3.8.13 ] ~ [ linux-3.7.10 ] ~ [ linux-3.6.11 ] ~ [ linux-3.5.7 ] ~ [ linux-3.4.113 ] ~ [ linux-3.3.8 ] ~ [ linux-3.2.102 ] ~ [ linux-3.1.10 ] ~ [ linux-3.0.101 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp