Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 // Copyright (C) 2020 ARM Limited
0003 
0004 #define _GNU_SOURCE
0005 
0006 #include <sys/auxv.h>
0007 #include <sys/types.h>
0008 #include <sys/wait.h>
0009 #include <signal.h>
0010 #include <setjmp.h>
0011 #include <sched.h>
0012 
0013 #include "../../kselftest_harness.h"
0014 #include "helper.h"
0015 
0016 #define PAC_COLLISION_ATTEMPTS 10
0017 /*
0018  * The kernel sets TBID by default. So bits 55 and above should remain
0019  * untouched no matter what.
0020  * The VA space size is 48 bits. Bigger is opt-in.
0021  */
0022 #define PAC_MASK (~0xff80ffffffffffff)
0023 #define ARBITRARY_VALUE (0x1234)
0024 #define ASSERT_PAUTH_ENABLED() \
0025 do { \
0026     unsigned long hwcaps = getauxval(AT_HWCAP); \
0027     /* data key instructions are not in NOP space. This prevents a SIGILL */ \
0028     if (!(hwcaps & HWCAP_PACA))                 \
0029         SKIP(return, "PAUTH not enabled"); \
0030 } while (0)
0031 #define ASSERT_GENERIC_PAUTH_ENABLED() \
0032 do { \
0033     unsigned long hwcaps = getauxval(AT_HWCAP); \
0034     /* generic key instructions are not in NOP space. This prevents a SIGILL */ \
0035     if (!(hwcaps & HWCAP_PACG)) \
0036         SKIP(return, "Generic PAUTH not enabled");  \
0037 } while (0)
0038 
0039 void sign_specific(struct signatures *sign, size_t val)
0040 {
0041     sign->keyia = keyia_sign(val);
0042     sign->keyib = keyib_sign(val);
0043     sign->keyda = keyda_sign(val);
0044     sign->keydb = keydb_sign(val);
0045 }
0046 
0047 void sign_all(struct signatures *sign, size_t val)
0048 {
0049     sign->keyia = keyia_sign(val);
0050     sign->keyib = keyib_sign(val);
0051     sign->keyda = keyda_sign(val);
0052     sign->keydb = keydb_sign(val);
0053     sign->keyg  = keyg_sign(val);
0054 }
0055 
0056 int n_same(struct signatures *old, struct signatures *new, int nkeys)
0057 {
0058     int res = 0;
0059 
0060     res += old->keyia == new->keyia;
0061     res += old->keyib == new->keyib;
0062     res += old->keyda == new->keyda;
0063     res += old->keydb == new->keydb;
0064     if (nkeys == NKEYS)
0065         res += old->keyg == new->keyg;
0066 
0067     return res;
0068 }
0069 
0070 int n_same_single_set(struct signatures *sign, int nkeys)
0071 {
0072     size_t vals[nkeys];
0073     int same = 0;
0074 
0075     vals[0] = sign->keyia & PAC_MASK;
0076     vals[1] = sign->keyib & PAC_MASK;
0077     vals[2] = sign->keyda & PAC_MASK;
0078     vals[3] = sign->keydb & PAC_MASK;
0079 
0080     if (nkeys >= 4)
0081         vals[4] = sign->keyg & PAC_MASK;
0082 
0083     for (int i = 0; i < nkeys - 1; i++) {
0084         for (int j = i + 1; j < nkeys; j++) {
0085             if (vals[i] == vals[j])
0086                 same += 1;
0087         }
0088     }
0089     return same;
0090 }
0091 
0092 int exec_sign_all(struct signatures *signed_vals, size_t val)
0093 {
0094     int new_stdin[2];
0095     int new_stdout[2];
0096     int status;
0097     int i;
0098     ssize_t ret;
0099     pid_t pid;
0100     cpu_set_t mask;
0101 
0102     ret = pipe(new_stdin);
0103     if (ret == -1) {
0104         perror("pipe returned error");
0105         return -1;
0106     }
0107 
0108     ret = pipe(new_stdout);
0109     if (ret == -1) {
0110         perror("pipe returned error");
0111         return -1;
0112     }
0113 
0114     /*
0115      * pin this process and all its children to a single CPU, so it can also
0116      * guarantee a context switch with its child
0117      */
0118     sched_getaffinity(0, sizeof(mask), &mask);
0119 
0120     for (i = 0; i < sizeof(cpu_set_t); i++)
0121         if (CPU_ISSET(i, &mask))
0122             break;
0123 
0124     CPU_ZERO(&mask);
0125     CPU_SET(i, &mask);
0126     sched_setaffinity(0, sizeof(mask), &mask);
0127 
0128     pid = fork();
0129     // child
0130     if (pid == 0) {
0131         dup2(new_stdin[0], STDIN_FILENO);
0132         if (ret == -1) {
0133             perror("dup2 returned error");
0134             exit(1);
0135         }
0136 
0137         dup2(new_stdout[1], STDOUT_FILENO);
0138         if (ret == -1) {
0139             perror("dup2 returned error");
0140             exit(1);
0141         }
0142 
0143         close(new_stdin[0]);
0144         close(new_stdin[1]);
0145         close(new_stdout[0]);
0146         close(new_stdout[1]);
0147 
0148         ret = execl("exec_target", "exec_target", (char *)NULL);
0149         if (ret == -1) {
0150             perror("exec returned error");
0151             exit(1);
0152         }
0153     }
0154 
0155     close(new_stdin[0]);
0156     close(new_stdout[1]);
0157 
0158     ret = write(new_stdin[1], &val, sizeof(size_t));
0159     if (ret == -1) {
0160         perror("write returned error");
0161         return -1;
0162     }
0163 
0164     /*
0165      * wait for the worker to finish, so that read() reads all data
0166      * will also context switch with worker so that this function can be used
0167      * for context switch tests
0168      */
0169     waitpid(pid, &status, 0);
0170     if (WIFEXITED(status) == 0) {
0171         fprintf(stderr, "worker exited unexpectedly\n");
0172         return -1;
0173     }
0174     if (WEXITSTATUS(status) != 0) {
0175         fprintf(stderr, "worker exited with error\n");
0176         return -1;
0177     }
0178 
0179     ret = read(new_stdout[0], signed_vals, sizeof(struct signatures));
0180     if (ret == -1) {
0181         perror("read returned error");
0182         return -1;
0183     }
0184 
0185     return 0;
0186 }
0187 
0188 sigjmp_buf jmpbuf;
0189 void pac_signal_handler(int signum, siginfo_t *si, void *uc)
0190 {
0191     if (signum == SIGSEGV || signum == SIGILL)
0192         siglongjmp(jmpbuf, 1);
0193 }
0194 
0195 /* check that a corrupted PAC results in SIGSEGV or SIGILL */
0196 TEST(corrupt_pac)
0197 {
0198     struct sigaction sa;
0199 
0200     ASSERT_PAUTH_ENABLED();
0201     if (sigsetjmp(jmpbuf, 1) == 0) {
0202         sa.sa_sigaction = pac_signal_handler;
0203         sa.sa_flags = SA_SIGINFO | SA_RESETHAND;
0204         sigemptyset(&sa.sa_mask);
0205 
0206         sigaction(SIGSEGV, &sa, NULL);
0207         sigaction(SIGILL, &sa, NULL);
0208 
0209         pac_corruptor();
0210         ASSERT_TRUE(0) TH_LOG("SIGSEGV/SIGILL signal did not occur");
0211     }
0212 }
0213 
0214 /*
0215  * There are no separate pac* and aut* controls so checking only the pac*
0216  * instructions is sufficient
0217  */
0218 TEST(pac_instructions_not_nop)
0219 {
0220     size_t keyia = 0;
0221     size_t keyib = 0;
0222     size_t keyda = 0;
0223     size_t keydb = 0;
0224 
0225     ASSERT_PAUTH_ENABLED();
0226 
0227     for (int i = 0; i < PAC_COLLISION_ATTEMPTS; i++) {
0228         keyia |= keyia_sign(i) & PAC_MASK;
0229         keyib |= keyib_sign(i) & PAC_MASK;
0230         keyda |= keyda_sign(i) & PAC_MASK;
0231         keydb |= keydb_sign(i) & PAC_MASK;
0232     }
0233 
0234     ASSERT_NE(0, keyia) TH_LOG("keyia instructions did nothing");
0235     ASSERT_NE(0, keyib) TH_LOG("keyib instructions did nothing");
0236     ASSERT_NE(0, keyda) TH_LOG("keyda instructions did nothing");
0237     ASSERT_NE(0, keydb) TH_LOG("keydb instructions did nothing");
0238 }
0239 
0240 TEST(pac_instructions_not_nop_generic)
0241 {
0242     size_t keyg = 0;
0243 
0244     ASSERT_GENERIC_PAUTH_ENABLED();
0245 
0246     for (int i = 0; i < PAC_COLLISION_ATTEMPTS; i++)
0247         keyg |= keyg_sign(i) & PAC_MASK;
0248 
0249     ASSERT_NE(0, keyg)  TH_LOG("keyg instructions did nothing");
0250 }
0251 
0252 TEST(single_thread_different_keys)
0253 {
0254     int same = 10;
0255     int nkeys = NKEYS;
0256     int tmp;
0257     struct signatures signed_vals;
0258     unsigned long hwcaps = getauxval(AT_HWCAP);
0259 
0260     /* generic and data key instructions are not in NOP space. This prevents a SIGILL */
0261     ASSERT_PAUTH_ENABLED();
0262     if (!(hwcaps & HWCAP_PACG)) {
0263         TH_LOG("WARNING: Generic PAUTH not enabled. Skipping generic key checks");
0264         nkeys = NKEYS - 1;
0265     }
0266 
0267     /*
0268      * In Linux the PAC field can be up to 7 bits wide. Even if keys are
0269      * different, there is about 5% chance for PACs to collide with
0270      * different addresses. This chance rapidly increases with fewer bits
0271      * allocated for the PAC (e.g. wider address). A comparison of the keys
0272      * directly will be more reliable.
0273      * All signed values need to be different at least once out of n
0274      * attempts to be certain that the keys are different
0275      */
0276     for (int i = 0; i < PAC_COLLISION_ATTEMPTS; i++) {
0277         if (nkeys == NKEYS)
0278             sign_all(&signed_vals, i);
0279         else
0280             sign_specific(&signed_vals, i);
0281 
0282         tmp = n_same_single_set(&signed_vals, nkeys);
0283         if (tmp < same)
0284             same = tmp;
0285     }
0286 
0287     ASSERT_EQ(0, same) TH_LOG("%d keys clashed every time", same);
0288 }
0289 
0290 /*
0291  * fork() does not change keys. Only exec() does so call a worker program.
0292  * Its only job is to sign a value and report back the resutls
0293  */
0294 TEST(exec_changed_keys)
0295 {
0296     struct signatures new_keys;
0297     struct signatures old_keys;
0298     int ret;
0299     int same = 10;
0300     int nkeys = NKEYS;
0301     unsigned long hwcaps = getauxval(AT_HWCAP);
0302 
0303     /* generic and data key instructions are not in NOP space. This prevents a SIGILL */
0304     ASSERT_PAUTH_ENABLED();
0305     if (!(hwcaps & HWCAP_PACG)) {
0306         TH_LOG("WARNING: Generic PAUTH not enabled. Skipping generic key checks");
0307         nkeys = NKEYS - 1;
0308     }
0309 
0310     for (int i = 0; i < PAC_COLLISION_ATTEMPTS; i++) {
0311         ret = exec_sign_all(&new_keys, i);
0312         ASSERT_EQ(0, ret) TH_LOG("failed to run worker");
0313 
0314         if (nkeys == NKEYS)
0315             sign_all(&old_keys, i);
0316         else
0317             sign_specific(&old_keys, i);
0318 
0319         ret = n_same(&old_keys, &new_keys, nkeys);
0320         if (ret < same)
0321             same = ret;
0322     }
0323 
0324     ASSERT_EQ(0, same) TH_LOG("exec() did not change %d keys", same);
0325 }
0326 
0327 TEST(context_switch_keep_keys)
0328 {
0329     int ret;
0330     struct signatures trash;
0331     struct signatures before;
0332     struct signatures after;
0333 
0334     ASSERT_PAUTH_ENABLED();
0335 
0336     sign_specific(&before, ARBITRARY_VALUE);
0337 
0338     /* will context switch with a process with different keys at least once */
0339     ret = exec_sign_all(&trash, ARBITRARY_VALUE);
0340     ASSERT_EQ(0, ret) TH_LOG("failed to run worker");
0341 
0342     sign_specific(&after, ARBITRARY_VALUE);
0343 
0344     ASSERT_EQ(before.keyia, after.keyia) TH_LOG("keyia changed after context switching");
0345     ASSERT_EQ(before.keyib, after.keyib) TH_LOG("keyib changed after context switching");
0346     ASSERT_EQ(before.keyda, after.keyda) TH_LOG("keyda changed after context switching");
0347     ASSERT_EQ(before.keydb, after.keydb) TH_LOG("keydb changed after context switching");
0348 }
0349 
0350 TEST(context_switch_keep_keys_generic)
0351 {
0352     int ret;
0353     struct signatures trash;
0354     size_t before;
0355     size_t after;
0356 
0357     ASSERT_GENERIC_PAUTH_ENABLED();
0358 
0359     before = keyg_sign(ARBITRARY_VALUE);
0360 
0361     /* will context switch with a process with different keys at least once */
0362     ret = exec_sign_all(&trash, ARBITRARY_VALUE);
0363     ASSERT_EQ(0, ret) TH_LOG("failed to run worker");
0364 
0365     after = keyg_sign(ARBITRARY_VALUE);
0366 
0367     ASSERT_EQ(before, after) TH_LOG("keyg changed after context switching");
0368 }
0369 
0370 TEST_HARNESS_MAIN