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 #include "pkeys.h" 11 12 static const char user_read[] = "[User Read (Running)]"; 13 static const char user_write[] = "[User Write (Running)]"; 14 static const char ptrace_read_running[] = "[Ptrace Read (Running)]"; 15 static const char ptrace_write_running[] = "[Ptrace Write (Running)]"; 16 17 /* Information shared between the parent and the child. */ 18 struct shared_info { 19 struct child_sync child_sync; 20 21 /* AMR value the parent expects to read from the child. */ 22 unsigned long amr1; 23 24 /* AMR value the parent is expected to write to the child. */ 25 unsigned long amr2; 26 27 /* AMR value that ptrace should refuse to write to the child. */ 28 unsigned long invalid_amr; 29 30 /* IAMR value the parent expects to read from the child. */ 31 unsigned long expected_iamr; 32 33 /* UAMOR value the parent expects to read from the child. */ 34 unsigned long expected_uamor; 35 36 /* 37 * IAMR and UAMOR values that ptrace should refuse to write to the child 38 * (even though they're valid ones) because userspace doesn't have 39 * access to those registers. 40 */ 41 unsigned long invalid_iamr; 42 unsigned long invalid_uamor; 43 }; 44 45 static int child(struct shared_info *info) 46 { 47 unsigned long reg; 48 bool disable_execute = true; 49 int pkey1, pkey2, pkey3; 50 int ret; 51 52 /* Wait until parent fills out the initial register values. */ 53 ret = wait_parent(&info->child_sync); 54 if (ret) 55 return ret; 56 57 /* Get some pkeys so that we can change their bits in the AMR. */ 58 pkey1 = sys_pkey_alloc(0, PKEY_DISABLE_EXECUTE); 59 if (pkey1 < 0) { 60 pkey1 = sys_pkey_alloc(0, 0); 61 CHILD_FAIL_IF(pkey1 < 0, &info->child_sync); 62 63 disable_execute = false; 64 } 65 66 pkey2 = sys_pkey_alloc(0, 0); 67 CHILD_FAIL_IF(pkey2 < 0, &info->child_sync); 68 69 pkey3 = sys_pkey_alloc(0, 0); 70 CHILD_FAIL_IF(pkey3 < 0, &info->child_sync); 71 72 info->amr1 |= 3ul << pkeyshift(pkey1); 73 info->amr2 |= 3ul << pkeyshift(pkey2); 74 /* 75 * invalid amr value where we try to force write 76 * things which are deined by a uamor setting. 77 */ 78 info->invalid_amr = info->amr2 | (~0x0UL & ~info->expected_uamor); 79 80 /* 81 * if PKEY_DISABLE_EXECUTE succeeded we should update the expected_iamr 82 */ 83 if (disable_execute) 84 info->expected_iamr |= 1ul << pkeyshift(pkey1); 85 else 86 info->expected_iamr &= ~(1ul << pkeyshift(pkey1)); 87 88 /* 89 * We allocated pkey2 and pkey 3 above. Clear the IAMR bits. 90 */ 91 info->expected_iamr &= ~(1ul << pkeyshift(pkey2)); 92 info->expected_iamr &= ~(1ul << pkeyshift(pkey3)); 93 94 /* 95 * Create an IAMR value different from expected value. 96 * Kernel will reject an IAMR and UAMOR change. 97 */ 98 info->invalid_iamr = info->expected_iamr | (1ul << pkeyshift(pkey1) | 1ul << pkeyshift(pkey2)); 99 info->invalid_uamor = info->expected_uamor & ~(0x3ul << pkeyshift(pkey1)); 100 101 printf("%-30s AMR: %016lx pkey1: %d pkey2: %d pkey3: %d\n", 102 user_write, info->amr1, pkey1, pkey2, pkey3); 103 104 set_amr(info->amr1); 105 106 /* Wait for parent to read our AMR value and write a new one. */ 107 ret = prod_parent(&info->child_sync); 108 CHILD_FAIL_IF(ret, &info->child_sync); 109 110 ret = wait_parent(&info->child_sync); 111 if (ret) 112 return ret; 113 114 reg = mfspr(SPRN_AMR); 115 116 printf("%-30s AMR: %016lx\n", user_read, reg); 117 118 CHILD_FAIL_IF(reg != info->amr2, &info->child_sync); 119 120 /* 121 * Wait for parent to try to write an invalid AMR value. 122 */ 123 ret = prod_parent(&info->child_sync); 124 CHILD_FAIL_IF(ret, &info->child_sync); 125 126 ret = wait_parent(&info->child_sync); 127 if (ret) 128 return ret; 129 130 reg = mfspr(SPRN_AMR); 131 132 printf("%-30s AMR: %016lx\n", user_read, reg); 133 134 CHILD_FAIL_IF(reg != info->amr2, &info->child_sync); 135 136 /* 137 * Wait for parent to try to write an IAMR and a UAMOR value. We can't 138 * verify them, but we can verify that the AMR didn't change. 139 */ 140 ret = prod_parent(&info->child_sync); 141 CHILD_FAIL_IF(ret, &info->child_sync); 142 143 ret = wait_parent(&info->child_sync); 144 if (ret) 145 return ret; 146 147 reg = mfspr(SPRN_AMR); 148 149 printf("%-30s AMR: %016lx\n", user_read, reg); 150 151 CHILD_FAIL_IF(reg != info->amr2, &info->child_sync); 152 153 /* Now let parent now that we are finished. */ 154 155 ret = prod_parent(&info->child_sync); 156 CHILD_FAIL_IF(ret, &info->child_sync); 157 158 return TEST_PASS; 159 } 160 161 static int parent(struct shared_info *info, pid_t pid) 162 { 163 unsigned long regs[3]; 164 int ret, status; 165 166 /* 167 * Get the initial values for AMR, IAMR and UAMOR and communicate them 168 * to the child. 169 */ 170 ret = ptrace_read_regs(pid, NT_PPC_PKEY, regs, 3); 171 PARENT_SKIP_IF_UNSUPPORTED(ret, &info->child_sync, "PKEYs not supported"); 172 PARENT_FAIL_IF(ret, &info->child_sync); 173 174 info->amr1 = info->amr2 = regs[0]; 175 info->expected_iamr = regs[1]; 176 info->expected_uamor = regs[2]; 177 178 /* Wake up child so that it can set itself up. */ 179 ret = prod_child(&info->child_sync); 180 PARENT_FAIL_IF(ret, &info->child_sync); 181 182 ret = wait_child(&info->child_sync); 183 if (ret) 184 return ret; 185 186 /* Verify that we can read the pkey registers from the child. */ 187 ret = ptrace_read_regs(pid, NT_PPC_PKEY, regs, 3); 188 PARENT_FAIL_IF(ret, &info->child_sync); 189 190 printf("%-30s AMR: %016lx IAMR: %016lx UAMOR: %016lx\n", 191 ptrace_read_running, regs[0], regs[1], regs[2]); 192 193 PARENT_FAIL_IF(regs[0] != info->amr1, &info->child_sync); 194 PARENT_FAIL_IF(regs[1] != info->expected_iamr, &info->child_sync); 195 PARENT_FAIL_IF(regs[2] != info->expected_uamor, &info->child_sync); 196 197 /* Write valid AMR value in child. */ 198 ret = ptrace_write_regs(pid, NT_PPC_PKEY, &info->amr2, 1); 199 PARENT_FAIL_IF(ret, &info->child_sync); 200 201 printf("%-30s AMR: %016lx\n", ptrace_write_running, info->amr2); 202 203 /* Wake up child so that it can verify it changed. */ 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 /* Write invalid AMR value in child. */ 212 ret = ptrace_write_regs(pid, NT_PPC_PKEY, &info->invalid_amr, 1); 213 PARENT_FAIL_IF(ret, &info->child_sync); 214 215 printf("%-30s AMR: %016lx\n", ptrace_write_running, info->invalid_amr); 216 217 /* Wake up child so that it can verify it didn't change. */ 218 ret = prod_child(&info->child_sync); 219 PARENT_FAIL_IF(ret, &info->child_sync); 220 221 ret = wait_child(&info->child_sync); 222 if (ret) 223 return ret; 224 225 /* Try to write to IAMR. */ 226 regs[0] = info->amr1; 227 regs[1] = info->invalid_iamr; 228 ret = ptrace_write_regs(pid, NT_PPC_PKEY, regs, 2); 229 PARENT_FAIL_IF(!ret, &info->child_sync); 230 231 printf("%-30s AMR: %016lx IAMR: %016lx\n", 232 ptrace_write_running, regs[0], regs[1]); 233 234 /* Try to write to IAMR and UAMOR. */ 235 regs[2] = info->invalid_uamor; 236 ret = ptrace_write_regs(pid, NT_PPC_PKEY, regs, 3); 237 PARENT_FAIL_IF(!ret, &info->child_sync); 238 239 printf("%-30s AMR: %016lx IAMR: %016lx UAMOR: %016lx\n", 240 ptrace_write_running, regs[0], regs[1], regs[2]); 241 242 /* Verify that all registers still have their expected values. */ 243 ret = ptrace_read_regs(pid, NT_PPC_PKEY, regs, 3); 244 PARENT_FAIL_IF(ret, &info->child_sync); 245 246 printf("%-30s AMR: %016lx IAMR: %016lx UAMOR: %016lx\n", 247 ptrace_read_running, regs[0], regs[1], regs[2]); 248 249 PARENT_FAIL_IF(regs[0] != info->amr2, &info->child_sync); 250 PARENT_FAIL_IF(regs[1] != info->expected_iamr, &info->child_sync); 251 PARENT_FAIL_IF(regs[2] != info->expected_uamor, &info->child_sync); 252 253 /* Wake up child so that it can verify AMR didn't change and wrap up. */ 254 ret = prod_child(&info->child_sync); 255 PARENT_FAIL_IF(ret, &info->child_sync); 256 257 ret = wait(&status); 258 if (ret != pid) { 259 printf("Child's exit status not captured\n"); 260 ret = TEST_PASS; 261 } else if (!WIFEXITED(status)) { 262 printf("Child exited abnormally\n"); 263 ret = TEST_FAIL; 264 } else 265 ret = WEXITSTATUS(status) ? TEST_FAIL : TEST_PASS; 266 267 return ret; 268 } 269 270 static int ptrace_pkey(void) 271 { 272 struct shared_info *info; 273 int shm_id; 274 int ret; 275 pid_t pid; 276 277 shm_id = shmget(IPC_PRIVATE, sizeof(*info), 0777 | IPC_CREAT); 278 info = shmat(shm_id, NULL, 0); 279 280 ret = init_child_sync(&info->child_sync); 281 if (ret) 282 return ret; 283 284 pid = fork(); 285 if (pid < 0) { 286 perror("fork() failed"); 287 ret = TEST_FAIL; 288 } else if (pid == 0) 289 ret = child(info); 290 else 291 ret = parent(info, pid); 292 293 shmdt(info); 294 295 if (pid) { 296 destroy_child_sync(&info->child_sync); 297 shmctl(shm_id, IPC_RMID, NULL); 298 } 299 300 return ret; 301 } 302 303 int main(int argc, char *argv[]) 304 { 305 return test_harness(ptrace_pkey, "ptrace_pkey"); 306 } 307