123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * Ptrace test for Memory Protection Key registers
- *
- * Copyright (C) 2015 Anshuman Khandual, IBM Corporation.
- * Copyright (C) 2018 IBM Corporation.
- */
- #include <limits.h>
- #include <linux/kernel.h>
- #include <sys/mman.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <sys/time.h>
- #include <sys/resource.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include "ptrace.h"
- #include "child.h"
- #ifndef __NR_pkey_alloc
- #define __NR_pkey_alloc 384
- #endif
- #ifndef __NR_pkey_free
- #define __NR_pkey_free 385
- #endif
- #ifndef NT_PPC_PKEY
- #define NT_PPC_PKEY 0x110
- #endif
- #ifndef PKEY_DISABLE_EXECUTE
- #define PKEY_DISABLE_EXECUTE 0x4
- #endif
- #define AMR_BITS_PER_PKEY 2
- #define PKEY_REG_BITS (sizeof(u64) * 8)
- #define pkeyshift(pkey) (PKEY_REG_BITS - ((pkey + 1) * AMR_BITS_PER_PKEY))
- #define CORE_FILE_LIMIT (5 * 1024 * 1024) /* 5 MB should be enough */
- static const char core_pattern_file[] = "/proc/sys/kernel/core_pattern";
- static const char user_write[] = "[User Write (Running)]";
- static const char core_read_running[] = "[Core Read (Running)]";
- /* Information shared between the parent and the child. */
- struct shared_info {
- struct child_sync child_sync;
- /* AMR value the parent expects to read in the core file. */
- unsigned long amr;
- /* IAMR value the parent expects to read in the core file. */
- unsigned long iamr;
- /* UAMOR value the parent expects to read in the core file. */
- unsigned long uamor;
- /* When the child crashed. */
- time_t core_time;
- };
- static int sys_pkey_alloc(unsigned long flags, unsigned long init_access_rights)
- {
- return syscall(__NR_pkey_alloc, flags, init_access_rights);
- }
- static int sys_pkey_free(int pkey)
- {
- return syscall(__NR_pkey_free, pkey);
- }
- static int increase_core_file_limit(void)
- {
- struct rlimit rlim;
- int ret;
- ret = getrlimit(RLIMIT_CORE, &rlim);
- FAIL_IF(ret);
- if (rlim.rlim_cur != RLIM_INFINITY && rlim.rlim_cur < CORE_FILE_LIMIT) {
- rlim.rlim_cur = CORE_FILE_LIMIT;
- if (rlim.rlim_max != RLIM_INFINITY &&
- rlim.rlim_max < CORE_FILE_LIMIT)
- rlim.rlim_max = CORE_FILE_LIMIT;
- ret = setrlimit(RLIMIT_CORE, &rlim);
- FAIL_IF(ret);
- }
- ret = getrlimit(RLIMIT_FSIZE, &rlim);
- FAIL_IF(ret);
- if (rlim.rlim_cur != RLIM_INFINITY && rlim.rlim_cur < CORE_FILE_LIMIT) {
- rlim.rlim_cur = CORE_FILE_LIMIT;
- if (rlim.rlim_max != RLIM_INFINITY &&
- rlim.rlim_max < CORE_FILE_LIMIT)
- rlim.rlim_max = CORE_FILE_LIMIT;
- ret = setrlimit(RLIMIT_FSIZE, &rlim);
- FAIL_IF(ret);
- }
- return TEST_PASS;
- }
- static int child(struct shared_info *info)
- {
- bool disable_execute = true;
- int pkey1, pkey2, pkey3;
- int *ptr, ret;
- /* Wait until parent fills out the initial register values. */
- ret = wait_parent(&info->child_sync);
- if (ret)
- return ret;
- ret = increase_core_file_limit();
- FAIL_IF(ret);
- /* Get some pkeys so that we can change their bits in the AMR. */
- pkey1 = sys_pkey_alloc(0, PKEY_DISABLE_EXECUTE);
- if (pkey1 < 0) {
- pkey1 = sys_pkey_alloc(0, 0);
- FAIL_IF(pkey1 < 0);
- disable_execute = false;
- }
- pkey2 = sys_pkey_alloc(0, 0);
- FAIL_IF(pkey2 < 0);
- pkey3 = sys_pkey_alloc(0, 0);
- FAIL_IF(pkey3 < 0);
- info->amr |= 3ul << pkeyshift(pkey1) | 2ul << pkeyshift(pkey2);
- if (disable_execute)
- info->iamr |= 1ul << pkeyshift(pkey1);
- info->uamor |= 3ul << pkeyshift(pkey1) | 3ul << pkeyshift(pkey2);
- printf("%-30s AMR: %016lx pkey1: %d pkey2: %d pkey3: %d\n",
- user_write, info->amr, pkey1, pkey2, pkey3);
- mtspr(SPRN_AMR, info->amr);
- /*
- * We won't use pkey3. This tests whether the kernel restores the UAMOR
- * permissions after a key is freed.
- */
- sys_pkey_free(pkey3);
- info->core_time = time(NULL);
- /* Crash. */
- ptr = 0;
- *ptr = 1;
- /* Shouldn't get here. */
- FAIL_IF(true);
- return TEST_FAIL;
- }
- /* Return file size if filename exists and pass sanity check, or zero if not. */
- static off_t try_core_file(const char *filename, struct shared_info *info,
- pid_t pid)
- {
- struct stat buf;
- int ret;
- ret = stat(filename, &buf);
- if (ret == -1)
- return TEST_FAIL;
- /* Make sure we're not using a stale core file. */
- return buf.st_mtime >= info->core_time ? buf.st_size : TEST_FAIL;
- }
- static Elf64_Nhdr *next_note(Elf64_Nhdr *nhdr)
- {
- return (void *) nhdr + sizeof(*nhdr) +
- __ALIGN_KERNEL(nhdr->n_namesz, 4) +
- __ALIGN_KERNEL(nhdr->n_descsz, 4);
- }
- static int check_core_file(struct shared_info *info, Elf64_Ehdr *ehdr,
- off_t core_size)
- {
- unsigned long *regs;
- Elf64_Phdr *phdr;
- Elf64_Nhdr *nhdr;
- size_t phdr_size;
- void *p = ehdr, *note;
- int ret;
- ret = memcmp(ehdr->e_ident, ELFMAG, SELFMAG);
- FAIL_IF(ret);
- FAIL_IF(ehdr->e_type != ET_CORE);
- FAIL_IF(ehdr->e_machine != EM_PPC64);
- FAIL_IF(ehdr->e_phoff == 0 || ehdr->e_phnum == 0);
- /*
- * e_phnum is at most 65535 so calculating the size of the
- * program header cannot overflow.
- */
- phdr_size = sizeof(*phdr) * ehdr->e_phnum;
- /* Sanity check the program header table location. */
- FAIL_IF(ehdr->e_phoff + phdr_size < ehdr->e_phoff);
- FAIL_IF(ehdr->e_phoff + phdr_size > core_size);
- /* Find the PT_NOTE segment. */
- for (phdr = p + ehdr->e_phoff;
- (void *) phdr < p + ehdr->e_phoff + phdr_size;
- phdr += ehdr->e_phentsize)
- if (phdr->p_type == PT_NOTE)
- break;
- FAIL_IF((void *) phdr >= p + ehdr->e_phoff + phdr_size);
- /* Find the NT_PPC_PKEY note. */
- for (nhdr = p + phdr->p_offset;
- (void *) nhdr < p + phdr->p_offset + phdr->p_filesz;
- nhdr = next_note(nhdr))
- if (nhdr->n_type == NT_PPC_PKEY)
- break;
- FAIL_IF((void *) nhdr >= p + phdr->p_offset + phdr->p_filesz);
- FAIL_IF(nhdr->n_descsz == 0);
- p = nhdr;
- note = p + sizeof(*nhdr) + __ALIGN_KERNEL(nhdr->n_namesz, 4);
- regs = (unsigned long *) note;
- printf("%-30s AMR: %016lx IAMR: %016lx UAMOR: %016lx\n",
- core_read_running, regs[0], regs[1], regs[2]);
- FAIL_IF(regs[0] != info->amr);
- FAIL_IF(regs[1] != info->iamr);
- FAIL_IF(regs[2] != info->uamor);
- return TEST_PASS;
- }
- static int parent(struct shared_info *info, pid_t pid)
- {
- char *filenames, *filename[3];
- int fd, i, ret, status;
- unsigned long regs[3];
- off_t core_size;
- void *core;
- /*
- * Get the initial values for AMR, IAMR and UAMOR and communicate them
- * to the child.
- */
- ret = ptrace_read_regs(pid, NT_PPC_PKEY, regs, 3);
- PARENT_SKIP_IF_UNSUPPORTED(ret, &info->child_sync);
- PARENT_FAIL_IF(ret, &info->child_sync);
- info->amr = regs[0];
- info->iamr = regs[1];
- info->uamor = regs[2];
- /* Wake up child so that it can set itself up. */
- ret = prod_child(&info->child_sync);
- PARENT_FAIL_IF(ret, &info->child_sync);
- ret = wait(&status);
- if (ret != pid) {
- printf("Child's exit status not captured\n");
- return TEST_FAIL;
- } else if (!WIFSIGNALED(status) || !WCOREDUMP(status)) {
- printf("Child didn't dump core\n");
- return TEST_FAIL;
- }
- /* Construct array of core file names to try. */
- filename[0] = filenames = malloc(PATH_MAX);
- if (!filenames) {
- perror("Error allocating memory");
- return TEST_FAIL;
- }
- ret = snprintf(filename[0], PATH_MAX, "core-pkey.%d", pid);
- if (ret < 0 || ret >= PATH_MAX) {
- ret = TEST_FAIL;
- goto out;
- }
- filename[1] = filename[0] + ret + 1;
- ret = snprintf(filename[1], PATH_MAX - ret - 1, "core.%d", pid);
- if (ret < 0 || ret >= PATH_MAX - ret - 1) {
- ret = TEST_FAIL;
- goto out;
- }
- filename[2] = "core";
- for (i = 0; i < 3; i++) {
- core_size = try_core_file(filename[i], info, pid);
- if (core_size != TEST_FAIL)
- break;
- }
- if (i == 3) {
- printf("Couldn't find core file\n");
- ret = TEST_FAIL;
- goto out;
- }
- fd = open(filename[i], O_RDONLY);
- if (fd == -1) {
- perror("Error opening core file");
- ret = TEST_FAIL;
- goto out;
- }
- core = mmap(NULL, core_size, PROT_READ, MAP_PRIVATE, fd, 0);
- if (core == (void *) -1) {
- perror("Error mmaping core file");
- ret = TEST_FAIL;
- goto out;
- }
- ret = check_core_file(info, core, core_size);
- munmap(core, core_size);
- close(fd);
- unlink(filename[i]);
- out:
- free(filenames);
- return ret;
- }
- static int write_core_pattern(const char *core_pattern)
- {
- size_t len = strlen(core_pattern), ret;
- FILE *f;
- f = fopen(core_pattern_file, "w");
- if (!f) {
- perror("Error writing to core_pattern file");
- return TEST_FAIL;
- }
- ret = fwrite(core_pattern, 1, len, f);
- fclose(f);
- if (ret != len) {
- perror("Error writing to core_pattern file");
- return TEST_FAIL;
- }
- return TEST_PASS;
- }
- static int setup_core_pattern(char **core_pattern_, bool *changed_)
- {
- FILE *f;
- char *core_pattern;
- int ret;
- core_pattern = malloc(PATH_MAX);
- if (!core_pattern) {
- perror("Error allocating memory");
- return TEST_FAIL;
- }
- f = fopen(core_pattern_file, "r");
- if (!f) {
- perror("Error opening core_pattern file");
- ret = TEST_FAIL;
- goto out;
- }
- ret = fread(core_pattern, 1, PATH_MAX, f);
- fclose(f);
- if (!ret) {
- perror("Error reading core_pattern file");
- ret = TEST_FAIL;
- goto out;
- }
- /* Check whether we can predict the name of the core file. */
- if (!strcmp(core_pattern, "core") || !strcmp(core_pattern, "core.%p"))
- *changed_ = false;
- else {
- ret = write_core_pattern("core-pkey.%p");
- if (ret)
- goto out;
- *changed_ = true;
- }
- *core_pattern_ = core_pattern;
- ret = TEST_PASS;
- out:
- if (ret)
- free(core_pattern);
- return ret;
- }
- static int core_pkey(void)
- {
- char *core_pattern;
- bool changed_core_pattern;
- struct shared_info *info;
- int shm_id;
- int ret;
- pid_t pid;
- ret = setup_core_pattern(&core_pattern, &changed_core_pattern);
- if (ret)
- return ret;
- shm_id = shmget(IPC_PRIVATE, sizeof(*info), 0777 | IPC_CREAT);
- info = shmat(shm_id, NULL, 0);
- ret = init_child_sync(&info->child_sync);
- if (ret)
- return ret;
- pid = fork();
- if (pid < 0) {
- perror("fork() failed");
- ret = TEST_FAIL;
- } else if (pid == 0)
- ret = child(info);
- else
- ret = parent(info, pid);
- shmdt(info);
- if (pid) {
- destroy_child_sync(&info->child_sync);
- shmctl(shm_id, IPC_RMID, NULL);
- if (changed_core_pattern)
- write_core_pattern(core_pattern);
- }
- free(core_pattern);
- return ret;
- }
- int main(int argc, char *argv[])
- {
- return test_harness(core_pkey, "core_pkey");
- }
|