Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Ptrace test for Memory Protection Key registers
0004  *
0005  * Copyright (C) 2015 Anshuman Khandual, IBM Corporation.
0006  * Copyright (C) 2018 IBM Corporation.
0007  */
0008 #include <limits.h>
0009 #include <linux/kernel.h>
0010 #include <sys/mman.h>
0011 #include <sys/types.h>
0012 #include <sys/stat.h>
0013 #include <sys/time.h>
0014 #include <sys/resource.h>
0015 #include <fcntl.h>
0016 #include <unistd.h>
0017 #include "ptrace.h"
0018 #include "child.h"
0019 
0020 #ifndef __NR_pkey_alloc
0021 #define __NR_pkey_alloc     384
0022 #endif
0023 
0024 #ifndef __NR_pkey_free
0025 #define __NR_pkey_free      385
0026 #endif
0027 
0028 #ifndef NT_PPC_PKEY
0029 #define NT_PPC_PKEY     0x110
0030 #endif
0031 
0032 #ifndef PKEY_DISABLE_EXECUTE
0033 #define PKEY_DISABLE_EXECUTE    0x4
0034 #endif
0035 
0036 #define AMR_BITS_PER_PKEY 2
0037 #define PKEY_REG_BITS (sizeof(u64) * 8)
0038 #define pkeyshift(pkey) (PKEY_REG_BITS - ((pkey + 1) * AMR_BITS_PER_PKEY))
0039 
0040 #define CORE_FILE_LIMIT (5 * 1024 * 1024)   /* 5 MB should be enough */
0041 
0042 static const char core_pattern_file[] = "/proc/sys/kernel/core_pattern";
0043 
0044 static const char user_write[] = "[User Write (Running)]";
0045 static const char core_read_running[] = "[Core Read (Running)]";
0046 
0047 /* Information shared between the parent and the child. */
0048 struct shared_info {
0049     struct child_sync child_sync;
0050 
0051     /* AMR value the parent expects to read in the core file. */
0052     unsigned long amr;
0053 
0054     /* IAMR value the parent expects to read in the core file. */
0055     unsigned long iamr;
0056 
0057     /* UAMOR value the parent expects to read in the core file. */
0058     unsigned long uamor;
0059 
0060     /* When the child crashed. */
0061     time_t core_time;
0062 };
0063 
0064 static int sys_pkey_alloc(unsigned long flags, unsigned long init_access_rights)
0065 {
0066     return syscall(__NR_pkey_alloc, flags, init_access_rights);
0067 }
0068 
0069 static int sys_pkey_free(int pkey)
0070 {
0071     return syscall(__NR_pkey_free, pkey);
0072 }
0073 
0074 static int increase_core_file_limit(void)
0075 {
0076     struct rlimit rlim;
0077     int ret;
0078 
0079     ret = getrlimit(RLIMIT_CORE, &rlim);
0080     FAIL_IF(ret);
0081 
0082     if (rlim.rlim_cur != RLIM_INFINITY && rlim.rlim_cur < CORE_FILE_LIMIT) {
0083         rlim.rlim_cur = CORE_FILE_LIMIT;
0084 
0085         if (rlim.rlim_max != RLIM_INFINITY &&
0086             rlim.rlim_max < CORE_FILE_LIMIT)
0087             rlim.rlim_max = CORE_FILE_LIMIT;
0088 
0089         ret = setrlimit(RLIMIT_CORE, &rlim);
0090         FAIL_IF(ret);
0091     }
0092 
0093     ret = getrlimit(RLIMIT_FSIZE, &rlim);
0094     FAIL_IF(ret);
0095 
0096     if (rlim.rlim_cur != RLIM_INFINITY && rlim.rlim_cur < CORE_FILE_LIMIT) {
0097         rlim.rlim_cur = CORE_FILE_LIMIT;
0098 
0099         if (rlim.rlim_max != RLIM_INFINITY &&
0100             rlim.rlim_max < CORE_FILE_LIMIT)
0101             rlim.rlim_max = CORE_FILE_LIMIT;
0102 
0103         ret = setrlimit(RLIMIT_FSIZE, &rlim);
0104         FAIL_IF(ret);
0105     }
0106 
0107     return TEST_PASS;
0108 }
0109 
0110 static int child(struct shared_info *info)
0111 {
0112     bool disable_execute = true;
0113     int pkey1, pkey2, pkey3;
0114     int *ptr, ret;
0115 
0116     /* Wait until parent fills out the initial register values. */
0117     ret = wait_parent(&info->child_sync);
0118     if (ret)
0119         return ret;
0120 
0121     ret = increase_core_file_limit();
0122     FAIL_IF(ret);
0123 
0124     /* Get some pkeys so that we can change their bits in the AMR. */
0125     pkey1 = sys_pkey_alloc(0, PKEY_DISABLE_EXECUTE);
0126     if (pkey1 < 0) {
0127         pkey1 = sys_pkey_alloc(0, 0);
0128         FAIL_IF(pkey1 < 0);
0129 
0130         disable_execute = false;
0131     }
0132 
0133     pkey2 = sys_pkey_alloc(0, 0);
0134     FAIL_IF(pkey2 < 0);
0135 
0136     pkey3 = sys_pkey_alloc(0, 0);
0137     FAIL_IF(pkey3 < 0);
0138 
0139     info->amr |= 3ul << pkeyshift(pkey1) | 2ul << pkeyshift(pkey2);
0140 
0141     if (disable_execute)
0142         info->iamr |= 1ul << pkeyshift(pkey1);
0143     else
0144         info->iamr &= ~(1ul << pkeyshift(pkey1));
0145 
0146     info->iamr &= ~(1ul << pkeyshift(pkey2) | 1ul << pkeyshift(pkey3));
0147 
0148     info->uamor |= 3ul << pkeyshift(pkey1) | 3ul << pkeyshift(pkey2);
0149 
0150     printf("%-30s AMR: %016lx pkey1: %d pkey2: %d pkey3: %d\n",
0151            user_write, info->amr, pkey1, pkey2, pkey3);
0152 
0153     set_amr(info->amr);
0154 
0155     /*
0156      * We won't use pkey3. This tests whether the kernel restores the UAMOR
0157      * permissions after a key is freed.
0158      */
0159     sys_pkey_free(pkey3);
0160 
0161     info->core_time = time(NULL);
0162 
0163     /* Crash. */
0164     ptr = 0;
0165     *ptr = 1;
0166 
0167     /* Shouldn't get here. */
0168     FAIL_IF(true);
0169 
0170     return TEST_FAIL;
0171 }
0172 
0173 /* Return file size if filename exists and pass sanity check, or zero if not. */
0174 static off_t try_core_file(const char *filename, struct shared_info *info,
0175                pid_t pid)
0176 {
0177     struct stat buf;
0178     int ret;
0179 
0180     ret = stat(filename, &buf);
0181     if (ret == -1)
0182         return TEST_FAIL;
0183 
0184     /* Make sure we're not using a stale core file. */
0185     return buf.st_mtime >= info->core_time ? buf.st_size : TEST_FAIL;
0186 }
0187 
0188 static Elf64_Nhdr *next_note(Elf64_Nhdr *nhdr)
0189 {
0190     return (void *) nhdr + sizeof(*nhdr) +
0191         __ALIGN_KERNEL(nhdr->n_namesz, 4) +
0192         __ALIGN_KERNEL(nhdr->n_descsz, 4);
0193 }
0194 
0195 static int check_core_file(struct shared_info *info, Elf64_Ehdr *ehdr,
0196                off_t core_size)
0197 {
0198     unsigned long *regs;
0199     Elf64_Phdr *phdr;
0200     Elf64_Nhdr *nhdr;
0201     size_t phdr_size;
0202     void *p = ehdr, *note;
0203     int ret;
0204 
0205     ret = memcmp(ehdr->e_ident, ELFMAG, SELFMAG);
0206     FAIL_IF(ret);
0207 
0208     FAIL_IF(ehdr->e_type != ET_CORE);
0209     FAIL_IF(ehdr->e_machine != EM_PPC64);
0210     FAIL_IF(ehdr->e_phoff == 0 || ehdr->e_phnum == 0);
0211 
0212     /*
0213      * e_phnum is at most 65535 so calculating the size of the
0214      * program header cannot overflow.
0215      */
0216     phdr_size = sizeof(*phdr) * ehdr->e_phnum;
0217 
0218     /* Sanity check the program header table location. */
0219     FAIL_IF(ehdr->e_phoff + phdr_size < ehdr->e_phoff);
0220     FAIL_IF(ehdr->e_phoff + phdr_size > core_size);
0221 
0222     /* Find the PT_NOTE segment. */
0223     for (phdr = p + ehdr->e_phoff;
0224          (void *) phdr < p + ehdr->e_phoff + phdr_size;
0225          phdr += ehdr->e_phentsize)
0226         if (phdr->p_type == PT_NOTE)
0227             break;
0228 
0229     FAIL_IF((void *) phdr >= p + ehdr->e_phoff + phdr_size);
0230 
0231     /* Find the NT_PPC_PKEY note. */
0232     for (nhdr = p + phdr->p_offset;
0233          (void *) nhdr < p + phdr->p_offset + phdr->p_filesz;
0234          nhdr = next_note(nhdr))
0235         if (nhdr->n_type == NT_PPC_PKEY)
0236             break;
0237 
0238     FAIL_IF((void *) nhdr >= p + phdr->p_offset + phdr->p_filesz);
0239     FAIL_IF(nhdr->n_descsz == 0);
0240 
0241     p = nhdr;
0242     note = p + sizeof(*nhdr) + __ALIGN_KERNEL(nhdr->n_namesz, 4);
0243 
0244     regs = (unsigned long *) note;
0245 
0246     printf("%-30s AMR: %016lx IAMR: %016lx UAMOR: %016lx\n",
0247            core_read_running, regs[0], regs[1], regs[2]);
0248 
0249     FAIL_IF(regs[0] != info->amr);
0250     FAIL_IF(regs[1] != info->iamr);
0251     FAIL_IF(regs[2] != info->uamor);
0252 
0253     return TEST_PASS;
0254 }
0255 
0256 static int parent(struct shared_info *info, pid_t pid)
0257 {
0258     char *filenames, *filename[3];
0259     int fd, i, ret, status;
0260     unsigned long regs[3];
0261     off_t core_size;
0262     void *core;
0263 
0264     /*
0265      * Get the initial values for AMR, IAMR and UAMOR and communicate them
0266      * to the child.
0267      */
0268     ret = ptrace_read_regs(pid, NT_PPC_PKEY, regs, 3);
0269     PARENT_SKIP_IF_UNSUPPORTED(ret, &info->child_sync);
0270     PARENT_FAIL_IF(ret, &info->child_sync);
0271 
0272     info->amr = regs[0];
0273     info->iamr = regs[1];
0274     info->uamor = regs[2];
0275 
0276     /* Wake up child so that it can set itself up. */
0277     ret = prod_child(&info->child_sync);
0278     PARENT_FAIL_IF(ret, &info->child_sync);
0279 
0280     ret = wait(&status);
0281     if (ret != pid) {
0282         printf("Child's exit status not captured\n");
0283         return TEST_FAIL;
0284     } else if (!WIFSIGNALED(status) || !WCOREDUMP(status)) {
0285         printf("Child didn't dump core\n");
0286         return TEST_FAIL;
0287     }
0288 
0289     /* Construct array of core file names to try. */
0290 
0291     filename[0] = filenames = malloc(PATH_MAX);
0292     if (!filenames) {
0293         perror("Error allocating memory");
0294         return TEST_FAIL;
0295     }
0296 
0297     ret = snprintf(filename[0], PATH_MAX, "core-pkey.%d", pid);
0298     if (ret < 0 || ret >= PATH_MAX) {
0299         ret = TEST_FAIL;
0300         goto out;
0301     }
0302 
0303     filename[1] = filename[0] + ret + 1;
0304     ret = snprintf(filename[1], PATH_MAX - ret - 1, "core.%d", pid);
0305     if (ret < 0 || ret >= PATH_MAX - ret - 1) {
0306         ret = TEST_FAIL;
0307         goto out;
0308     }
0309     filename[2] = "core";
0310 
0311     for (i = 0; i < 3; i++) {
0312         core_size = try_core_file(filename[i], info, pid);
0313         if (core_size != TEST_FAIL)
0314             break;
0315     }
0316 
0317     if (i == 3) {
0318         printf("Couldn't find core file\n");
0319         ret = TEST_FAIL;
0320         goto out;
0321     }
0322 
0323     fd = open(filename[i], O_RDONLY);
0324     if (fd == -1) {
0325         perror("Error opening core file");
0326         ret = TEST_FAIL;
0327         goto out;
0328     }
0329 
0330     core = mmap(NULL, core_size, PROT_READ, MAP_PRIVATE, fd, 0);
0331     if (core == (void *) -1) {
0332         perror("Error mmaping core file");
0333         ret = TEST_FAIL;
0334         goto out;
0335     }
0336 
0337     ret = check_core_file(info, core, core_size);
0338 
0339     munmap(core, core_size);
0340     close(fd);
0341     unlink(filename[i]);
0342 
0343  out:
0344     free(filenames);
0345 
0346     return ret;
0347 }
0348 
0349 static int write_core_pattern(const char *core_pattern)
0350 {
0351     size_t len = strlen(core_pattern), ret;
0352     FILE *f;
0353 
0354     f = fopen(core_pattern_file, "w");
0355     SKIP_IF_MSG(!f, "Try with root privileges");
0356 
0357     ret = fwrite(core_pattern, 1, len, f);
0358     fclose(f);
0359     if (ret != len) {
0360         perror("Error writing to core_pattern file");
0361         return TEST_FAIL;
0362     }
0363 
0364     return TEST_PASS;
0365 }
0366 
0367 static int setup_core_pattern(char **core_pattern_, bool *changed_)
0368 {
0369     FILE *f;
0370     char *core_pattern;
0371     int ret;
0372 
0373     core_pattern = malloc(PATH_MAX);
0374     if (!core_pattern) {
0375         perror("Error allocating memory");
0376         return TEST_FAIL;
0377     }
0378 
0379     f = fopen(core_pattern_file, "r");
0380     if (!f) {
0381         perror("Error opening core_pattern file");
0382         ret = TEST_FAIL;
0383         goto out;
0384     }
0385 
0386     ret = fread(core_pattern, 1, PATH_MAX, f);
0387     fclose(f);
0388     if (!ret) {
0389         perror("Error reading core_pattern file");
0390         ret = TEST_FAIL;
0391         goto out;
0392     }
0393 
0394     /* Check whether we can predict the name of the core file. */
0395     if (!strcmp(core_pattern, "core") || !strcmp(core_pattern, "core.%p"))
0396         *changed_ = false;
0397     else {
0398         ret = write_core_pattern("core-pkey.%p");
0399         if (ret)
0400             goto out;
0401 
0402         *changed_ = true;
0403     }
0404 
0405     *core_pattern_ = core_pattern;
0406     ret = TEST_PASS;
0407 
0408  out:
0409     if (ret)
0410         free(core_pattern);
0411 
0412     return ret;
0413 }
0414 
0415 static int core_pkey(void)
0416 {
0417     char *core_pattern;
0418     bool changed_core_pattern;
0419     struct shared_info *info;
0420     int shm_id;
0421     int ret;
0422     pid_t pid;
0423 
0424     ret = setup_core_pattern(&core_pattern, &changed_core_pattern);
0425     if (ret)
0426         return ret;
0427 
0428     shm_id = shmget(IPC_PRIVATE, sizeof(*info), 0777 | IPC_CREAT);
0429     info = shmat(shm_id, NULL, 0);
0430 
0431     ret = init_child_sync(&info->child_sync);
0432     if (ret)
0433         return ret;
0434 
0435     pid = fork();
0436     if (pid < 0) {
0437         perror("fork() failed");
0438         ret = TEST_FAIL;
0439     } else if (pid == 0)
0440         ret = child(info);
0441     else
0442         ret = parent(info, pid);
0443 
0444     shmdt(info);
0445 
0446     if (pid) {
0447         destroy_child_sync(&info->child_sync);
0448         shmctl(shm_id, IPC_RMID, NULL);
0449 
0450         if (changed_core_pattern)
0451             write_core_pattern(core_pattern);
0452     }
0453 
0454     free(core_pattern);
0455 
0456     return ret;
0457 }
0458 
0459 int main(int argc, char *argv[])
0460 {
0461     return test_harness(core_pkey, "core_pkey");
0462 }