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 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 child 57 * (even though they're valid ones) because userspace doesn't have 58 * access to those registers. 59 */ 60 unsigned long invalid_iamr; 61 unsigned long invalid_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 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 write 100 * 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_iamr 106 */ 107 if (disable_execute) 108 info->expected_iamr |= 1ul << pkeyshift(pkey1); 109 else 110 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't 162 * 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 185 static 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 them 192 * to the child. 193 */ 194 ret = ptrace_read_regs(pid, NT_PPC_PKEY, regs, 3); 195 PARENT_SKIP_IF_UNSUPPORTED(ret, &info->child_sync); 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 } else 289 ret = WEXITSTATUS(status) ? TEST_FAIL : TEST_PASS; 290 291 return ret; 292 } 293 294 static 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 else 315 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 327 int main(int argc, char *argv[]) 328 { 329 return test_harness(ptrace_pkey, "ptrace_pkey"); 330 } 331