root/tools/testing/selftests/powerpc/ptrace/core-pkey.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. sys_pkey_alloc
  2. sys_pkey_free
  3. increase_core_file_limit
  4. child
  5. try_core_file
  6. next_note
  7. check_core_file
  8. parent
  9. write_core_pattern
  10. setup_core_pattern
  11. core_pkey
  12. main

   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         mtspr(SPRN_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);
 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 
 349 static 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 
 367 static 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 
 415 static 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 
 459 int main(int argc, char *argv[])
 460 {
 461         return test_harness(core_pkey, "core_pkey");
 462 }

/* [<][>][^][v][top][bottom][index][help] */