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        if (!f) {
 356                perror("Error writing to core_pattern file");
 357                return TEST_FAIL;
 358        }
 359
 360        ret = fwrite(core_pattern, 1, len, f);
 361        fclose(f);
 362        if (ret != len) {
 363                perror("Error writing to core_pattern file");
 364                return TEST_FAIL;
 365        }
 366
 367        return TEST_PASS;
 368}
 369
 370static int setup_core_pattern(char **core_pattern_, bool *changed_)
 371{
 372        FILE *f;
 373        char *core_pattern;
 374        int ret;
 375
 376        core_pattern = malloc(PATH_MAX);
 377        if (!core_pattern) {
 378                perror("Error allocating memory");
 379                return TEST_FAIL;
 380        }
 381
 382        f = fopen(core_pattern_file, "r");
 383        if (!f) {
 384                perror("Error opening core_pattern file");
 385                ret = TEST_FAIL;
 386                goto out;
 387        }
 388
 389        ret = fread(core_pattern, 1, PATH_MAX, f);
 390        fclose(f);
 391        if (!ret) {
 392                perror("Error reading core_pattern file");
 393                ret = TEST_FAIL;
 394                goto out;
 395        }
 396
 397        /* Check whether we can predict the name of the core file. */
 398        if (!strcmp(core_pattern, "core") || !strcmp(core_pattern, "core.%p"))
 399                *changed_ = false;
 400        else {
 401                ret = write_core_pattern("core-pkey.%p");
 402                if (ret)
 403                        goto out;
 404
 405                *changed_ = true;
 406        }
 407
 408        *core_pattern_ = core_pattern;
 409        ret = TEST_PASS;
 410
 411 out:
 412        if (ret)
 413                free(core_pattern);
 414
 415        return ret;
 416}
 417
 418static int core_pkey(void)
 419{
 420        char *core_pattern;
 421        bool changed_core_pattern;
 422        struct shared_info *info;
 423        int shm_id;
 424        int ret;
 425        pid_t pid;
 426
 427        ret = setup_core_pattern(&core_pattern, &changed_core_pattern);
 428        if (ret)
 429                return ret;
 430
 431        shm_id = shmget(IPC_PRIVATE, sizeof(*info), 0777 | IPC_CREAT);
 432        info = shmat(shm_id, NULL, 0);
 433
 434        ret = init_child_sync(&info->child_sync);
 435        if (ret)
 436                return ret;
 437
 438        pid = fork();
 439        if (pid < 0) {
 440                perror("fork() failed");
 441                ret = TEST_FAIL;
 442        } else if (pid == 0)
 443                ret = child(info);
 444        else
 445                ret = parent(info, pid);
 446
 447        shmdt(info);
 448
 449        if (pid) {
 450                destroy_child_sync(&info->child_sync);
 451                shmctl(shm_id, IPC_RMID, NULL);
 452
 453                if (changed_core_pattern)
 454                        write_core_pattern(core_pattern);
 455        }
 456
 457        free(core_pattern);
 458
 459        return ret;
 460}
 461
 462int main(int argc, char *argv[])
 463{
 464        return test_harness(core_pkey, "core_pkey");
 465}
 466