aboutsummaryrefslogtreecommitdiffstats
path: root/tools/testing
diff options
context:
space:
mode:
authorThiago Jung Bauermann <bauerman@linux.ibm.com>2018-05-24 22:11:45 -0400
committerMichael Ellerman <mpe@ellerman.id.au>2018-05-28 04:46:36 -0400
commit39b91dd625f15438859204a489ab62ef41d2be39 (patch)
treeacb991aae4814d06e03a1000adecd7e9a5e7c1fe /tools/testing
parent1f7256e7dddef49acf9f6c9fe3f93522a8a2a4c2 (diff)
selftests/powerpc: Add core file test for Protection Key registers
This test verifies that the AMR, IAMR and UAMOR are being written to a process' core file. Signed-off-by: Thiago Jung Bauermann <bauerman@linux.ibm.com> [mpe: Simplify make rule] Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Diffstat (limited to 'tools/testing')
-rw-r--r--tools/testing/selftests/powerpc/ptrace/Makefile6
-rw-r--r--tools/testing/selftests/powerpc/ptrace/core-pkey.c461
2 files changed, 464 insertions, 3 deletions
diff --git a/tools/testing/selftests/powerpc/ptrace/Makefile b/tools/testing/selftests/powerpc/ptrace/Makefile
index bd8959854aec..4f5957538908 100644
--- a/tools/testing/selftests/powerpc/ptrace/Makefile
+++ b/tools/testing/selftests/powerpc/ptrace/Makefile
@@ -1,7 +1,7 @@
1# SPDX-License-Identifier: GPL-2.0 1# SPDX-License-Identifier: GPL-2.0
2TEST_PROGS := ptrace-gpr ptrace-tm-gpr ptrace-tm-spd-gpr \ 2TEST_PROGS := ptrace-gpr ptrace-tm-gpr ptrace-tm-spd-gpr \
3 ptrace-tar ptrace-tm-tar ptrace-tm-spd-tar ptrace-vsx ptrace-tm-vsx \ 3 ptrace-tar ptrace-tm-tar ptrace-tm-spd-tar ptrace-vsx ptrace-tm-vsx \
4 ptrace-tm-spd-vsx ptrace-tm-spr ptrace-hwbreak ptrace-pkey 4 ptrace-tm-spd-vsx ptrace-tm-spr ptrace-hwbreak ptrace-pkey core-pkey
5 5
6include ../../lib.mk 6include ../../lib.mk
7 7
@@ -9,8 +9,8 @@ all: $(TEST_PROGS)
9 9
10CFLAGS += -m64 -I../../../../../usr/include -I../tm -mhtm -fno-pie 10CFLAGS += -m64 -I../../../../../usr/include -I../tm -mhtm -fno-pie
11 11
12ptrace-pkey: child.h 12ptrace-pkey core-pkey: child.h
13ptrace-pkey: LDLIBS += -pthread 13ptrace-pkey core-pkey: LDLIBS += -pthread
14 14
15$(TEST_PROGS): ../harness.c ../utils.c ../lib/reg.S ptrace.h 15$(TEST_PROGS): ../harness.c ../utils.c ../lib/reg.S ptrace.h
16 16
diff --git a/tools/testing/selftests/powerpc/ptrace/core-pkey.c b/tools/testing/selftests/powerpc/ptrace/core-pkey.c
new file mode 100644
index 000000000000..36bc312b1f5c
--- /dev/null
+++ b/tools/testing/selftests/powerpc/ptrace/core-pkey.c
@@ -0,0 +1,461 @@
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
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
144 info->uamor |= 3ul << pkeyshift(pkey1) | 3ul << pkeyshift(pkey2);
145
146 printf("%-30s AMR: %016lx pkey1: %d pkey2: %d pkey3: %d\n",
147 user_write, info->amr, pkey1, pkey2, pkey3);
148
149 mtspr(SPRN_AMR, info->amr);
150
151 /*
152 * We won't use pkey3. This tests whether the kernel restores the UAMOR
153 * permissions after a key is freed.
154 */
155 sys_pkey_free(pkey3);
156
157 info->core_time = time(NULL);
158
159 /* Crash. */
160 ptr = 0;
161 *ptr = 1;
162
163 /* Shouldn't get here. */
164 FAIL_IF(true);
165
166 return TEST_FAIL;
167}
168
169/* Return file size if filename exists and pass sanity check, or zero if not. */
170static off_t try_core_file(const char *filename, struct shared_info *info,
171 pid_t pid)
172{
173 struct stat buf;
174 int ret;
175
176 ret = stat(filename, &buf);
177 if (ret == -1)
178 return TEST_FAIL;
179
180 /* Make sure we're not using a stale core file. */
181 return buf.st_mtime >= info->core_time ? buf.st_size : TEST_FAIL;
182}
183
184static Elf64_Nhdr *next_note(Elf64_Nhdr *nhdr)
185{
186 return (void *) nhdr + sizeof(*nhdr) +
187 __ALIGN_KERNEL(nhdr->n_namesz, 4) +
188 __ALIGN_KERNEL(nhdr->n_descsz, 4);
189}
190
191static int check_core_file(struct shared_info *info, Elf64_Ehdr *ehdr,
192 off_t core_size)
193{
194 unsigned long *regs;
195 Elf64_Phdr *phdr;
196 Elf64_Nhdr *nhdr;
197 size_t phdr_size;
198 void *p = ehdr, *note;
199 int ret;
200
201 ret = memcmp(ehdr->e_ident, ELFMAG, SELFMAG);
202 FAIL_IF(ret);
203
204 FAIL_IF(ehdr->e_type != ET_CORE);
205 FAIL_IF(ehdr->e_machine != EM_PPC64);
206 FAIL_IF(ehdr->e_phoff == 0 || ehdr->e_phnum == 0);
207
208 /*
209 * e_phnum is at most 65535 so calculating the size of the
210 * program header cannot overflow.
211 */
212 phdr_size = sizeof(*phdr) * ehdr->e_phnum;
213
214 /* Sanity check the program header table location. */
215 FAIL_IF(ehdr->e_phoff + phdr_size < ehdr->e_phoff);
216 FAIL_IF(ehdr->e_phoff + phdr_size > core_size);
217
218 /* Find the PT_NOTE segment. */
219 for (phdr = p + ehdr->e_phoff;
220 (void *) phdr < p + ehdr->e_phoff + phdr_size;
221 phdr += ehdr->e_phentsize)
222 if (phdr->p_type == PT_NOTE)
223 break;
224
225 FAIL_IF((void *) phdr >= p + ehdr->e_phoff + phdr_size);
226
227 /* Find the NT_PPC_PKEY note. */
228 for (nhdr = p + phdr->p_offset;
229 (void *) nhdr < p + phdr->p_offset + phdr->p_filesz;
230 nhdr = next_note(nhdr))
231 if (nhdr->n_type == NT_PPC_PKEY)
232 break;
233
234 FAIL_IF((void *) nhdr >= p + phdr->p_offset + phdr->p_filesz);
235 FAIL_IF(nhdr->n_descsz == 0);
236
237 p = nhdr;
238 note = p + sizeof(*nhdr) + __ALIGN_KERNEL(nhdr->n_namesz, 4);
239
240 regs = (unsigned long *) note;
241
242 printf("%-30s AMR: %016lx IAMR: %016lx UAMOR: %016lx\n",
243 core_read_running, regs[0], regs[1], regs[2]);
244
245 FAIL_IF(regs[0] != info->amr);
246 FAIL_IF(regs[1] != info->iamr);
247 FAIL_IF(regs[2] != info->uamor);
248
249 return TEST_PASS;
250}
251
252static int parent(struct shared_info *info, pid_t pid)
253{
254 char *filenames, *filename[3];
255 int fd, i, ret, status;
256 unsigned long regs[3];
257 off_t core_size;
258 void *core;
259
260 /*
261 * Get the initial values for AMR, IAMR and UAMOR and communicate them
262 * to the child.
263 */
264 ret = ptrace_read_regs(pid, NT_PPC_PKEY, regs, 3);
265 PARENT_SKIP_IF_UNSUPPORTED(ret, &info->child_sync);
266 PARENT_FAIL_IF(ret, &info->child_sync);
267
268 info->amr = regs[0];
269 info->iamr = regs[1];
270 info->uamor = regs[2];
271
272 /* Wake up child so that it can set itself up. */
273 ret = prod_child(&info->child_sync);
274 PARENT_FAIL_IF(ret, &info->child_sync);
275
276 ret = wait(&status);
277 if (ret != pid) {
278 printf("Child's exit status not captured\n");
279 return TEST_FAIL;
280 } else if (!WIFSIGNALED(status) || !WCOREDUMP(status)) {
281 printf("Child didn't dump core\n");
282 return TEST_FAIL;
283 }
284
285 /* Construct array of core file names to try. */
286
287 filename[0] = filenames = malloc(PATH_MAX);
288 if (!filenames) {
289 perror("Error allocating memory");
290 return TEST_FAIL;
291 }
292
293 ret = snprintf(filename[0], PATH_MAX, "core-pkey.%d", pid);
294 if (ret < 0 || ret >= PATH_MAX) {
295 ret = TEST_FAIL;
296 goto out;
297 }
298
299 filename[1] = filename[0] + ret + 1;
300 ret = snprintf(filename[1], PATH_MAX - ret - 1, "core.%d", pid);
301 if (ret < 0 || ret >= PATH_MAX - ret - 1) {
302 ret = TEST_FAIL;
303 goto out;
304 }
305 filename[2] = "core";
306
307 for (i = 0; i < 3; i++) {
308 core_size = try_core_file(filename[i], info, pid);
309 if (core_size != TEST_FAIL)
310 break;
311 }
312
313 if (i == 3) {
314 printf("Couldn't find core file\n");
315 ret = TEST_FAIL;
316 goto out;
317 }
318
319 fd = open(filename[i], O_RDONLY);
320 if (fd == -1) {
321 perror("Error opening core file");
322 ret = TEST_FAIL;
323 goto out;
324 }
325
326 core = mmap(NULL, core_size, PROT_READ, MAP_PRIVATE, fd, 0);
327 if (core == (void *) -1) {
328 perror("Error mmaping core file");
329 ret = TEST_FAIL;
330 goto out;
331 }
332
333 ret = check_core_file(info, core, core_size);
334
335 munmap(core, core_size);
336 close(fd);
337 unlink(filename[i]);
338
339 out:
340 free(filenames);
341
342 return ret;
343}
344
345static int write_core_pattern(const char *core_pattern)
346{
347 size_t len = strlen(core_pattern), ret;
348 FILE *f;
349
350 f = fopen(core_pattern_file, "w");
351 if (!f) {
352 perror("Error writing to core_pattern file");
353 return TEST_FAIL;
354 }
355
356 ret = fwrite(core_pattern, 1, len, f);
357 fclose(f);
358 if (ret != len) {
359 perror("Error writing to core_pattern file");
360 return TEST_FAIL;
361 }
362
363 return TEST_PASS;
364}
365
366static int setup_core_pattern(char **core_pattern_, bool *changed_)
367{
368 FILE *f;
369 char *core_pattern;
370 int ret;
371
372 core_pattern = malloc(PATH_MAX);
373 if (!core_pattern) {
374 perror("Error allocating memory");
375 return TEST_FAIL;
376 }
377
378 f = fopen(core_pattern_file, "r");
379 if (!f) {
380 perror("Error opening core_pattern file");
381 ret = TEST_FAIL;
382 goto out;
383 }
384
385 ret = fread(core_pattern, 1, PATH_MAX, f);
386 fclose(f);
387 if (!ret) {
388 perror("Error reading core_pattern file");
389 ret = TEST_FAIL;
390 goto out;
391 }
392
393 /* Check whether we can predict the name of the core file. */
394 if (!strcmp(core_pattern, "core") || !strcmp(core_pattern, "core.%p"))
395 *changed_ = false;
396 else {
397 ret = write_core_pattern("core-pkey.%p");
398 if (ret)
399 goto out;
400
401 *changed_ = true;
402 }
403
404 *core_pattern_ = core_pattern;
405 ret = TEST_PASS;
406
407 out:
408 if (ret)
409 free(core_pattern);
410
411 return ret;
412}
413
414static int core_pkey(void)
415{
416 char *core_pattern;
417 bool changed_core_pattern;
418 struct shared_info *info;
419 int shm_id;
420 int ret;
421 pid_t pid;
422
423 ret = setup_core_pattern(&core_pattern, &changed_core_pattern);
424 if (ret)
425 return ret;
426
427 shm_id = shmget(IPC_PRIVATE, sizeof(*info), 0777 | IPC_CREAT);
428 info = shmat(shm_id, NULL, 0);
429
430 ret = init_child_sync(&info->child_sync);
431 if (ret)
432 return ret;
433
434 pid = fork();
435 if (pid < 0) {
436 perror("fork() failed");
437 ret = TEST_FAIL;
438 } else if (pid == 0)
439 ret = child(info);
440 else
441 ret = parent(info, pid);
442
443 shmdt(info);
444
445 if (pid) {
446 destroy_child_sync(&info->child_sync);
447 shmctl(shm_id, IPC_RMID, NULL);
448
449 if (changed_core_pattern)
450 write_core_pattern(core_pattern);
451 }
452
453 free(core_pattern);
454
455 return ret;
456}
457
458int main(int argc, char *argv[])
459{
460 return test_harness(core_pkey, "core_pkey");
461}