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 	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. */
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 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 
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 	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 
367 static 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 - 1, f);
387 	fclose(f);
388 	if (!ret) {
389 		perror("Error reading core_pattern file");
390 		ret = TEST_FAIL;
391 		goto out;
392 	}
393 
394 	core_pattern[ret] = '\0';
395 
396 	/* Check whether we can predict the name of the core file. */
397 	if (!strcmp(core_pattern, "core") || !strcmp(core_pattern, "core.%p"))
398 		*changed_ = false;
399 	else {
400 		ret = write_core_pattern("core-pkey.%p");
401 		if (ret)
402 			goto out;
403 
404 		*changed_ = true;
405 	}
406 
407 	*core_pattern_ = core_pattern;
408 	ret = TEST_PASS;
409 
410  out:
411 	if (ret)
412 		free(core_pattern);
413 
414 	return ret;
415 }
416 
417 static int core_pkey(void)
418 {
419 	char *core_pattern;
420 	bool changed_core_pattern;
421 	struct shared_info *info;
422 	int shm_id;
423 	int ret;
424 	pid_t pid;
425 
426 	ret = setup_core_pattern(&core_pattern, &changed_core_pattern);
427 	if (ret)
428 		return ret;
429 
430 	shm_id = shmget(IPC_PRIVATE, sizeof(*info), 0777 | IPC_CREAT);
431 	info = shmat(shm_id, NULL, 0);
432 
433 	ret = init_child_sync(&info->child_sync);
434 	if (ret)
435 		return ret;
436 
437 	pid = fork();
438 	if (pid < 0) {
439 		perror("fork() failed");
440 		ret = TEST_FAIL;
441 	} else if (pid == 0)
442 		ret = child(info);
443 	else
444 		ret = parent(info, pid);
445 
446 	shmdt(info);
447 
448 	if (pid) {
449 		destroy_child_sync(&info->child_sync);
450 		shmctl(shm_id, IPC_RMID, NULL);
451 
452 		if (changed_core_pattern)
453 			write_core_pattern(core_pattern);
454 	}
455 
456 	free(core_pattern);
457 
458 	return ret;
459 }
460 
461 int main(int argc, char *argv[])
462 {
463 	return test_harness(core_pkey, "core_pkey");
464 }
465