Home | History | Annotate | Download | only in ptrace
      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 
     42 static const char core_pattern_file[] = "/proc/sys/kernel/core_pattern";
     43 
     44 static const char user_write[] = "[User Write (Running)]";
     45 static const char core_read_running[] = "[Core Read (Running)]";
     46 
     47 /* Information shared between the parent and the child. */
     48 struct 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 
     64 static 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 
     69 static int sys_pkey_free(int pkey)
     70 {
     71 	return syscall(__NR_pkey_free, pkey);
     72 }
     73 
     74 static 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 
    110 static 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. */
    174 static 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 
    188 static 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 
    195 static 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 
    256 static 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 
    349 static 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 
    370 static 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 
    418 static 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 
    462 int main(int argc, char *argv[])
    463 {
    464 	return test_harness(core_pkey, "core_pkey");
    465 }
    466