brintos

brintos / linux-shallow public Read only

0
0
Text · 9.8 KiB · f6da4cb Raw
453 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Ptrace test for Memory Protection Key registers4 *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_alloc21#define __NR_pkey_alloc		38422#endif23 24#ifndef __NR_pkey_free25#define __NR_pkey_free		38526#endif27 28#ifndef NT_PPC_PKEY29#define NT_PPC_PKEY		0x11030#endif31 32#ifndef PKEY_DISABLE_EXECUTE33#define PKEY_DISABLE_EXECUTE	0x434#endif35 36#define AMR_BITS_PER_PKEY 237#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	else144		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 UAMOR157	 * 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 the214	 * 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 them266	 * to the child.267	 */268	ret = ptrace_read_regs(pid, NT_PPC_PKEY, regs, 3);269	PARENT_SKIP_IF_UNSUPPORTED(ret, &info->child_sync, "PKEYs not supported");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 mmapping 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	int err;352 353	err = write_file(core_pattern_file, core_pattern, strlen(core_pattern));354	if (err) {355		SKIP_IF_MSG(err == -EPERM, "Try with root privileges");356		perror("Error writing to core_pattern file");357		return TEST_FAIL;358	}359 360	return TEST_PASS;361}362 363static int setup_core_pattern(char **core_pattern_, bool *changed_)364{365	char *core_pattern;366	size_t len;367	int ret;368 369	core_pattern = malloc(PATH_MAX);370	if (!core_pattern) {371		perror("Error allocating memory");372		return TEST_FAIL;373	}374 375	ret = read_file(core_pattern_file, core_pattern, PATH_MAX - 1, &len);376	if (ret) {377		perror("Error reading core_pattern file");378		ret = TEST_FAIL;379		goto out;380	}381 382	core_pattern[len] = '\0';383 384	/* Check whether we can predict the name of the core file. */385	if (!strcmp(core_pattern, "core") || !strcmp(core_pattern, "core.%p"))386		*changed_ = false;387	else {388		ret = write_core_pattern("core-pkey.%p");389		if (ret)390			goto out;391 392		*changed_ = true;393	}394 395	*core_pattern_ = core_pattern;396	ret = TEST_PASS;397 398 out:399	if (ret)400		free(core_pattern);401 402	return ret;403}404 405static int core_pkey(void)406{407	char *core_pattern;408	bool changed_core_pattern;409	struct shared_info *info;410	int shm_id;411	int ret;412	pid_t pid;413 414	ret = setup_core_pattern(&core_pattern, &changed_core_pattern);415	if (ret)416		return ret;417 418	shm_id = shmget(IPC_PRIVATE, sizeof(*info), 0777 | IPC_CREAT);419	info = shmat(shm_id, NULL, 0);420 421	ret = init_child_sync(&info->child_sync);422	if (ret)423		return ret;424 425	pid = fork();426	if (pid < 0) {427		perror("fork() failed");428		ret = TEST_FAIL;429	} else if (pid == 0)430		ret = child(info);431	else432		ret = parent(info, pid);433 434	shmdt(info);435 436	if (pid) {437		destroy_child_sync(&info->child_sync);438		shmctl(shm_id, IPC_RMID, NULL);439 440		if (changed_core_pattern)441			write_core_pattern(core_pattern);442	}443 444	free(core_pattern);445 446	return ret;447}448 449int main(int argc, char *argv[])450{451	return test_harness(core_pkey, "core_pkey");452}453