root/tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. sys_pkey_alloc
  2. sys_pkey_free
  3. child
  4. parent
  5. ptrace_pkey
  6. main

   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 }

/* [<][>][^][v][top][bottom][index][help] */