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 else 144 info->iamr &= ~(1ul << pkeyshift(pkey1)); 145 146 info->iamr &= ~(1ul << pkeyshift(pkey2) | 1ul << pkeyshift(pkey3)); 147 148 info->uamor |= 3ul << pkeyshift(pkey1) | 3ul << pkeyshift(pkey2); 149 150 printf("%-30s AMR: %016lx pkey1: %d pkey2: %d pkey3: %d\n", 151 user_write, info->amr, pkey1, pkey2, pkey3); 152 153 set_amr(info->amr); 154 155 /* 156 * We won't use pkey3. This tests whether the kernel restores the UAMOR 157 * permissions after a key is freed. 158 */ 159 sys_pkey_free(pkey3); 160 161 info->core_time = time(NULL); 162 163 /* Crash. */ 164 ptr = 0; 165 *ptr = 1; 166 167 /* Shouldn't get here. */ 168 FAIL_IF(true); 169 170 return TEST_FAIL; 171 } 172 173 /* Return file size if filename exists and pass sanity check, or zero if not. */ 174 static off_t try_core_file(const char *filename, struct shared_info *info, 175 pid_t pid) 176 { 177 struct stat buf; 178 int ret; 179 180 ret = stat(filename, &buf); 181 if (ret == -1) 182 return TEST_FAIL; 183 184 /* Make sure we're not using a stale core file. */ 185 return buf.st_mtime >= info->core_time ? buf.st_size : TEST_FAIL; 186 } 187 188 static Elf64_Nhdr *next_note(Elf64_Nhdr *nhdr) 189 { 190 return (void *) nhdr + sizeof(*nhdr) + 191 __ALIGN_KERNEL(nhdr->n_namesz, 4) + 192 __ALIGN_KERNEL(nhdr->n_descsz, 4); 193 } 194 195 static int check_core_file(struct shared_info *info, Elf64_Ehdr *ehdr, 196 off_t core_size) 197 { 198 unsigned long *regs; 199 Elf64_Phdr *phdr; 200 Elf64_Nhdr *nhdr; 201 size_t phdr_size; 202 void *p = ehdr, *note; 203 int ret; 204 205 ret = memcmp(ehdr->e_ident, ELFMAG, SELFMAG); 206 FAIL_IF(ret); 207 208 FAIL_IF(ehdr->e_type != ET_CORE); 209 FAIL_IF(ehdr->e_machine != EM_PPC64); 210 FAIL_IF(ehdr->e_phoff == 0 || ehdr->e_phnum == 0); 211 212 /* 213 * e_phnum is at most 65535 so calculating the size of the 214 * program header cannot overflow. 215 */ 216 phdr_size = sizeof(*phdr) * ehdr->e_phnum; 217 218 /* Sanity check the program header table location. */ 219 FAIL_IF(ehdr->e_phoff + phdr_size < ehdr->e_phoff); 220 FAIL_IF(ehdr->e_phoff + phdr_size > core_size); 221 222 /* Find the PT_NOTE segment. */ 223 for (phdr = p + ehdr->e_phoff; 224 (void *) phdr < p + ehdr->e_phoff + phdr_size; 225 phdr += ehdr->e_phentsize) 226 if (phdr->p_type == PT_NOTE) 227 break; 228 229 FAIL_IF((void *) phdr >= p + ehdr->e_phoff + phdr_size); 230 231 /* Find the NT_PPC_PKEY note. */ 232 for (nhdr = p + phdr->p_offset; 233 (void *) nhdr < p + phdr->p_offset + phdr->p_filesz; 234 nhdr = next_note(nhdr)) 235 if (nhdr->n_type == NT_PPC_PKEY) 236 break; 237 238 FAIL_IF((void *) nhdr >= p + phdr->p_offset + phdr->p_filesz); 239 FAIL_IF(nhdr->n_descsz == 0); 240 241 p = nhdr; 242 note = p + sizeof(*nhdr) + __ALIGN_KERNEL(nhdr->n_namesz, 4); 243 244 regs = (unsigned long *) note; 245 246 printf("%-30s AMR: %016lx IAMR: %016lx UAMOR: %016lx\n", 247 core_read_running, regs[0], regs[1], regs[2]); 248 249 FAIL_IF(regs[0] != info->amr); 250 FAIL_IF(regs[1] != info->iamr); 251 FAIL_IF(regs[2] != info->uamor); 252 253 return TEST_PASS; 254 } 255 256 static int parent(struct shared_info *info, pid_t pid) 257 { 258 char *filenames, *filename[3]; 259 int fd, i, ret, status; 260 unsigned long regs[3]; 261 off_t core_size; 262 void *core; 263 264 /* 265 * Get the initial values for AMR, IAMR and UAMOR and communicate them 266 * to the child. 267 */ 268 ret = ptrace_read_regs(pid, NT_PPC_PKEY, regs, 3); 269 PARENT_SKIP_IF_UNSUPPORTED(ret, &info->child_sync, "PKEYs not supported"); 270 PARENT_FAIL_IF(ret, &info->child_sync); 271 272 info->amr = regs[0]; 273 info->iamr = regs[1]; 274 info->uamor = regs[2]; 275 276 /* Wake up child so that it can set itself up. */ 277 ret = prod_child(&info->child_sync); 278 PARENT_FAIL_IF(ret, &info->child_sync); 279 280 ret = wait(&status); 281 if (ret != pid) { 282 printf("Child's exit status not captured\n"); 283 return TEST_FAIL; 284 } else if (!WIFSIGNALED(status) || !WCOREDUMP(status)) { 285 printf("Child didn't dump core\n"); 286 return TEST_FAIL; 287 } 288 289 /* Construct array of core file names to try. */ 290 291 filename[0] = filenames = malloc(PATH_MAX); 292 if (!filenames) { 293 perror("Error allocating memory"); 294 return TEST_FAIL; 295 } 296 297 ret = snprintf(filename[0], PATH_MAX, "core-pkey.%d", pid); 298 if (ret < 0 || ret >= PATH_MAX) { 299 ret = TEST_FAIL; 300 goto out; 301 } 302 303 filename[1] = filename[0] + ret + 1; 304 ret = snprintf(filename[1], PATH_MAX - ret - 1, "core.%d", pid); 305 if (ret < 0 || ret >= PATH_MAX - ret - 1) { 306 ret = TEST_FAIL; 307 goto out; 308 } 309 filename[2] = "core"; 310 311 for (i = 0; i < 3; i++) { 312 core_size = try_core_file(filename[i], info, pid); 313 if (core_size != TEST_FAIL) 314 break; 315 } 316 317 if (i == 3) { 318 printf("Couldn't find core file\n"); 319 ret = TEST_FAIL; 320 goto out; 321 } 322 323 fd = open(filename[i], O_RDONLY); 324 if (fd == -1) { 325 perror("Error opening core file"); 326 ret = TEST_FAIL; 327 goto out; 328 } 329 330 core = mmap(NULL, core_size, PROT_READ, MAP_PRIVATE, fd, 0); 331 if (core == (void *) -1) { 332 perror("Error mmapping core file"); 333 ret = TEST_FAIL; 334 goto out; 335 } 336 337 ret = check_core_file(info, core, core_size); 338 339 munmap(core, core_size); 340 close(fd); 341 unlink(filename[i]); 342 343 out: 344 free(filenames); 345 346 return ret; 347 } 348 349 static int write_core_pattern(const char *core_pattern) 350 { 351 int err; 352 353 err = write_file(core_pattern_file, core_pattern, strlen(core_pattern)); 354 if (err) { 355 SKIP_IF_MSG(err == -EPERM, "Try with root privileges"); 356 perror("Error writing to core_pattern file"); 357 return TEST_FAIL; 358 } 359 360 return TEST_PASS; 361 } 362 363 static int setup_core_pattern(char **core_pattern_, bool *changed_) 364 { 365 char *core_pattern; 366 size_t len; 367 int ret; 368 369 core_pattern = malloc(PATH_MAX); 370 if (!core_pattern) { 371 perror("Error allocating memory"); 372 return TEST_FAIL; 373 } 374 375 ret = read_file(core_pattern_file, core_pattern, PATH_MAX - 1, &len); 376 if (ret) { 377 perror("Error reading core_pattern file"); 378 ret = TEST_FAIL; 379 goto out; 380 } 381 382 core_pattern[len] = '\0'; 383 384 /* Check whether we can predict the name of the core file. */ 385 if (!strcmp(core_pattern, "core") || !strcmp(core_pattern, "core.%p")) 386 *changed_ = false; 387 else { 388 ret = write_core_pattern("core-pkey.%p"); 389 if (ret) 390 goto out; 391 392 *changed_ = true; 393 } 394 395 *core_pattern_ = core_pattern; 396 ret = TEST_PASS; 397 398 out: 399 if (ret) 400 free(core_pattern); 401 402 return ret; 403 } 404 405 static int core_pkey(void) 406 { 407 char *core_pattern; 408 bool changed_core_pattern; 409 struct shared_info *info; 410 int shm_id; 411 int ret; 412 pid_t pid; 413 414 ret = setup_core_pattern(&core_pattern, &changed_core_pattern); 415 if (ret) 416 return ret; 417 418 shm_id = shmget(IPC_PRIVATE, sizeof(*info), 0777 | IPC_CREAT); 419 info = shmat(shm_id, NULL, 0); 420 421 ret = init_child_sync(&info->child_sync); 422 if (ret) 423 return ret; 424 425 pid = fork(); 426 if (pid < 0) { 427 perror("fork() failed"); 428 ret = TEST_FAIL; 429 } else if (pid == 0) 430 ret = child(info); 431 else 432 ret = parent(info, pid); 433 434 shmdt(info); 435 436 if (pid) { 437 destroy_child_sync(&info->child_sync); 438 shmctl(shm_id, IPC_RMID, NULL); 439 440 if (changed_core_pattern) 441 write_core_pattern(core_pattern); 442 } 443 444 free(core_pattern); 445 446 return ret; 447 } 448 449 int main(int argc, char *argv[]) 450 { 451 return test_harness(core_pkey, "core_pkey"); 452 } 453
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.