0001
0002
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
0019
0020
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 \
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 \
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
0116
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
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
0166
0167
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
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
0216
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
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
0269
0270
0271
0272
0273
0274
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
0292
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
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
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
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