cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

core-pkey.c (10115B)


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