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 <limits.h> 9 #include <linux/kernel.h> 10 #include <sys/mman.h> 11 #include <sys/types.h> 12 #include <sys/stat.h> 13 #include <sys/time.h> 14 #include <sys/resource.h> 15 #include <fcntl.h> 16 #include <unistd.h> 17 #include "ptrace.h" 18 #include "child.h" 19 20 #ifndef __NR_pkey_alloc 21 #define __NR_pkey_alloc 384 22 #endif 23 24 #ifndef __NR_pkey_free 25 #define __NR_pkey_free 385 26 #endif 27 28 #ifndef NT_PPC_PKEY 29 #define NT_PPC_PKEY 0x110 30 #endif 31 32 #ifndef PKEY_DISABLE_EXECUTE 33 #define PKEY_DISABLE_EXECUTE 0x4 34 #endif 35 36 #define AMR_BITS_PER_PKEY 2 37 #define PKEY_REG_BITS (sizeof(u64) * 8) 38 #define pkeyshift(pkey) (PKEY_REG_BITS - ((pkey + 1) * AMR_BITS_PER_PKEY)) 39 40 #define CORE_FILE_LIMIT (5 * 1024 * 1024) /* 5 MB should be enough */ 41 42 static const char core_pattern_file[] = "/proc/sys/kernel/core_pattern"; 43 44 static const char user_write[] = "[User Write (Running)]"; 45 static const char core_read_running[] = "[Core Read (Running)]"; 46 47 /* Information shared between the parent and the child. */ 48 struct shared_info { 49 struct child_sync child_sync; 50 51 /* AMR value the parent expects to read in the core file. */ 52 unsigned long amr; 53 54 /* IAMR value the parent expects to read in the core file. */ 55 unsigned long iamr; 56 57 /* UAMOR value the parent expects to read in the core file. */ 58 unsigned long uamor; 59 60 /* When the child crashed. */ 61 time_t core_time; 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 increase_core_file_limit(void) 75 { 76 struct rlimit rlim; 77 int ret; 78 79 ret = getrlimit(RLIMIT_CORE, &rlim); 80 FAIL_IF(ret); 81 82 if (rlim.rlim_cur != RLIM_INFINITY && rlim.rlim_cur < CORE_FILE_LIMIT) { 83 rlim.rlim_cur = CORE_FILE_LIMIT; 84 85 if (rlim.rlim_max != RLIM_INFINITY && 86 rlim.rlim_max < CORE_FILE_LIMIT) 87 rlim.rlim_max = CORE_FILE_LIMIT; 88 89 ret = setrlimit(RLIMIT_CORE, &rlim); 90 FAIL_IF(ret); 91 } 92 93 ret = getrlimit(RLIMIT_FSIZE, &rlim); 94 FAIL_IF(ret); 95 96 if (rlim.rlim_cur != RLIM_INFINITY && rlim.rlim_cur < CORE_FILE_LIMIT) { 97 rlim.rlim_cur = CORE_FILE_LIMIT; 98 99 if (rlim.rlim_max != RLIM_INFINITY && 100 rlim.rlim_max < CORE_FILE_LIMIT) 101 rlim.rlim_max = CORE_FILE_LIMIT; 102 103 ret = setrlimit(RLIMIT_FSIZE, &rlim); 104 FAIL_IF(ret); 105 } 106 107 return TEST_PASS; 108 } 109 110 static int child(struct shared_info *info) 111 { 112 bool disable_execute = true; 113 int pkey1, pkey2, pkey3; 114 int *ptr, ret; 115 116 /* Wait until parent fills out the initial register values. */ 117 ret = wait_parent(&info->child_sync); 118 if (ret) 119 return ret; 120 121 ret = increase_core_file_limit(); 122 FAIL_IF(ret); 123 124 /* Get some pkeys so that we can change their bits in the AMR. */ 125 pkey1 = sys_pkey_alloc(0, PKEY_DISABLE_EXECUTE); 126 if (pkey1 < 0) { 127 pkey1 = sys_pkey_alloc(0, 0); 128 FAIL_IF(pkey1 < 0); 129 130 disable_execute = false; 131 } 132 133 pkey2 = sys_pkey_alloc(0, 0); 134 FAIL_IF(pkey2 < 0); 135 136 pkey3 = sys_pkey_alloc(0, 0); 137 FAIL_IF(pkey3 < 0); 138 139 info->amr |= 3ul << pkeyshift(pkey1) | 2ul << pkeyshift(pkey2); 140 141 if (disable_execute) 142 info->iamr |= 1ul << pkeyshift(pkey1); 143 144 info->uamor |= 3ul << pkeyshift(pkey1) | 3ul << pkeyshift(pkey2); 145 146 printf("%-30s AMR: %016lx pkey1: %d pkey2: %d pkey3: %d\n", 147 user_write, info->amr, pkey1, pkey2, pkey3); 148 149 mtspr(SPRN_AMR, info->amr); 150 151 /* 152 * We won't use pkey3. This tests whether the kernel restores the UAMOR 153 * permissions after a key is freed. 154 */ 155 sys_pkey_free(pkey3); 156 157 info->core_time = time(NULL); 158 159 /* Crash. */ 160 ptr = 0; 161 *ptr = 1; 162 163 /* Shouldn't get here. */ 164 FAIL_IF(true); 165 166 return TEST_FAIL; 167 } 168 169 /* Return file size if filename exists and pass sanity check, or zero if not. */ 170 static off_t try_core_file(const char *filename, struct shared_info *info, 171 pid_t pid) 172 { 173 struct stat buf; 174 int ret; 175 176 ret = stat(filename, &buf); 177 if (ret == -1) 178 return TEST_FAIL; 179 180 /* Make sure we're not using a stale core file. */ 181 return buf.st_mtime >= info->core_time ? buf.st_size : TEST_FAIL; 182 } 183 184 static Elf64_Nhdr *next_note(Elf64_Nhdr *nhdr) 185 { 186 return (void *) nhdr + sizeof(*nhdr) + 187 __ALIGN_KERNEL(nhdr->n_namesz, 4) + 188 __ALIGN_KERNEL(nhdr->n_descsz, 4); 189 } 190 191 static int check_core_file(struct shared_info *info, Elf64_Ehdr *ehdr, 192 off_t core_size) 193 { 194 unsigned long *regs; 195 Elf64_Phdr *phdr; 196 Elf64_Nhdr *nhdr; 197 size_t phdr_size; 198 void *p = ehdr, *note; 199 int ret; 200 201 ret = memcmp(ehdr->e_ident, ELFMAG, SELFMAG); 202 FAIL_IF(ret); 203 204 FAIL_IF(ehdr->e_type != ET_CORE); 205 FAIL_IF(ehdr->e_machine != EM_PPC64); 206 FAIL_IF(ehdr->e_phoff == 0 || ehdr->e_phnum == 0); 207 208 /* 209 * e_phnum is at most 65535 so calculating the size of the 210 * program header cannot overflow. 211 */ 212 phdr_size = sizeof(*phdr) * ehdr->e_phnum; 213 214 /* Sanity check the program header table location. */ 215 FAIL_IF(ehdr->e_phoff + phdr_size < ehdr->e_phoff); 216 FAIL_IF(ehdr->e_phoff + phdr_size > core_size); 217 218 /* Find the PT_NOTE segment. */ 219 for (phdr = p + ehdr->e_phoff; 220 (void *) phdr < p + ehdr->e_phoff + phdr_size; 221 phdr += ehdr->e_phentsize) 222 if (phdr->p_type == PT_NOTE) 223 break; 224 225 FAIL_IF((void *) phdr >= p + ehdr->e_phoff + phdr_size); 226 227 /* Find the NT_PPC_PKEY note. */ 228 for (nhdr = p + phdr->p_offset; 229 (void *) nhdr < p + phdr->p_offset + phdr->p_filesz; 230 nhdr = next_note(nhdr)) 231 if (nhdr->n_type == NT_PPC_PKEY) 232 break; 233 234 FAIL_IF((void *) nhdr >= p + phdr->p_offset + phdr->p_filesz); 235 FAIL_IF(nhdr->n_descsz == 0); 236 237 p = nhdr; 238 note = p + sizeof(*nhdr) + __ALIGN_KERNEL(nhdr->n_namesz, 4); 239 240 regs = (unsigned long *) note; 241 242 printf("%-30s AMR: %016lx IAMR: %016lx UAMOR: %016lx\n", 243 core_read_running, regs[0], regs[1], regs[2]); 244 245 FAIL_IF(regs[0] != info->amr); 246 FAIL_IF(regs[1] != info->iamr); 247 FAIL_IF(regs[2] != info->uamor); 248 249 return TEST_PASS; 250 } 251 252 static int parent(struct shared_info *info, pid_t pid) 253 { 254 char *filenames, *filename[3]; 255 int fd, i, ret, status; 256 unsigned long regs[3]; 257 off_t core_size; 258 void *core; 259 260 /* 261 * Get the initial values for AMR, IAMR and UAMOR and communicate them 262 * to the child. 263 */ 264 ret = ptrace_read_regs(pid, NT_PPC_PKEY, regs, 3); 265 PARENT_SKIP_IF_UNSUPPORTED(ret, &info->child_sync); 266 PARENT_FAIL_IF(ret, &info->child_sync); 267 268 info->amr = regs[0]; 269 info->iamr = regs[1]; 270 info->uamor = regs[2]; 271 272 /* Wake up child so that it can set itself up. */ 273 ret = prod_child(&info->child_sync); 274 PARENT_FAIL_IF(ret, &info->child_sync); 275 276 ret = wait(&status); 277 if (ret != pid) { 278 printf("Child's exit status not captured\n"); 279 return TEST_FAIL; 280 } else if (!WIFSIGNALED(status) || !WCOREDUMP(status)) { 281 printf("Child didn't dump core\n"); 282 return TEST_FAIL; 283 } 284 285 /* Construct array of core file names to try. */ 286 287 filename[0] = filenames = malloc(PATH_MAX); 288 if (!filenames) { 289 perror("Error allocating memory"); 290 return TEST_FAIL; 291 } 292 293 ret = snprintf(filename[0], PATH_MAX, "core-pkey.%d", pid); 294 if (ret < 0 || ret >= PATH_MAX) { 295 ret = TEST_FAIL; 296 goto out; 297 } 298 299 filename[1] = filename[0] + ret + 1; 300 ret = snprintf(filename[1], PATH_MAX - ret - 1, "core.%d", pid); 301 if (ret < 0 || ret >= PATH_MAX - ret - 1) { 302 ret = TEST_FAIL; 303 goto out; 304 } 305 filename[2] = "core"; 306 307 for (i = 0; i < 3; i++) { 308 core_size = try_core_file(filename[i], info, pid); 309 if (core_size != TEST_FAIL) 310 break; 311 } 312 313 if (i == 3) { 314 printf("Couldn't find core file\n"); 315 ret = TEST_FAIL; 316 goto out; 317 } 318 319 fd = open(filename[i], O_RDONLY); 320 if (fd == -1) { 321 perror("Error opening core file"); 322 ret = TEST_FAIL; 323 goto out; 324 } 325 326 core = mmap(NULL, core_size, PROT_READ, MAP_PRIVATE, fd, 0); 327 if (core == (void *) -1) { 328 perror("Error mmaping core file"); 329 ret = TEST_FAIL; 330 goto out; 331 } 332 333 ret = check_core_file(info, core, core_size); 334 335 munmap(core, core_size); 336 close(fd); 337 unlink(filename[i]); 338 339 out: 340 free(filenames); 341 342 return ret; 343 } 344 345 static int write_core_pattern(const char *core_pattern) 346 { 347 size_t len = strlen(core_pattern), ret; 348 FILE *f; 349 350 f = fopen(core_pattern_file, "w"); 351 if (!f) { 352 perror("Error writing to core_pattern file"); 353 return TEST_FAIL; 354 } 355 356 ret = fwrite(core_pattern, 1, len, f); 357 fclose(f); 358 if (ret != len) { 359 perror("Error writing to core_pattern file"); 360 return TEST_FAIL; 361 } 362 363 return TEST_PASS; 364 } 365 366 static int setup_core_pattern(char **core_pattern_, bool *changed_) 367 { 368 FILE *f; 369 char *core_pattern; 370 int ret; 371 372 core_pattern = malloc(PATH_MAX); 373 if (!core_pattern) { 374 perror("Error allocating memory"); 375 return TEST_FAIL; 376 } 377 378 f = fopen(core_pattern_file, "r"); 379 if (!f) { 380 perror("Error opening core_pattern file"); 381 ret = TEST_FAIL; 382 goto out; 383 } 384 385 ret = fread(core_pattern, 1, PATH_MAX, f); 386 fclose(f); 387 if (!ret) { 388 perror("Error reading core_pattern file"); 389 ret = TEST_FAIL; 390 goto out; 391 } 392 393 /* Check whether we can predict the name of the core file. */ 394 if (!strcmp(core_pattern, "core") || !strcmp(core_pattern, "core.%p")) 395 *changed_ = false; 396 else { 397 ret = write_core_pattern("core-pkey.%p"); 398 if (ret) 399 goto out; 400 401 *changed_ = true; 402 } 403 404 *core_pattern_ = core_pattern; 405 ret = TEST_PASS; 406 407 out: 408 if (ret) 409 free(core_pattern); 410 411 return ret; 412 } 413 414 static int core_pkey(void) 415 { 416 char *core_pattern; 417 bool changed_core_pattern; 418 struct shared_info *info; 419 int shm_id; 420 int ret; 421 pid_t pid; 422 423 ret = setup_core_pattern(&core_pattern, &changed_core_pattern); 424 if (ret) 425 return ret; 426 427 shm_id = shmget(IPC_PRIVATE, sizeof(*info), 0777 | IPC_CREAT); 428 info = shmat(shm_id, NULL, 0); 429 430 ret = init_child_sync(&info->child_sync); 431 if (ret) 432 return ret; 433 434 pid = fork(); 435 if (pid < 0) { 436 perror("fork() failed"); 437 ret = TEST_FAIL; 438 } else if (pid == 0) 439 ret = child(info); 440 else 441 ret = parent(info, pid); 442 443 shmdt(info); 444 445 if (pid) { 446 destroy_child_sync(&info->child_sync); 447 shmctl(shm_id, IPC_RMID, NULL); 448 449 if (changed_core_pattern) 450 write_core_pattern(core_pattern); 451 } 452 453 free(core_pattern); 454 455 return ret; 456 } 457 458 int main(int argc, char *argv[]) 459 { 460 return test_harness(core_pkey, "core_pkey"); 461 } 462