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
increase_core_file_limit(void)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
child(struct shared_info * info)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. */
try_core_file(const char * filename,struct shared_info * info,pid_t pid)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
next_note(Elf64_Nhdr * nhdr)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
check_core_file(struct shared_info * info,Elf64_Ehdr * ehdr,off_t core_size)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
parent(struct shared_info * info,pid_t pid)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
write_core_pattern(const char * core_pattern)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
setup_core_pattern(char ** core_pattern_,bool * changed_)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
core_pkey(void)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
main(int argc,char * argv[])420 int main(int argc, char *argv[])
421 {
422 return test_harness(core_pkey, "core_pkey");
423 }
424