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