File Index
Files and directories
Sign in
FILE

core-pkey.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/powerpc/ptrace/core-pkey.c C
  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
 23static const char core_pattern_file[] = "/proc/sys/kernel/core_pattern";
 24
 25static const char user_write[] = "[User Write (Running)]";
 26static const char core_read_running[] = "[Core Read (Running)]";
 27
 28/* Information shared between the parent and the child. */
 29struct 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
 45static 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
 81static 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, PKEY_UNRESTRICTED);
 99		FAIL_IF(pkey1 < 0);
100
101		disable_execute = false;
102	}
103
104	pkey2 = sys_pkey_alloc(0, PKEY_UNRESTRICTED);
105	FAIL_IF(pkey2 < 0);
106
107	pkey3 = sys_pkey_alloc(0, PKEY_UNRESTRICTED);
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. */
145static 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
159static 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
166static 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
227static 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
320static 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
334static 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
376static 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
420int main(int argc, char *argv[])
421{
422	return test_harness(core_pkey, "core_pkey");
423}
~/ Read-only file preview.