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