linux/tools/testing/selftests/powerpc/ptrace/core-pkey.c
<<
>>
Prefs
   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        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. */
 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}
 463