~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/testing/selftests/powerpc/ptrace/core-pkey.c

Version: ~ [ linux-5.15-rc1 ] ~ [ linux-5.14.5 ] ~ [ linux-5.13.18 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.66 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.147 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.206 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.246 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.282 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.283 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.18.140 ] ~ [ linux-3.16.85 ] ~ [ linux-3.14.79 ] ~ [ linux-3.12.74 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 
 42 static const char core_pattern_file[] = "/proc/sys/kernel/core_pattern";
 43 
 44 static const char user_write[] = "[User Write (Running)]";
 45 static const char core_read_running[] = "[Core Read (Running)]";
 46 
 47 /* Information shared between the parent and the child. */
 48 struct 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 
 64 static 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 
 69 static int sys_pkey_free(int pkey)
 70 {
 71         return syscall(__NR_pkey_free, pkey);
 72 }
 73 
 74 static 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 
110 static 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         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         mtspr(SPRN_AMR, info->amr);
154 
155         /*
156          * We won't use pkey3. This tests whether the kernel restores the UAMOR
157          * permissions after a key is freed.
158          */
159         sys_pkey_free(pkey3);
160 
161         info->core_time = time(NULL);
162 
163         /* Crash. */
164         ptr = 0;
165         *ptr = 1;
166 
167         /* Shouldn't get here. */
168         FAIL_IF(true);
169 
170         return TEST_FAIL;
171 }
172 
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,
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         /* Make sure we're not using a stale core file. */
185         return buf.st_mtime >= info->core_time ? buf.st_size : TEST_FAIL;
186 }
187 
188 static 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 
195 static 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          * e_phnum is at most 65535 so calculating the size of the
214          * program header cannot overflow.
215          */
216         phdr_size = sizeof(*phdr) * ehdr->e_phnum;
217 
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);
221 
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)
227                         break;
228 
229         FAIL_IF((void *) phdr >= p + ehdr->e_phoff + phdr_size);
230 
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)
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 
256 static 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          * Get the initial values for AMR, IAMR and UAMOR and communicate them
266          * to the child.
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         /* 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);
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         /* Construct array of core file names to try. */
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 
349 static 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 
367 static 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         /* Check whether we can predict the name of the core file. */
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 
415 static 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 
459 int main(int argc, char *argv[])
460 {
461         return test_harness(core_pkey, "core_pkey");
462 }
463 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp