pac.c 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. // SPDX-License-Identifier: GPL-2.0
  2. // Copyright (C) 2020 ARM Limited
  3. #define _GNU_SOURCE
  4. #include <sys/auxv.h>
  5. #include <sys/types.h>
  6. #include <sys/wait.h>
  7. #include <signal.h>
  8. #include <setjmp.h>
  9. #include <sched.h>
  10. #include "../../kselftest_harness.h"
  11. #include "helper.h"
  12. #define PAC_COLLISION_ATTEMPTS 10
  13. /*
  14. * The kernel sets TBID by default. So bits 55 and above should remain
  15. * untouched no matter what.
  16. * The VA space size is 48 bits. Bigger is opt-in.
  17. */
  18. #define PAC_MASK (~0xff80ffffffffffff)
  19. #define ARBITRARY_VALUE (0x1234)
  20. #define ASSERT_PAUTH_ENABLED() \
  21. do { \
  22. unsigned long hwcaps = getauxval(AT_HWCAP); \
  23. /* data key instructions are not in NOP space. This prevents a SIGILL */ \
  24. if (!(hwcaps & HWCAP_PACA)) \
  25. SKIP(return, "PAUTH not enabled"); \
  26. } while (0)
  27. #define ASSERT_GENERIC_PAUTH_ENABLED() \
  28. do { \
  29. unsigned long hwcaps = getauxval(AT_HWCAP); \
  30. /* generic key instructions are not in NOP space. This prevents a SIGILL */ \
  31. if (!(hwcaps & HWCAP_PACG)) \
  32. SKIP(return, "Generic PAUTH not enabled"); \
  33. } while (0)
  34. void sign_specific(struct signatures *sign, size_t val)
  35. {
  36. sign->keyia = keyia_sign(val);
  37. sign->keyib = keyib_sign(val);
  38. sign->keyda = keyda_sign(val);
  39. sign->keydb = keydb_sign(val);
  40. }
  41. void sign_all(struct signatures *sign, size_t val)
  42. {
  43. sign->keyia = keyia_sign(val);
  44. sign->keyib = keyib_sign(val);
  45. sign->keyda = keyda_sign(val);
  46. sign->keydb = keydb_sign(val);
  47. sign->keyg = keyg_sign(val);
  48. }
  49. int n_same(struct signatures *old, struct signatures *new, int nkeys)
  50. {
  51. int res = 0;
  52. res += old->keyia == new->keyia;
  53. res += old->keyib == new->keyib;
  54. res += old->keyda == new->keyda;
  55. res += old->keydb == new->keydb;
  56. if (nkeys == NKEYS)
  57. res += old->keyg == new->keyg;
  58. return res;
  59. }
  60. int n_same_single_set(struct signatures *sign, int nkeys)
  61. {
  62. size_t vals[nkeys];
  63. int same = 0;
  64. vals[0] = sign->keyia & PAC_MASK;
  65. vals[1] = sign->keyib & PAC_MASK;
  66. vals[2] = sign->keyda & PAC_MASK;
  67. vals[3] = sign->keydb & PAC_MASK;
  68. if (nkeys >= 4)
  69. vals[4] = sign->keyg & PAC_MASK;
  70. for (int i = 0; i < nkeys - 1; i++) {
  71. for (int j = i + 1; j < nkeys; j++) {
  72. if (vals[i] == vals[j])
  73. same += 1;
  74. }
  75. }
  76. return same;
  77. }
  78. int exec_sign_all(struct signatures *signed_vals, size_t val)
  79. {
  80. int new_stdin[2];
  81. int new_stdout[2];
  82. int status;
  83. int i;
  84. ssize_t ret;
  85. pid_t pid;
  86. cpu_set_t mask;
  87. ret = pipe(new_stdin);
  88. if (ret == -1) {
  89. perror("pipe returned error");
  90. return -1;
  91. }
  92. ret = pipe(new_stdout);
  93. if (ret == -1) {
  94. perror("pipe returned error");
  95. return -1;
  96. }
  97. /*
  98. * pin this process and all its children to a single CPU, so it can also
  99. * guarantee a context switch with its child
  100. */
  101. sched_getaffinity(0, sizeof(mask), &mask);
  102. for (i = 0; i < sizeof(cpu_set_t); i++)
  103. if (CPU_ISSET(i, &mask))
  104. break;
  105. CPU_ZERO(&mask);
  106. CPU_SET(i, &mask);
  107. sched_setaffinity(0, sizeof(mask), &mask);
  108. pid = fork();
  109. // child
  110. if (pid == 0) {
  111. dup2(new_stdin[0], STDIN_FILENO);
  112. if (ret == -1) {
  113. perror("dup2 returned error");
  114. exit(1);
  115. }
  116. dup2(new_stdout[1], STDOUT_FILENO);
  117. if (ret == -1) {
  118. perror("dup2 returned error");
  119. exit(1);
  120. }
  121. close(new_stdin[0]);
  122. close(new_stdin[1]);
  123. close(new_stdout[0]);
  124. close(new_stdout[1]);
  125. ret = execl("exec_target", "exec_target", (char *)NULL);
  126. if (ret == -1) {
  127. perror("exec returned error");
  128. exit(1);
  129. }
  130. }
  131. close(new_stdin[0]);
  132. close(new_stdout[1]);
  133. ret = write(new_stdin[1], &val, sizeof(size_t));
  134. if (ret == -1) {
  135. perror("write returned error");
  136. return -1;
  137. }
  138. /*
  139. * wait for the worker to finish, so that read() reads all data
  140. * will also context switch with worker so that this function can be used
  141. * for context switch tests
  142. */
  143. waitpid(pid, &status, 0);
  144. if (WIFEXITED(status) == 0) {
  145. fprintf(stderr, "worker exited unexpectedly\n");
  146. return -1;
  147. }
  148. if (WEXITSTATUS(status) != 0) {
  149. fprintf(stderr, "worker exited with error\n");
  150. return -1;
  151. }
  152. ret = read(new_stdout[0], signed_vals, sizeof(struct signatures));
  153. if (ret == -1) {
  154. perror("read returned error");
  155. return -1;
  156. }
  157. return 0;
  158. }
  159. sigjmp_buf jmpbuf;
  160. void pac_signal_handler(int signum, siginfo_t *si, void *uc)
  161. {
  162. if (signum == SIGSEGV || signum == SIGILL)
  163. siglongjmp(jmpbuf, 1);
  164. }
  165. /* check that a corrupted PAC results in SIGSEGV or SIGILL */
  166. TEST(corrupt_pac)
  167. {
  168. struct sigaction sa;
  169. ASSERT_PAUTH_ENABLED();
  170. if (sigsetjmp(jmpbuf, 1) == 0) {
  171. sa.sa_sigaction = pac_signal_handler;
  172. sa.sa_flags = SA_SIGINFO | SA_RESETHAND;
  173. sigemptyset(&sa.sa_mask);
  174. sigaction(SIGSEGV, &sa, NULL);
  175. sigaction(SIGILL, &sa, NULL);
  176. pac_corruptor();
  177. ASSERT_TRUE(0) TH_LOG("SIGSEGV/SIGILL signal did not occur");
  178. }
  179. }
  180. /*
  181. * There are no separate pac* and aut* controls so checking only the pac*
  182. * instructions is sufficient
  183. */
  184. TEST(pac_instructions_not_nop)
  185. {
  186. size_t keyia = 0;
  187. size_t keyib = 0;
  188. size_t keyda = 0;
  189. size_t keydb = 0;
  190. ASSERT_PAUTH_ENABLED();
  191. for (int i = 0; i < PAC_COLLISION_ATTEMPTS; i++) {
  192. keyia |= keyia_sign(i) & PAC_MASK;
  193. keyib |= keyib_sign(i) & PAC_MASK;
  194. keyda |= keyda_sign(i) & PAC_MASK;
  195. keydb |= keydb_sign(i) & PAC_MASK;
  196. }
  197. ASSERT_NE(0, keyia) TH_LOG("keyia instructions did nothing");
  198. ASSERT_NE(0, keyib) TH_LOG("keyib instructions did nothing");
  199. ASSERT_NE(0, keyda) TH_LOG("keyda instructions did nothing");
  200. ASSERT_NE(0, keydb) TH_LOG("keydb instructions did nothing");
  201. }
  202. TEST(pac_instructions_not_nop_generic)
  203. {
  204. size_t keyg = 0;
  205. ASSERT_GENERIC_PAUTH_ENABLED();
  206. for (int i = 0; i < PAC_COLLISION_ATTEMPTS; i++)
  207. keyg |= keyg_sign(i) & PAC_MASK;
  208. ASSERT_NE(0, keyg) TH_LOG("keyg instructions did nothing");
  209. }
  210. TEST(single_thread_different_keys)
  211. {
  212. int same = 10;
  213. int nkeys = NKEYS;
  214. int tmp;
  215. struct signatures signed_vals;
  216. unsigned long hwcaps = getauxval(AT_HWCAP);
  217. /* generic and data key instructions are not in NOP space. This prevents a SIGILL */
  218. ASSERT_PAUTH_ENABLED();
  219. if (!(hwcaps & HWCAP_PACG)) {
  220. TH_LOG("WARNING: Generic PAUTH not enabled. Skipping generic key checks");
  221. nkeys = NKEYS - 1;
  222. }
  223. /*
  224. * In Linux the PAC field can be up to 7 bits wide. Even if keys are
  225. * different, there is about 5% chance for PACs to collide with
  226. * different addresses. This chance rapidly increases with fewer bits
  227. * allocated for the PAC (e.g. wider address). A comparison of the keys
  228. * directly will be more reliable.
  229. * All signed values need to be different at least once out of n
  230. * attempts to be certain that the keys are different
  231. */
  232. for (int i = 0; i < PAC_COLLISION_ATTEMPTS; i++) {
  233. if (nkeys == NKEYS)
  234. sign_all(&signed_vals, i);
  235. else
  236. sign_specific(&signed_vals, i);
  237. tmp = n_same_single_set(&signed_vals, nkeys);
  238. if (tmp < same)
  239. same = tmp;
  240. }
  241. ASSERT_EQ(0, same) TH_LOG("%d keys clashed every time", same);
  242. }
  243. /*
  244. * fork() does not change keys. Only exec() does so call a worker program.
  245. * Its only job is to sign a value and report back the resutls
  246. */
  247. TEST(exec_changed_keys)
  248. {
  249. struct signatures new_keys;
  250. struct signatures old_keys;
  251. int ret;
  252. int same = 10;
  253. int nkeys = NKEYS;
  254. unsigned long hwcaps = getauxval(AT_HWCAP);
  255. /* generic and data key instructions are not in NOP space. This prevents a SIGILL */
  256. ASSERT_PAUTH_ENABLED();
  257. if (!(hwcaps & HWCAP_PACG)) {
  258. TH_LOG("WARNING: Generic PAUTH not enabled. Skipping generic key checks");
  259. nkeys = NKEYS - 1;
  260. }
  261. for (int i = 0; i < PAC_COLLISION_ATTEMPTS; i++) {
  262. ret = exec_sign_all(&new_keys, i);
  263. ASSERT_EQ(0, ret) TH_LOG("failed to run worker");
  264. if (nkeys == NKEYS)
  265. sign_all(&old_keys, i);
  266. else
  267. sign_specific(&old_keys, i);
  268. ret = n_same(&old_keys, &new_keys, nkeys);
  269. if (ret < same)
  270. same = ret;
  271. }
  272. ASSERT_EQ(0, same) TH_LOG("exec() did not change %d keys", same);
  273. }
  274. TEST(context_switch_keep_keys)
  275. {
  276. int ret;
  277. struct signatures trash;
  278. struct signatures before;
  279. struct signatures after;
  280. ASSERT_PAUTH_ENABLED();
  281. sign_specific(&before, ARBITRARY_VALUE);
  282. /* will context switch with a process with different keys at least once */
  283. ret = exec_sign_all(&trash, ARBITRARY_VALUE);
  284. ASSERT_EQ(0, ret) TH_LOG("failed to run worker");
  285. sign_specific(&after, ARBITRARY_VALUE);
  286. ASSERT_EQ(before.keyia, after.keyia) TH_LOG("keyia changed after context switching");
  287. ASSERT_EQ(before.keyib, after.keyib) TH_LOG("keyib changed after context switching");
  288. ASSERT_EQ(before.keyda, after.keyda) TH_LOG("keyda changed after context switching");
  289. ASSERT_EQ(before.keydb, after.keydb) TH_LOG("keydb changed after context switching");
  290. }
  291. TEST(context_switch_keep_keys_generic)
  292. {
  293. int ret;
  294. struct signatures trash;
  295. size_t before;
  296. size_t after;
  297. ASSERT_GENERIC_PAUTH_ENABLED();
  298. before = keyg_sign(ARBITRARY_VALUE);
  299. /* will context switch with a process with different keys at least once */
  300. ret = exec_sign_all(&trash, ARBITRARY_VALUE);
  301. ASSERT_EQ(0, ret) TH_LOG("failed to run worker");
  302. after = keyg_sign(ARBITRARY_VALUE);
  303. ASSERT_EQ(before, after) TH_LOG("keyg changed after context switching");
  304. }
  305. TEST_HARNESS_MAIN