xref: /linux/tools/testing/selftests/powerpc/ptrace/core-pkey.c (revision 95ec54a420b8f445e04a7ca0ea8deb72c51fe1d3)
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 #include "pkeys.h"
20 
21 #define CORE_FILE_LIMIT	(5 * 1024 * 1024)	/* 5 MB should be enough */
22 
23 static const char core_pattern_file[] = "/proc/sys/kernel/core_pattern";
24 
25 static const char user_write[] = "[User Write (Running)]";
26 static const char core_read_running[] = "[Core Read (Running)]";
27 
28 /* Information shared between the parent and the child. */
29 struct shared_info {
30 	struct child_sync child_sync;
31 
32 	/* AMR value the parent expects to read in the core file. */
33 	unsigned long amr;
34 
35 	/* IAMR value the parent expects to read in the core file. */
36 	unsigned long iamr;
37 
38 	/* UAMOR value the parent expects to read in the core file. */
39 	unsigned long uamor;
40 
41 	/* When the child crashed. */
42 	time_t core_time;
43 };
44 
45 static int increase_core_file_limit(void)
46 {
47 	struct rlimit rlim;
48 	int ret;
49 
50 	ret = getrlimit(RLIMIT_CORE, &rlim);
51 	FAIL_IF(ret);
52 
53 	if (rlim.rlim_cur != RLIM_INFINITY && rlim.rlim_cur < CORE_FILE_LIMIT) {
54 		rlim.rlim_cur = CORE_FILE_LIMIT;
55 
56 		if (rlim.rlim_max != RLIM_INFINITY &&
57 		    rlim.rlim_max < CORE_FILE_LIMIT)
58 			rlim.rlim_max = CORE_FILE_LIMIT;
59 
60 		ret = setrlimit(RLIMIT_CORE, &rlim);
61 		FAIL_IF(ret);
62 	}
63 
64 	ret = getrlimit(RLIMIT_FSIZE, &rlim);
65 	FAIL_IF(ret);
66 
67 	if (rlim.rlim_cur != RLIM_INFINITY && rlim.rlim_cur < CORE_FILE_LIMIT) {
68 		rlim.rlim_cur = CORE_FILE_LIMIT;
69 
70 		if (rlim.rlim_max != RLIM_INFINITY &&
71 		    rlim.rlim_max < CORE_FILE_LIMIT)
72 			rlim.rlim_max = CORE_FILE_LIMIT;
73 
74 		ret = setrlimit(RLIMIT_FSIZE, &rlim);
75 		FAIL_IF(ret);
76 	}
77 
78 	return TEST_PASS;
79 }
80 
81 static int child(struct shared_info *info)
82 {
83 	bool disable_execute = true;
84 	int pkey1, pkey2, pkey3;
85 	int *ptr, ret;
86 
87 	/* Wait until parent fills out the initial register values. */
88 	ret = wait_parent(&info->child_sync);
89 	if (ret)
90 		return ret;
91 
92 	ret = increase_core_file_limit();
93 	FAIL_IF(ret);
94 
95 	/* Get some pkeys so that we can change their bits in the AMR. */
96 	pkey1 = sys_pkey_alloc(0, PKEY_DISABLE_EXECUTE);
97 	if (pkey1 < 0) {
98 		pkey1 = sys_pkey_alloc(0, 0);
99 		FAIL_IF(pkey1 < 0);
100 
101 		disable_execute = false;
102 	}
103 
104 	pkey2 = sys_pkey_alloc(0, 0);
105 	FAIL_IF(pkey2 < 0);
106 
107 	pkey3 = sys_pkey_alloc(0, 0);
108 	FAIL_IF(pkey3 < 0);
109 
110 	info->amr |= 3ul << pkeyshift(pkey1) | 2ul << pkeyshift(pkey2);
111 
112 	if (disable_execute)
113 		info->iamr |= 1ul << pkeyshift(pkey1);
114 	else
115 		info->iamr &= ~(1ul << pkeyshift(pkey1));
116 
117 	info->iamr &= ~(1ul << pkeyshift(pkey2) | 1ul << pkeyshift(pkey3));
118 
119 	info->uamor |= 3ul << pkeyshift(pkey1) | 3ul << pkeyshift(pkey2);
120 
121 	printf("%-30s AMR: %016lx pkey1: %d pkey2: %d pkey3: %d\n",
122 	       user_write, info->amr, pkey1, pkey2, pkey3);
123 
124 	set_amr(info->amr);
125 
126 	/*
127 	 * We won't use pkey3. This tests whether the kernel restores the UAMOR
128 	 * permissions after a key is freed.
129 	 */
130 	sys_pkey_free(pkey3);
131 
132 	info->core_time = time(NULL);
133 
134 	/* Crash. */
135 	ptr = 0;
136 	*ptr = 1;
137 
138 	/* Shouldn't get here. */
139 	FAIL_IF(true);
140 
141 	return TEST_FAIL;
142 }
143 
144 /* Return file size if filename exists and pass sanity check, or zero if not. */
145 static off_t try_core_file(const char *filename, struct shared_info *info,
146 			   pid_t pid)
147 {
148 	struct stat buf;
149 	int ret;
150 
151 	ret = stat(filename, &buf);
152 	if (ret == -1)
153 		return TEST_FAIL;
154 
155 	/* Make sure we're not using a stale core file. */
156 	return buf.st_mtime >= info->core_time ? buf.st_size : TEST_FAIL;
157 }
158 
159 static Elf64_Nhdr *next_note(Elf64_Nhdr *nhdr)
160 {
161 	return (void *) nhdr + sizeof(*nhdr) +
162 		__ALIGN_KERNEL(nhdr->n_namesz, 4) +
163 		__ALIGN_KERNEL(nhdr->n_descsz, 4);
164 }
165 
166 static int check_core_file(struct shared_info *info, Elf64_Ehdr *ehdr,
167 			   off_t core_size)
168 {
169 	unsigned long *regs;
170 	Elf64_Phdr *phdr;
171 	Elf64_Nhdr *nhdr;
172 	size_t phdr_size;
173 	void *p = ehdr, *note;
174 	int ret;
175 
176 	ret = memcmp(ehdr->e_ident, ELFMAG, SELFMAG);
177 	FAIL_IF(ret);
178 
179 	FAIL_IF(ehdr->e_type != ET_CORE);
180 	FAIL_IF(ehdr->e_machine != EM_PPC64);
181 	FAIL_IF(ehdr->e_phoff == 0 || ehdr->e_phnum == 0);
182 
183 	/*
184 	 * e_phnum is at most 65535 so calculating the size of the
185 	 * program header cannot overflow.
186 	 */
187 	phdr_size = sizeof(*phdr) * ehdr->e_phnum;
188 
189 	/* Sanity check the program header table location. */
190 	FAIL_IF(ehdr->e_phoff + phdr_size < ehdr->e_phoff);
191 	FAIL_IF(ehdr->e_phoff + phdr_size > core_size);
192 
193 	/* Find the PT_NOTE segment. */
194 	for (phdr = p + ehdr->e_phoff;
195 	     (void *) phdr < p + ehdr->e_phoff + phdr_size;
196 	     phdr += ehdr->e_phentsize)
197 		if (phdr->p_type == PT_NOTE)
198 			break;
199 
200 	FAIL_IF((void *) phdr >= p + ehdr->e_phoff + phdr_size);
201 
202 	/* Find the NT_PPC_PKEY note. */
203 	for (nhdr = p + phdr->p_offset;
204 	     (void *) nhdr < p + phdr->p_offset + phdr->p_filesz;
205 	     nhdr = next_note(nhdr))
206 		if (nhdr->n_type == NT_PPC_PKEY)
207 			break;
208 
209 	FAIL_IF((void *) nhdr >= p + phdr->p_offset + phdr->p_filesz);
210 	FAIL_IF(nhdr->n_descsz == 0);
211 
212 	p = nhdr;
213 	note = p + sizeof(*nhdr) + __ALIGN_KERNEL(nhdr->n_namesz, 4);
214 
215 	regs = (unsigned long *) note;
216 
217 	printf("%-30s AMR: %016lx IAMR: %016lx UAMOR: %016lx\n",
218 	       core_read_running, regs[0], regs[1], regs[2]);
219 
220 	FAIL_IF(regs[0] != info->amr);
221 	FAIL_IF(regs[1] != info->iamr);
222 	FAIL_IF(regs[2] != info->uamor);
223 
224 	return TEST_PASS;
225 }
226 
227 static int parent(struct shared_info *info, pid_t pid)
228 {
229 	char *filenames, *filename[3];
230 	int fd, i, ret, status;
231 	unsigned long regs[3];
232 	off_t core_size;
233 	void *core;
234 
235 	/*
236 	 * Get the initial values for AMR, IAMR and UAMOR and communicate them
237 	 * to the child.
238 	 */
239 	ret = ptrace_read_regs(pid, NT_PPC_PKEY, regs, 3);
240 	PARENT_SKIP_IF_UNSUPPORTED(ret, &info->child_sync, "PKEYs not supported");
241 	PARENT_FAIL_IF(ret, &info->child_sync);
242 
243 	info->amr = regs[0];
244 	info->iamr = regs[1];
245 	info->uamor = regs[2];
246 
247 	/* Wake up child so that it can set itself up. */
248 	ret = prod_child(&info->child_sync);
249 	PARENT_FAIL_IF(ret, &info->child_sync);
250 
251 	ret = wait(&status);
252 	if (ret != pid) {
253 		printf("Child's exit status not captured\n");
254 		return TEST_FAIL;
255 	} else if (!WIFSIGNALED(status) || !WCOREDUMP(status)) {
256 		printf("Child didn't dump core\n");
257 		return TEST_FAIL;
258 	}
259 
260 	/* Construct array of core file names to try. */
261 
262 	filename[0] = filenames = malloc(PATH_MAX);
263 	if (!filenames) {
264 		perror("Error allocating memory");
265 		return TEST_FAIL;
266 	}
267 
268 	ret = snprintf(filename[0], PATH_MAX, "core-pkey.%d", pid);
269 	if (ret < 0 || ret >= PATH_MAX) {
270 		ret = TEST_FAIL;
271 		goto out;
272 	}
273 
274 	filename[1] = filename[0] + ret + 1;
275 	ret = snprintf(filename[1], PATH_MAX - ret - 1, "core.%d", pid);
276 	if (ret < 0 || ret >= PATH_MAX - ret - 1) {
277 		ret = TEST_FAIL;
278 		goto out;
279 	}
280 	filename[2] = "core";
281 
282 	for (i = 0; i < 3; i++) {
283 		core_size = try_core_file(filename[i], info, pid);
284 		if (core_size != TEST_FAIL)
285 			break;
286 	}
287 
288 	if (i == 3) {
289 		printf("Couldn't find core file\n");
290 		ret = TEST_FAIL;
291 		goto out;
292 	}
293 
294 	fd = open(filename[i], O_RDONLY);
295 	if (fd == -1) {
296 		perror("Error opening core file");
297 		ret = TEST_FAIL;
298 		goto out;
299 	}
300 
301 	core = mmap(NULL, core_size, PROT_READ, MAP_PRIVATE, fd, 0);
302 	if (core == (void *) -1) {
303 		perror("Error mmapping core file");
304 		ret = TEST_FAIL;
305 		goto out;
306 	}
307 
308 	ret = check_core_file(info, core, core_size);
309 
310 	munmap(core, core_size);
311 	close(fd);
312 	unlink(filename[i]);
313 
314  out:
315 	free(filenames);
316 
317 	return ret;
318 }
319 
320 static int write_core_pattern(const char *core_pattern)
321 {
322 	int err;
323 
324 	err = write_file(core_pattern_file, core_pattern, strlen(core_pattern));
325 	if (err) {
326 		SKIP_IF_MSG(err == -EPERM, "Try with root privileges");
327 		perror("Error writing to core_pattern file");
328 		return TEST_FAIL;
329 	}
330 
331 	return TEST_PASS;
332 }
333 
334 static int setup_core_pattern(char **core_pattern_, bool *changed_)
335 {
336 	char *core_pattern;
337 	size_t len;
338 	int ret;
339 
340 	core_pattern = malloc(PATH_MAX);
341 	if (!core_pattern) {
342 		perror("Error allocating memory");
343 		return TEST_FAIL;
344 	}
345 
346 	ret = read_file(core_pattern_file, core_pattern, PATH_MAX - 1, &len);
347 	if (ret) {
348 		perror("Error reading core_pattern file");
349 		ret = TEST_FAIL;
350 		goto out;
351 	}
352 
353 	core_pattern[len] = '\0';
354 
355 	/* Check whether we can predict the name of the core file. */
356 	if (!strcmp(core_pattern, "core") || !strcmp(core_pattern, "core.%p"))
357 		*changed_ = false;
358 	else {
359 		ret = write_core_pattern("core-pkey.%p");
360 		if (ret)
361 			goto out;
362 
363 		*changed_ = true;
364 	}
365 
366 	*core_pattern_ = core_pattern;
367 	ret = TEST_PASS;
368 
369  out:
370 	if (ret)
371 		free(core_pattern);
372 
373 	return ret;
374 }
375 
376 static int core_pkey(void)
377 {
378 	char *core_pattern;
379 	bool changed_core_pattern;
380 	struct shared_info *info;
381 	int shm_id;
382 	int ret;
383 	pid_t pid;
384 
385 	ret = setup_core_pattern(&core_pattern, &changed_core_pattern);
386 	if (ret)
387 		return ret;
388 
389 	shm_id = shmget(IPC_PRIVATE, sizeof(*info), 0777 | IPC_CREAT);
390 	info = shmat(shm_id, NULL, 0);
391 
392 	ret = init_child_sync(&info->child_sync);
393 	if (ret)
394 		return ret;
395 
396 	pid = fork();
397 	if (pid < 0) {
398 		perror("fork() failed");
399 		ret = TEST_FAIL;
400 	} else if (pid == 0)
401 		ret = child(info);
402 	else
403 		ret = parent(info, pid);
404 
405 	shmdt(info);
406 
407 	if (pid) {
408 		destroy_child_sync(&info->child_sync);
409 		shmctl(shm_id, IPC_RMID, NULL);
410 
411 		if (changed_core_pattern)
412 			write_core_pattern(core_pattern);
413 	}
414 
415 	free(core_pattern);
416 
417 	return ret;
418 }
419 
420 int main(int argc, char *argv[])
421 {
422 	return test_harness(core_pkey, "core_pkey");
423 }
424