cregit-Linux how code gets into the kernel

Release 4.18 tools/testing/selftests/powerpc/ptrace/core-pkey.c

// 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); }

Contributors

PersonTokensPropCommitsCommitProp
Thiago Jung Bauermann24100.00%1100.00%
Total24100.00%1100.00%


static int sys_pkey_free(int pkey) { return syscall(__NR_pkey_free, pkey); }

Contributors

PersonTokensPropCommitsCommitProp
Thiago Jung Bauermann17100.00%1100.00%
Total17100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Thiago Jung Bauermann162100.00%1100.00%
Total162100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Thiago Jung Bauermann238100.00%1100.00%
Total238100.00%1100.00%

/* 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; }

Contributors

PersonTokensPropCommitsCommitProp
Thiago Jung Bauermann62100.00%1100.00%
Total62100.00%1100.00%


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); }

Contributors

PersonTokensPropCommitsCommitProp
Thiago Jung Bauermann42100.00%1100.00%
Total42100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Thiago Jung Bauermann356100.00%1100.00%
Total356100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Thiago Jung Bauermann477100.00%1100.00%
Total477100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Thiago Jung Bauermann86100.00%1100.00%
Total86100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Thiago Jung Bauermann182100.00%1100.00%
Total182100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Thiago Jung Bauermann183100.00%1100.00%
Total183100.00%1100.00%


int main(int argc, char *argv[]) { return test_harness(core_pkey, "core_pkey"); }

Contributors

PersonTokensPropCommitsCommitProp
Thiago Jung Bauermann21100.00%1100.00%
Total21100.00%1100.00%


Overall Contributors

PersonTokensPropCommitsCommitProp
Thiago Jung Bauermann1995100.00%1100.00%
Total1995100.00%1100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.