1 // SPDX-License-Identifier: GPL-2.0+
3 * Ptrace test for Memory Protection Key registers
5 * Copyright (C) 2015 Anshuman Khandual, IBM Corporation.
6 * Copyright (C) 2018 IBM Corporation.
9 #include <linux/kernel.h>
11 #include <sys/types.h>
14 #include <sys/resource.h>
20 #ifndef __NR_pkey_alloc
21 #define __NR_pkey_alloc 384
24 #ifndef __NR_pkey_free
25 #define __NR_pkey_free 385
29 #define NT_PPC_PKEY 0x110
32 #ifndef PKEY_DISABLE_EXECUTE
33 #define PKEY_DISABLE_EXECUTE 0x4
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))
40 #define CORE_FILE_LIMIT (5 * 1024 * 1024) /* 5 MB should be enough */
42 static const char core_pattern_file[] = "/proc/sys/kernel/core_pattern";
44 static const char user_write[] = "[User Write (Running)]";
45 static const char core_read_running[] = "[Core Read (Running)]";
47 /* Information shared between the parent and the child. */
49 struct child_sync child_sync;
51 /* AMR value the parent expects to read in the core file. */
54 /* IAMR value the parent expects to read in the core file. */
57 /* UAMOR value the parent expects to read in the core file. */
60 /* When the child crashed. */
64 static int sys_pkey_alloc(unsigned long flags, unsigned long init_access_rights)
66 return syscall(__NR_pkey_alloc, flags, init_access_rights);
69 static int sys_pkey_free(int pkey)
71 return syscall(__NR_pkey_free, pkey);
74 static int increase_core_file_limit(void)
79 ret = getrlimit(RLIMIT_CORE, &rlim);
82 if (rlim.rlim_cur != RLIM_INFINITY && rlim.rlim_cur < CORE_FILE_LIMIT) {
83 rlim.rlim_cur = CORE_FILE_LIMIT;
85 if (rlim.rlim_max != RLIM_INFINITY &&
86 rlim.rlim_max < CORE_FILE_LIMIT)
87 rlim.rlim_max = CORE_FILE_LIMIT;
89 ret = setrlimit(RLIMIT_CORE, &rlim);
93 ret = getrlimit(RLIMIT_FSIZE, &rlim);
96 if (rlim.rlim_cur != RLIM_INFINITY && rlim.rlim_cur < CORE_FILE_LIMIT) {
97 rlim.rlim_cur = CORE_FILE_LIMIT;
99 if (rlim.rlim_max != RLIM_INFINITY &&
100 rlim.rlim_max < CORE_FILE_LIMIT)
101 rlim.rlim_max = CORE_FILE_LIMIT;
103 ret = setrlimit(RLIMIT_FSIZE, &rlim);
110 static int child(struct shared_info *info)
112 bool disable_execute = true;
113 int pkey1, pkey2, pkey3;
116 /* Wait until parent fills out the initial register values. */
117 ret = wait_parent(&info->child_sync);
121 ret = increase_core_file_limit();
124 /* Get some pkeys so that we can change their bits in the AMR. */
125 pkey1 = sys_pkey_alloc(0, PKEY_DISABLE_EXECUTE);
127 pkey1 = sys_pkey_alloc(0, 0);
130 disable_execute = false;
133 pkey2 = sys_pkey_alloc(0, 0);
136 pkey3 = sys_pkey_alloc(0, 0);
139 info->amr |= 3ul << pkeyshift(pkey1) | 2ul << pkeyshift(pkey2);
142 info->iamr |= 1ul << pkeyshift(pkey1);
144 info->iamr &= ~(1ul << pkeyshift(pkey1));
146 info->iamr &= ~(1ul << pkeyshift(pkey2) | 1ul << pkeyshift(pkey3));
148 info->uamor |= 3ul << pkeyshift(pkey1) | 3ul << pkeyshift(pkey2);
150 printf("%-30s AMR: %016lx pkey1: %d pkey2: %d pkey3: %d\n",
151 user_write, info->amr, pkey1, pkey2, pkey3);
153 mtspr(SPRN_AMR, info->amr);
156 * We won't use pkey3. This tests whether the kernel restores the UAMOR
157 * permissions after a key is freed.
159 sys_pkey_free(pkey3);
161 info->core_time = time(NULL);
167 /* Shouldn't get here. */
173 /* Return file size if filename exists and pass sanity check, or zero if not. */
174 static off_t try_core_file(const char *filename, struct shared_info *info,
180 ret = stat(filename, &buf);
184 /* Make sure we're not using a stale core file. */
185 return buf.st_mtime >= info->core_time ? buf.st_size : TEST_FAIL;
188 static Elf64_Nhdr *next_note(Elf64_Nhdr *nhdr)
190 return (void *) nhdr + sizeof(*nhdr) +
191 __ALIGN_KERNEL(nhdr->n_namesz, 4) +
192 __ALIGN_KERNEL(nhdr->n_descsz, 4);
195 static int check_core_file(struct shared_info *info, Elf64_Ehdr *ehdr,
202 void *p = ehdr, *note;
205 ret = memcmp(ehdr->e_ident, ELFMAG, SELFMAG);
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);
213 * e_phnum is at most 65535 so calculating the size of the
214 * program header cannot overflow.
216 phdr_size = sizeof(*phdr) * ehdr->e_phnum;
218 /* Sanity check the program header table location. */
219 FAIL_IF(ehdr->e_phoff + phdr_size < ehdr->e_phoff);
220 FAIL_IF(ehdr->e_phoff + phdr_size > core_size);
222 /* Find the PT_NOTE segment. */
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)
229 FAIL_IF((void *) phdr >= p + ehdr->e_phoff + phdr_size);
231 /* Find the NT_PPC_PKEY note. */
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)
238 FAIL_IF((void *) nhdr >= p + phdr->p_offset + phdr->p_filesz);
239 FAIL_IF(nhdr->n_descsz == 0);
242 note = p + sizeof(*nhdr) + __ALIGN_KERNEL(nhdr->n_namesz, 4);
244 regs = (unsigned long *) note;
246 printf("%-30s AMR: %016lx IAMR: %016lx UAMOR: %016lx\n",
247 core_read_running, regs[0], regs[1], regs[2]);
249 FAIL_IF(regs[0] != info->amr);
250 FAIL_IF(regs[1] != info->iamr);
251 FAIL_IF(regs[2] != info->uamor);
256 static int parent(struct shared_info *info, pid_t pid)
258 char *filenames, *filename[3];
259 int fd, i, ret, status;
260 unsigned long regs[3];
265 * Get the initial values for AMR, IAMR and UAMOR and communicate them
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);
273 info->iamr = regs[1];
274 info->uamor = regs[2];
276 /* Wake up child so that it can set itself up. */
277 ret = prod_child(&info->child_sync);
278 PARENT_FAIL_IF(ret, &info->child_sync);
282 printf("Child's exit status not captured\n");
284 } else if (!WIFSIGNALED(status) || !WCOREDUMP(status)) {
285 printf("Child didn't dump core\n");
289 /* Construct array of core file names to try. */
291 filename[0] = filenames = malloc(PATH_MAX);
293 perror("Error allocating memory");
297 ret = snprintf(filename[0], PATH_MAX, "core-pkey.%d", pid);
298 if (ret < 0 || ret >= PATH_MAX) {
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) {
309 filename[2] = "core";
311 for (i = 0; i < 3; i++) {
312 core_size = try_core_file(filename[i], info, pid);
313 if (core_size != TEST_FAIL)
318 printf("Couldn't find core file\n");
323 fd = open(filename[i], O_RDONLY);
325 perror("Error opening core file");
330 core = mmap(NULL, core_size, PROT_READ, MAP_PRIVATE, fd, 0);
331 if (core == (void *) -1) {
332 perror("Error mmaping core file");
337 ret = check_core_file(info, core, core_size);
339 munmap(core, core_size);
349 static int write_core_pattern(const char *core_pattern)
351 size_t len = strlen(core_pattern), ret;
354 f = fopen(core_pattern_file, "w");
355 SKIP_IF_MSG(!f, "Try with root privileges");
357 ret = fwrite(core_pattern, 1, len, f);
360 perror("Error writing to core_pattern file");
367 static int setup_core_pattern(char **core_pattern_, bool *changed_)
373 core_pattern = malloc(PATH_MAX);
375 perror("Error allocating memory");
379 f = fopen(core_pattern_file, "r");
381 perror("Error opening core_pattern file");
386 ret = fread(core_pattern, 1, PATH_MAX, f);
389 perror("Error reading core_pattern file");
394 /* Check whether we can predict the name of the core file. */
395 if (!strcmp(core_pattern, "core") || !strcmp(core_pattern, "core.%p"))
398 ret = write_core_pattern("core-pkey.%p");
405 *core_pattern_ = core_pattern;
415 static int core_pkey(void)
418 bool changed_core_pattern;
419 struct shared_info *info;
424 ret = setup_core_pattern(&core_pattern, &changed_core_pattern);
428 shm_id = shmget(IPC_PRIVATE, sizeof(*info), 0777 | IPC_CREAT);
429 info = shmat(shm_id, NULL, 0);
431 ret = init_child_sync(&info->child_sync);
437 perror("fork() failed");
442 ret = parent(info, pid);
447 destroy_child_sync(&info->child_sync);
448 shmctl(shm_id, IPC_RMID, NULL);
450 if (changed_core_pattern)
451 write_core_pattern(core_pattern);
459 int main(int argc, char *argv[])
461 return test_harness(core_pkey, "core_pkey");