Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 
0003 /*
0004  * Copyright 2020, Sandipan Das, IBM Corp.
0005  *
0006  * Test if the signal information reports the correct memory protection
0007  * key upon getting a key access violation fault for a page that was
0008  * attempted to be protected by two different keys from two competing
0009  * threads at the same time.
0010  */
0011 
0012 #define _GNU_SOURCE
0013 #include <stdio.h>
0014 #include <stdlib.h>
0015 #include <string.h>
0016 #include <signal.h>
0017 
0018 #include <unistd.h>
0019 #include <pthread.h>
0020 #include <sys/mman.h>
0021 
0022 #include "pkeys.h"
0023 
0024 #define PPC_INST_NOP    0x60000000
0025 #define PPC_INST_BLR    0x4e800020
0026 #define PROT_RWX    (PROT_READ | PROT_WRITE | PROT_EXEC)
0027 
0028 #define NUM_ITERATIONS  1000000
0029 
0030 static volatile sig_atomic_t perm_pkey, rest_pkey;
0031 static volatile sig_atomic_t rights, fault_count;
0032 static volatile unsigned int *volatile fault_addr;
0033 static pthread_barrier_t iteration_barrier;
0034 
0035 static void segv_handler(int signum, siginfo_t *sinfo, void *ctx)
0036 {
0037     void *pgstart;
0038     size_t pgsize;
0039     int pkey;
0040 
0041     pkey = siginfo_pkey(sinfo);
0042 
0043     /* Check if this fault originated from a pkey access violation */
0044     if (sinfo->si_code != SEGV_PKUERR) {
0045         sigsafe_err("got a fault for an unexpected reason\n");
0046         _exit(1);
0047     }
0048 
0049     /* Check if this fault originated from the expected address */
0050     if (sinfo->si_addr != (void *) fault_addr) {
0051         sigsafe_err("got a fault for an unexpected address\n");
0052         _exit(1);
0053     }
0054 
0055     /* Check if this fault originated from the restrictive pkey */
0056     if (pkey != rest_pkey) {
0057         sigsafe_err("got a fault for an unexpected pkey\n");
0058         _exit(1);
0059     }
0060 
0061     /* Check if too many faults have occurred for the same iteration */
0062     if (fault_count > 0) {
0063         sigsafe_err("got too many faults for the same address\n");
0064         _exit(1);
0065     }
0066 
0067     pgsize = getpagesize();
0068     pgstart = (void *) ((unsigned long) fault_addr & ~(pgsize - 1));
0069 
0070     /*
0071      * If the current fault occurred due to lack of execute rights,
0072      * reassociate the page with the exec-only pkey since execute
0073      * rights cannot be changed directly for the faulting pkey as
0074      * IAMR is inaccessible from userspace.
0075      *
0076      * Otherwise, if the current fault occurred due to lack of
0077      * read-write rights, change the AMR permission bits for the
0078      * pkey.
0079      *
0080      * This will let the test continue.
0081      */
0082     if (rights == PKEY_DISABLE_EXECUTE &&
0083         mprotect(pgstart, pgsize, PROT_EXEC))
0084         _exit(1);
0085     else
0086         pkey_set_rights(pkey, 0);
0087 
0088     fault_count++;
0089 }
0090 
0091 struct region {
0092     unsigned long rights;
0093     unsigned int *base;
0094     size_t size;
0095 };
0096 
0097 static void *protect(void *p)
0098 {
0099     unsigned long rights;
0100     unsigned int *base;
0101     size_t size;
0102     int tid, i;
0103 
0104     tid = gettid();
0105     base = ((struct region *) p)->base;
0106     size = ((struct region *) p)->size;
0107     FAIL_IF_EXIT(!base);
0108 
0109     /* No read, write and execute restrictions */
0110     rights = 0;
0111 
0112     printf("tid %d, pkey permissions are %s\n", tid, pkey_rights(rights));
0113 
0114     /* Allocate the permissive pkey */
0115     perm_pkey = sys_pkey_alloc(0, rights);
0116     FAIL_IF_EXIT(perm_pkey < 0);
0117 
0118     /*
0119      * Repeatedly try to protect the common region with a permissive
0120      * pkey
0121      */
0122     for (i = 0; i < NUM_ITERATIONS; i++) {
0123         /*
0124          * Wait until the other thread has finished allocating the
0125          * restrictive pkey or until the next iteration has begun
0126          */
0127         pthread_barrier_wait(&iteration_barrier);
0128 
0129         /* Try to associate the permissive pkey with the region */
0130         FAIL_IF_EXIT(sys_pkey_mprotect(base, size, PROT_RWX,
0131                            perm_pkey));
0132     }
0133 
0134     /* Free the permissive pkey */
0135     sys_pkey_free(perm_pkey);
0136 
0137     return NULL;
0138 }
0139 
0140 static void *protect_access(void *p)
0141 {
0142     size_t size, numinsns;
0143     unsigned int *base;
0144     int tid, i;
0145 
0146     tid = gettid();
0147     base = ((struct region *) p)->base;
0148     size = ((struct region *) p)->size;
0149     rights = ((struct region *) p)->rights;
0150     numinsns = size / sizeof(base[0]);
0151     FAIL_IF_EXIT(!base);
0152 
0153     /* Allocate the restrictive pkey */
0154     rest_pkey = sys_pkey_alloc(0, rights);
0155     FAIL_IF_EXIT(rest_pkey < 0);
0156 
0157     printf("tid %d, pkey permissions are %s\n", tid, pkey_rights(rights));
0158     printf("tid %d, %s randomly in range [%p, %p]\n", tid,
0159            (rights == PKEY_DISABLE_EXECUTE) ? "execute" :
0160            (rights == PKEY_DISABLE_WRITE)  ? "write" : "read",
0161            base, base + numinsns);
0162 
0163     /*
0164      * Repeatedly try to protect the common region with a restrictive
0165      * pkey and read, write or execute from it
0166      */
0167     for (i = 0; i < NUM_ITERATIONS; i++) {
0168         /*
0169          * Wait until the other thread has finished allocating the
0170          * permissive pkey or until the next iteration has begun
0171          */
0172         pthread_barrier_wait(&iteration_barrier);
0173 
0174         /* Try to associate the restrictive pkey with the region */
0175         FAIL_IF_EXIT(sys_pkey_mprotect(base, size, PROT_RWX,
0176                            rest_pkey));
0177 
0178         /* Choose a random instruction word address from the region */
0179         fault_addr = base + (rand() % numinsns);
0180         fault_count = 0;
0181 
0182         switch (rights) {
0183         /* Read protection test */
0184         case PKEY_DISABLE_ACCESS:
0185             /*
0186              * Read an instruction word from the region and
0187              * verify if it has not been overwritten to
0188              * something unexpected
0189              */
0190             FAIL_IF_EXIT(*fault_addr != PPC_INST_NOP &&
0191                      *fault_addr != PPC_INST_BLR);
0192             break;
0193 
0194         /* Write protection test */
0195         case PKEY_DISABLE_WRITE:
0196             /*
0197              * Write an instruction word to the region and
0198              * verify if the overwrite has succeeded
0199              */
0200             *fault_addr = PPC_INST_BLR;
0201             FAIL_IF_EXIT(*fault_addr != PPC_INST_BLR);
0202             break;
0203 
0204         /* Execute protection test */
0205         case PKEY_DISABLE_EXECUTE:
0206             /* Jump to the region and execute instructions */
0207             asm volatile(
0208                 "mtctr  %0; bctrl"
0209                 : : "r"(fault_addr) : "ctr", "lr");
0210             break;
0211         }
0212 
0213         /*
0214          * Restore the restrictions originally imposed by the
0215          * restrictive pkey as the signal handler would have
0216          * cleared out the corresponding AMR bits
0217          */
0218         pkey_set_rights(rest_pkey, rights);
0219     }
0220 
0221     /* Free restrictive pkey */
0222     sys_pkey_free(rest_pkey);
0223 
0224     return NULL;
0225 }
0226 
0227 static void reset_pkeys(unsigned long rights)
0228 {
0229     int pkeys[NR_PKEYS], i;
0230 
0231     /* Exhaustively allocate all available pkeys */
0232     for (i = 0; i < NR_PKEYS; i++)
0233         pkeys[i] = sys_pkey_alloc(0, rights);
0234 
0235     /* Free all allocated pkeys */
0236     for (i = 0; i < NR_PKEYS; i++)
0237         sys_pkey_free(pkeys[i]);
0238 }
0239 
0240 static int test(void)
0241 {
0242     pthread_t prot_thread, pacc_thread;
0243     struct sigaction act;
0244     pthread_attr_t attr;
0245     size_t numinsns;
0246     struct region r;
0247     int ret, i;
0248 
0249     srand(time(NULL));
0250     ret = pkeys_unsupported();
0251     if (ret)
0252         return ret;
0253 
0254     /* Allocate the region */
0255     r.size = getpagesize();
0256     r.base = mmap(NULL, r.size, PROT_RWX,
0257               MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
0258     FAIL_IF(r.base == MAP_FAILED);
0259 
0260     /*
0261      * Fill the region with no-ops with a branch at the end
0262      * for returning to the caller
0263      */
0264     numinsns = r.size / sizeof(r.base[0]);
0265     for (i = 0; i < numinsns - 1; i++)
0266         r.base[i] = PPC_INST_NOP;
0267     r.base[i] = PPC_INST_BLR;
0268 
0269     /* Setup SIGSEGV handler */
0270     act.sa_handler = 0;
0271     act.sa_sigaction = segv_handler;
0272     FAIL_IF(sigprocmask(SIG_SETMASK, 0, &act.sa_mask) != 0);
0273     act.sa_flags = SA_SIGINFO;
0274     act.sa_restorer = 0;
0275     FAIL_IF(sigaction(SIGSEGV, &act, NULL) != 0);
0276 
0277     /*
0278      * For these tests, the parent process should clear all bits of
0279      * AMR and IAMR, i.e. impose no restrictions, for all available
0280      * pkeys. This will be the base for the initial AMR and IAMR
0281      * values for all the test thread pairs.
0282      *
0283      * If the AMR and IAMR bits of all available pkeys are cleared
0284      * before running the tests and a fault is generated when
0285      * attempting to read, write or execute instructions from a
0286      * pkey protected region, the pkey responsible for this must be
0287      * the one from the protect-and-access thread since the other
0288      * one is fully permissive. Despite that, if the pkey reported
0289      * by siginfo is not the restrictive pkey, then there must be a
0290      * kernel bug.
0291      */
0292     reset_pkeys(0);
0293 
0294     /* Setup barrier for protect and protect-and-access threads */
0295     FAIL_IF(pthread_attr_init(&attr) != 0);
0296     FAIL_IF(pthread_barrier_init(&iteration_barrier, NULL, 2) != 0);
0297 
0298     /* Setup and start protect and protect-and-read threads */
0299     puts("starting thread pair (protect, protect-and-read)");
0300     r.rights = PKEY_DISABLE_ACCESS;
0301     FAIL_IF(pthread_create(&prot_thread, &attr, &protect, &r) != 0);
0302     FAIL_IF(pthread_create(&pacc_thread, &attr, &protect_access, &r) != 0);
0303     FAIL_IF(pthread_join(prot_thread, NULL) != 0);
0304     FAIL_IF(pthread_join(pacc_thread, NULL) != 0);
0305 
0306     /* Setup and start protect and protect-and-write threads */
0307     puts("starting thread pair (protect, protect-and-write)");
0308     r.rights = PKEY_DISABLE_WRITE;
0309     FAIL_IF(pthread_create(&prot_thread, &attr, &protect, &r) != 0);
0310     FAIL_IF(pthread_create(&pacc_thread, &attr, &protect_access, &r) != 0);
0311     FAIL_IF(pthread_join(prot_thread, NULL) != 0);
0312     FAIL_IF(pthread_join(pacc_thread, NULL) != 0);
0313 
0314     /* Setup and start protect and protect-and-execute threads */
0315     puts("starting thread pair (protect, protect-and-execute)");
0316     r.rights = PKEY_DISABLE_EXECUTE;
0317     FAIL_IF(pthread_create(&prot_thread, &attr, &protect, &r) != 0);
0318     FAIL_IF(pthread_create(&pacc_thread, &attr, &protect_access, &r) != 0);
0319     FAIL_IF(pthread_join(prot_thread, NULL) != 0);
0320     FAIL_IF(pthread_join(pacc_thread, NULL) != 0);
0321 
0322     /* Cleanup */
0323     FAIL_IF(pthread_attr_destroy(&attr) != 0);
0324     FAIL_IF(pthread_barrier_destroy(&iteration_barrier) != 0);
0325     munmap(r.base, r.size);
0326 
0327     return 0;
0328 }
0329 
0330 int main(void)
0331 {
0332     return test_harness(test, "pkey_siginfo");
0333 }