core-pkey.c (10115B)
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 42static const char core_pattern_file[] = "/proc/sys/kernel/core_pattern"; 43 44static const char user_write[] = "[User Write (Running)]"; 45static const char core_read_running[] = "[Core Read (Running)]"; 46 47/* Information shared between the parent and the child. */ 48struct 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 64static 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 69static int sys_pkey_free(int pkey) 70{ 71 return syscall(__NR_pkey_free, pkey); 72} 73 74static 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 110static 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. */ 174static 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 188static 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 195static 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 256static 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); 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 mmaping 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 349static int write_core_pattern(const char *core_pattern) 350{ 351 size_t len = strlen(core_pattern), ret; 352 FILE *f; 353 354 f = fopen(core_pattern_file, "w"); 355 SKIP_IF_MSG(!f, "Try with root privileges"); 356 357 ret = fwrite(core_pattern, 1, len, f); 358 fclose(f); 359 if (ret != len) { 360 perror("Error writing to core_pattern file"); 361 return TEST_FAIL; 362 } 363 364 return TEST_PASS; 365} 366 367static int setup_core_pattern(char **core_pattern_, bool *changed_) 368{ 369 FILE *f; 370 char *core_pattern; 371 int ret; 372 373 core_pattern = malloc(PATH_MAX); 374 if (!core_pattern) { 375 perror("Error allocating memory"); 376 return TEST_FAIL; 377 } 378 379 f = fopen(core_pattern_file, "r"); 380 if (!f) { 381 perror("Error opening core_pattern file"); 382 ret = TEST_FAIL; 383 goto out; 384 } 385 386 ret = fread(core_pattern, 1, PATH_MAX, f); 387 fclose(f); 388 if (!ret) { 389 perror("Error reading core_pattern file"); 390 ret = TEST_FAIL; 391 goto out; 392 } 393 394 /* Check whether we can predict the name of the core file. */ 395 if (!strcmp(core_pattern, "core") || !strcmp(core_pattern, "core.%p")) 396 *changed_ = false; 397 else { 398 ret = write_core_pattern("core-pkey.%p"); 399 if (ret) 400 goto out; 401 402 *changed_ = true; 403 } 404 405 *core_pattern_ = core_pattern; 406 ret = TEST_PASS; 407 408 out: 409 if (ret) 410 free(core_pattern); 411 412 return ret; 413} 414 415static int core_pkey(void) 416{ 417 char *core_pattern; 418 bool changed_core_pattern; 419 struct shared_info *info; 420 int shm_id; 421 int ret; 422 pid_t pid; 423 424 ret = setup_core_pattern(&core_pattern, &changed_core_pattern); 425 if (ret) 426 return ret; 427 428 shm_id = shmget(IPC_PRIVATE, sizeof(*info), 0777 | IPC_CREAT); 429 info = shmat(shm_id, NULL, 0); 430 431 ret = init_child_sync(&info->child_sync); 432 if (ret) 433 return ret; 434 435 pid = fork(); 436 if (pid < 0) { 437 perror("fork() failed"); 438 ret = TEST_FAIL; 439 } else if (pid == 0) 440 ret = child(info); 441 else 442 ret = parent(info, pid); 443 444 shmdt(info); 445 446 if (pid) { 447 destroy_child_sync(&info->child_sync); 448 shmctl(shm_id, IPC_RMID, NULL); 449 450 if (changed_core_pattern) 451 write_core_pattern(core_pattern); 452 } 453 454 free(core_pattern); 455 456 return ret; 457} 458 459int main(int argc, char *argv[]) 460{ 461 return test_harness(core_pkey, "core_pkey"); 462}