1
2
3
4
5
6
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)
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
48struct shared_info {
49 struct child_sync child_sync;
50
51
52 unsigned long amr;
53
54
55 unsigned long iamr;
56
57
58 unsigned long uamor;
59
60
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
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
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
157
158
159 sys_pkey_free(pkey3);
160
161 info->core_time = time(NULL);
162
163
164 ptr = 0;
165 *ptr = 1;
166
167
168 FAIL_IF(true);
169
170 return TEST_FAIL;
171}
172
173
174static 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
185 return buf.st_mtime >= info->core_time ? buf.st_size : TEST_FAIL;
186}
187
188static 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
195static 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
214
215
216 phdr_size = sizeof(*phdr) * ehdr->e_phnum;
217
218
219 FAIL_IF(ehdr->e_phoff + phdr_size < ehdr->e_phoff);
220 FAIL_IF(ehdr->e_phoff + phdr_size > core_size);
221
222
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
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
256static 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
266
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
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
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
349static 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
367static 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, f);
387 fclose(f);
388 if (!ret) {
389 perror("Error reading core_pattern file");
390 ret = TEST_FAIL;
391 goto out;
392 }
393
394
395 if (!strcmp(core_pattern, "core") || !strcmp(core_pattern, "core.%p"))
396 *changed_ = false;
397 else {
398 ret = write_core_pattern("core-pkey.%p");
399 if (ret)
400 goto out;
401
402 *changed_ = true;
403 }
404
405 *core_pattern_ = core_pattern;
406 ret = TEST_PASS;
407
408 out:
409 if (ret)
410 free(core_pattern);
411
412 return ret;
413}
414
415static int core_pkey(void)
416{
417 char *core_pattern;
418 bool changed_core_pattern;
419 struct shared_info *info;
420 int shm_id;
421 int ret;
422 pid_t pid;
423
424 ret = setup_core_pattern(&core_pattern, &changed_core_pattern);
425 if (ret)
426 return ret;
427
428 shm_id = shmget(IPC_PRIVATE, sizeof(*info), 0777 | IPC_CREAT);
429 info = shmat(shm_id, NULL, 0);
430
431 ret = init_child_sync(&info->child_sync);
432 if (ret)
433 return ret;
434
435 pid = fork();
436 if (pid < 0) {
437 perror("fork() failed");
438 ret = TEST_FAIL;
439 } else if (pid == 0)
440 ret = child(info);
441 else
442 ret = parent(info, pid);
443
444 shmdt(info);
445
446 if (pid) {
447 destroy_child_sync(&info->child_sync);
448 shmctl(shm_id, IPC_RMID, NULL);
449
450 if (changed_core_pattern)
451 write_core_pattern(core_pattern);
452 }
453
454 free(core_pattern);
455
456 return ret;
457}
458
459int main(int argc, char *argv[])
460{
461 return test_harness(core_pkey, "core_pkey");
462}
463