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 108 info->expected_uamor |= 3ul << pkeyshift(pkey1) | 109 3ul << pkeyshift(pkey2); 110 info->new_iamr |= 1ul << pkeyshift(pkey1) | 1ul << pkeyshift(pkey2); 111 info->new_uamor |= 3ul << pkeyshift(pkey1); 112 113 /* 114 * We won't use pkey3. We just want a plausible but invalid key to test 115 * whether ptrace will let us write to AMR bits we are not supposed to. 116 * 117 * This also tests whether the kernel restores the UAMOR permissions 118 * after a key is freed. 119 */ 120 sys_pkey_free(pkey3); 121 122 printf("%-30s AMR: %016lx pkey1: %d pkey2: %d pkey3: %d\n", 123 user_write, info->amr1, pkey1, pkey2, pkey3); 124 125 mtspr(SPRN_AMR, info->amr1); 126 127 /* Wait for parent to read our AMR value and write a new one. */ 128 ret = prod_parent(&info->child_sync); 129 CHILD_FAIL_IF(ret, &info->child_sync); 130 131 ret = wait_parent(&info->child_sync); 132 if (ret) 133 return ret; 134 135 reg = mfspr(SPRN_AMR); 136 137 printf("%-30s AMR: %016lx\n", user_read, reg); 138 139 CHILD_FAIL_IF(reg != info->amr2, &info->child_sync); 140 141 /* 142 * Wait for parent to try to write an invalid AMR value. 143 */ 144 ret = prod_parent(&info->child_sync); 145 CHILD_FAIL_IF(ret, &info->child_sync); 146 147 ret = wait_parent(&info->child_sync); 148 if (ret) 149 return ret; 150 151 reg = mfspr(SPRN_AMR); 152 153 printf("%-30s AMR: %016lx\n", user_read, reg); 154 155 CHILD_FAIL_IF(reg != info->amr2, &info->child_sync); 156 157 /* 158 * Wait for parent to try to write an IAMR and a UAMOR value. We can't 159 * verify them, but we can verify that the AMR didn't change. 160 */ 161 ret = prod_parent(&info->child_sync); 162 CHILD_FAIL_IF(ret, &info->child_sync); 163 164 ret = wait_parent(&info->child_sync); 165 if (ret) 166 return ret; 167 168 reg = mfspr(SPRN_AMR); 169 170 printf("%-30s AMR: %016lx\n", user_read, reg); 171 172 CHILD_FAIL_IF(reg != info->amr2, &info->child_sync); 173 174 /* Now let parent now that we are finished. */ 175 176 ret = prod_parent(&info->child_sync); 177 CHILD_FAIL_IF(ret, &info->child_sync); 178 179 return TEST_PASS; 180 } 181 182 static int parent(struct shared_info *info, pid_t pid) 183 { 184 unsigned long regs[3]; 185 int ret, status; 186 187 /* 188 * Get the initial values for AMR, IAMR and UAMOR and communicate them 189 * to the child. 190 */ 191 ret = ptrace_read_regs(pid, NT_PPC_PKEY, regs, 3); 192 PARENT_SKIP_IF_UNSUPPORTED(ret, &info->child_sync); 193 PARENT_FAIL_IF(ret, &info->child_sync); 194 195 info->amr1 = info->amr2 = info->amr3 = regs[0]; 196 info->expected_iamr = info->new_iamr = regs[1]; 197 info->expected_uamor = info->new_uamor = regs[2]; 198 199 /* Wake up child so that it can set itself up. */ 200 ret = prod_child(&info->child_sync); 201 PARENT_FAIL_IF(ret, &info->child_sync); 202 203 ret = wait_child(&info->child_sync); 204 if (ret) 205 return ret; 206 207 /* Verify that we can read the pkey registers from the child. */ 208 ret = ptrace_read_regs(pid, NT_PPC_PKEY, regs, 3); 209 PARENT_FAIL_IF(ret, &info->child_sync); 210 211 printf("%-30s AMR: %016lx IAMR: %016lx UAMOR: %016lx\n", 212 ptrace_read_running, regs[0], regs[1], regs[2]); 213 214 PARENT_FAIL_IF(regs[0] != info->amr1, &info->child_sync); 215 PARENT_FAIL_IF(regs[1] != info->expected_iamr, &info->child_sync); 216 PARENT_FAIL_IF(regs[2] != info->expected_uamor, &info->child_sync); 217 218 /* Write valid AMR value in child. */ 219 ret = ptrace_write_regs(pid, NT_PPC_PKEY, &info->amr2, 1); 220 PARENT_FAIL_IF(ret, &info->child_sync); 221 222 printf("%-30s AMR: %016lx\n", ptrace_write_running, info->amr2); 223 224 /* Wake up child so that it can verify it changed. */ 225 ret = prod_child(&info->child_sync); 226 PARENT_FAIL_IF(ret, &info->child_sync); 227 228 ret = wait_child(&info->child_sync); 229 if (ret) 230 return ret; 231 232 /* Write invalid AMR value in child. */ 233 ret = ptrace_write_regs(pid, NT_PPC_PKEY, &info->amr3, 1); 234 PARENT_FAIL_IF(ret, &info->child_sync); 235 236 printf("%-30s AMR: %016lx\n", ptrace_write_running, info->amr3); 237 238 /* Wake up child so that it can verify it didn't change. */ 239 ret = prod_child(&info->child_sync); 240 PARENT_FAIL_IF(ret, &info->child_sync); 241 242 ret = wait_child(&info->child_sync); 243 if (ret) 244 return ret; 245 246 /* Try to write to IAMR. */ 247 regs[0] = info->amr1; 248 regs[1] = info->new_iamr; 249 ret = ptrace_write_regs(pid, NT_PPC_PKEY, regs, 2); 250 PARENT_FAIL_IF(!ret, &info->child_sync); 251 252 printf("%-30s AMR: %016lx IAMR: %016lx\n", 253 ptrace_write_running, regs[0], regs[1]); 254 255 /* Try to write to IAMR and UAMOR. */ 256 regs[2] = info->new_uamor; 257 ret = ptrace_write_regs(pid, NT_PPC_PKEY, regs, 3); 258 PARENT_FAIL_IF(!ret, &info->child_sync); 259 260 printf("%-30s AMR: %016lx IAMR: %016lx UAMOR: %016lx\n", 261 ptrace_write_running, regs[0], regs[1], regs[2]); 262 263 /* Verify that all registers still have their expected values. */ 264 ret = ptrace_read_regs(pid, NT_PPC_PKEY, regs, 3); 265 PARENT_FAIL_IF(ret, &info->child_sync); 266 267 printf("%-30s AMR: %016lx IAMR: %016lx UAMOR: %016lx\n", 268 ptrace_read_running, regs[0], regs[1], regs[2]); 269 270 PARENT_FAIL_IF(regs[0] != info->amr2, &info->child_sync); 271 PARENT_FAIL_IF(regs[1] != info->expected_iamr, &info->child_sync); 272 PARENT_FAIL_IF(regs[2] != info->expected_uamor, &info->child_sync); 273 274 /* Wake up child so that it can verify AMR didn't change and wrap up. */ 275 ret = prod_child(&info->child_sync); 276 PARENT_FAIL_IF(ret, &info->child_sync); 277 278 ret = wait(&status); 279 if (ret != pid) { 280 printf("Child's exit status not captured\n"); 281 ret = TEST_PASS; 282 } else if (!WIFEXITED(status)) { 283 printf("Child exited abnormally\n"); 284 ret = TEST_FAIL; 285 } else 286 ret = WEXITSTATUS(status) ? TEST_FAIL : TEST_PASS; 287 288 return ret; 289 } 290 291 static int ptrace_pkey(void) 292 { 293 struct shared_info *info; 294 int shm_id; 295 int ret; 296 pid_t pid; 297 298 shm_id = shmget(IPC_PRIVATE, sizeof(*info), 0777 | IPC_CREAT); 299 info = shmat(shm_id, NULL, 0); 300 301 ret = init_child_sync(&info->child_sync); 302 if (ret) 303 return ret; 304 305 pid = fork(); 306 if (pid < 0) { 307 perror("fork() failed"); 308 ret = TEST_FAIL; 309 } else if (pid == 0) 310 ret = child(info); 311 else 312 ret = parent(info, pid); 313 314 shmdt(info); 315 316 if (pid) { 317 destroy_child_sync(&info->child_sync); 318 shmctl(shm_id, IPC_RMID, NULL); 319 } 320 321 return ret; 322 } 323 324 int main(int argc, char *argv[]) 325 { 326 return test_harness(ptrace_pkey, "ptrace_pkey"); 327 } 328