tst-pkey.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. /* Tests for memory protection keys.
  2. Copyright (C) 2017-2019 Free Software Foundation, Inc.
  3. This file is part of the GNU C Library.
  4. The GNU C Library is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU Lesser General Public
  6. License as published by the Free Software Foundation; either
  7. version 2.1 of the License, or (at your option) any later version.
  8. The GNU C Library is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public
  13. License along with the GNU C Library; if not, see
  14. <http://www.gnu.org/licenses/>. */
  15. #include <errno.h>
  16. #include <inttypes.h>
  17. #include <setjmp.h>
  18. #include <stdbool.h>
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <string.h>
  22. #include <support/check.h>
  23. #include <support/support.h>
  24. #include <support/test-driver.h>
  25. #include <support/xsignal.h>
  26. #include <support/xthread.h>
  27. #include <support/xunistd.h>
  28. #include <sys/mman.h>
  29. /* Used to force threads to wait until the main thread has set up the
  30. keys as intended. */
  31. static pthread_barrier_t barrier;
  32. /* The keys used for testing. These have been allocated with access
  33. rights set based on their array index. */
  34. enum { key_count = 4 };
  35. static int keys[key_count];
  36. static volatile int *pages[key_count];
  37. /* Used to report results from the signal handler. */
  38. static volatile void *sigsegv_addr;
  39. static volatile int sigsegv_code;
  40. static volatile int sigsegv_pkey;
  41. static sigjmp_buf sigsegv_jmp;
  42. /* Used to handle expected read or write faults. */
  43. static void
  44. sigsegv_handler (int signum, siginfo_t *info, void *context)
  45. {
  46. sigsegv_addr = info->si_addr;
  47. sigsegv_code = info->si_code;
  48. sigsegv_pkey = info->si_pkey;
  49. siglongjmp (sigsegv_jmp, 2);
  50. }
  51. static const struct sigaction sigsegv_sigaction =
  52. {
  53. .sa_flags = SA_RESETHAND | SA_SIGINFO,
  54. .sa_sigaction = &sigsegv_handler,
  55. };
  56. /* Check if PAGE is readable (if !WRITE) or writable (if WRITE). */
  57. static bool
  58. check_page_access (int page, bool write)
  59. {
  60. /* This is needed to work around bug 22396: On x86-64, siglongjmp
  61. does not restore the protection key access rights for the current
  62. thread. We restore only the access rights for the keys under
  63. test. (This is not a general solution to this problem, but it
  64. allows testing to proceed after a fault.) */
  65. unsigned saved_rights[key_count];
  66. for (int i = 0; i < key_count; ++i)
  67. saved_rights[i] = pkey_get (keys[i]);
  68. volatile int *addr = pages[page];
  69. if (test_verbose > 0)
  70. {
  71. printf ("info: checking access at %p (page %d) for %s\n",
  72. addr, page, write ? "writing" : "reading");
  73. }
  74. int result = sigsetjmp (sigsegv_jmp, 1);
  75. if (result == 0)
  76. {
  77. xsigaction (SIGSEGV, &sigsegv_sigaction, NULL);
  78. if (write)
  79. *addr = 3;
  80. else
  81. (void) *addr;
  82. xsignal (SIGSEGV, SIG_DFL);
  83. if (test_verbose > 0)
  84. puts (" --> access allowed");
  85. return true;
  86. }
  87. else
  88. {
  89. xsignal (SIGSEGV, SIG_DFL);
  90. if (test_verbose > 0)
  91. puts (" --> access denied");
  92. TEST_COMPARE (result, 2);
  93. TEST_COMPARE ((uintptr_t) sigsegv_addr, (uintptr_t) addr);
  94. TEST_COMPARE (sigsegv_code, SEGV_PKUERR);
  95. TEST_COMPARE (sigsegv_pkey, keys[page]);
  96. for (int i = 0; i < key_count; ++i)
  97. TEST_COMPARE (pkey_set (keys[i], saved_rights[i]), 0);
  98. return false;
  99. }
  100. }
  101. static volatile sig_atomic_t sigusr1_handler_ran;
  102. /* Used to check that access is revoked in signal handlers. */
  103. static void
  104. sigusr1_handler (int signum)
  105. {
  106. TEST_COMPARE (signum, SIGUSR1);
  107. for (int i = 0; i < key_count; ++i)
  108. TEST_COMPARE (pkey_get (keys[i]), PKEY_DISABLE_ACCESS);
  109. sigusr1_handler_ran = 1;
  110. }
  111. /* Used to report results from other threads. */
  112. struct thread_result
  113. {
  114. int access_rights[key_count];
  115. pthread_t next_thread;
  116. };
  117. /* Return the thread's access rights for the keys under test. */
  118. static void *
  119. get_thread_func (void *closure)
  120. {
  121. struct thread_result *result = xmalloc (sizeof (*result));
  122. for (int i = 0; i < key_count; ++i)
  123. result->access_rights[i] = pkey_get (keys[i]);
  124. memset (&result->next_thread, 0, sizeof (result->next_thread));
  125. return result;
  126. }
  127. /* Wait for initialization and then check that the current thread does
  128. not have access through the keys under test. */
  129. static void *
  130. delayed_thread_func (void *closure)
  131. {
  132. bool check_access = *(bool *) closure;
  133. pthread_barrier_wait (&barrier);
  134. struct thread_result *result = get_thread_func (NULL);
  135. if (check_access)
  136. {
  137. /* Also check directly. This code should not run with other
  138. threads in parallel because of the SIGSEGV handler which is
  139. installed by check_page_access. */
  140. for (int i = 0; i < key_count; ++i)
  141. {
  142. TEST_VERIFY (!check_page_access (i, false));
  143. TEST_VERIFY (!check_page_access (i, true));
  144. }
  145. }
  146. result->next_thread = xpthread_create (NULL, get_thread_func, NULL);
  147. return result;
  148. }
  149. static int
  150. do_test (void)
  151. {
  152. long pagesize = xsysconf (_SC_PAGESIZE);
  153. /* pkey_mprotect with key -1 should work even when there is no
  154. protection key support. */
  155. {
  156. int *page = xmmap (NULL, pagesize, PROT_NONE,
  157. MAP_ANONYMOUS | MAP_PRIVATE, -1);
  158. TEST_COMPARE (pkey_mprotect (page, pagesize, PROT_READ | PROT_WRITE, -1),
  159. 0);
  160. volatile int *vpage = page;
  161. *vpage = 5;
  162. TEST_COMPARE (*vpage, 5);
  163. xmunmap (page, pagesize);
  164. }
  165. xpthread_barrier_init (&barrier, NULL, 2);
  166. bool delayed_thread_check_access = true;
  167. pthread_t delayed_thread = xpthread_create
  168. (NULL, &delayed_thread_func, &delayed_thread_check_access);
  169. keys[0] = pkey_alloc (0, 0);
  170. if (keys[0] < 0)
  171. {
  172. if (errno == ENOSYS)
  173. FAIL_UNSUPPORTED
  174. ("kernel does not support memory protection keys");
  175. if (errno == EINVAL)
  176. FAIL_UNSUPPORTED
  177. ("CPU does not support memory protection keys: %m");
  178. FAIL_EXIT1 ("pkey_alloc: %m");
  179. }
  180. TEST_COMPARE (pkey_get (keys[0]), 0);
  181. for (int i = 1; i < key_count; ++i)
  182. {
  183. keys[i] = pkey_alloc (0, i);
  184. if (keys[i] < 0)
  185. FAIL_EXIT1 ("pkey_alloc (0, %d): %m", i);
  186. /* pkey_alloc is supposed to change the current thread's access
  187. rights for the new key. */
  188. TEST_COMPARE (pkey_get (keys[i]), i);
  189. }
  190. /* Check that all the keys have the expected access rights for the
  191. current thread. */
  192. for (int i = 0; i < key_count; ++i)
  193. TEST_COMPARE (pkey_get (keys[i]), i);
  194. /* Allocate a test page for each key. */
  195. for (int i = 0; i < key_count; ++i)
  196. {
  197. pages[i] = xmmap (NULL, pagesize, PROT_READ | PROT_WRITE,
  198. MAP_ANONYMOUS | MAP_PRIVATE, -1);
  199. TEST_COMPARE (pkey_mprotect ((void *) pages[i], pagesize,
  200. PROT_READ | PROT_WRITE, keys[i]), 0);
  201. }
  202. /* Check that the initial thread does not have access to the new
  203. keys. */
  204. {
  205. pthread_barrier_wait (&barrier);
  206. struct thread_result *result = xpthread_join (delayed_thread);
  207. for (int i = 0; i < key_count; ++i)
  208. TEST_COMPARE (result->access_rights[i],
  209. PKEY_DISABLE_ACCESS);
  210. struct thread_result *result2 = xpthread_join (result->next_thread);
  211. for (int i = 0; i < key_count; ++i)
  212. TEST_COMPARE (result->access_rights[i],
  213. PKEY_DISABLE_ACCESS);
  214. free (result);
  215. free (result2);
  216. }
  217. /* Check that the current thread access rights are inherited by new
  218. threads. */
  219. {
  220. pthread_t get_thread = xpthread_create (NULL, get_thread_func, NULL);
  221. struct thread_result *result = xpthread_join (get_thread);
  222. for (int i = 0; i < key_count; ++i)
  223. TEST_COMPARE (result->access_rights[i], i);
  224. free (result);
  225. }
  226. for (int i = 0; i < key_count; ++i)
  227. TEST_COMPARE (pkey_get (keys[i]), i);
  228. /* Check that in a signal handler, there is no access. */
  229. xsignal (SIGUSR1, &sigusr1_handler);
  230. xraise (SIGUSR1);
  231. xsignal (SIGUSR1, SIG_DFL);
  232. TEST_COMPARE (sigusr1_handler_ran, 1);
  233. /* The first key results in a writable page. */
  234. TEST_VERIFY (check_page_access (0, false));
  235. TEST_VERIFY (check_page_access (0, true));
  236. /* The other keys do not. */
  237. for (int i = 1; i < key_count; ++i)
  238. {
  239. if (test_verbose)
  240. printf ("info: checking access for key %d, bits 0x%x\n",
  241. i, pkey_get (keys[i]));
  242. for (int j = 0; j < key_count; ++j)
  243. TEST_COMPARE (pkey_get (keys[j]), j);
  244. if (i & PKEY_DISABLE_ACCESS)
  245. {
  246. TEST_VERIFY (!check_page_access (i, false));
  247. TEST_VERIFY (!check_page_access (i, true));
  248. }
  249. else
  250. {
  251. TEST_VERIFY (i & PKEY_DISABLE_WRITE);
  252. TEST_VERIFY (check_page_access (i, false));
  253. TEST_VERIFY (!check_page_access (i, true));
  254. }
  255. }
  256. /* But if we set the current thread's access rights, we gain
  257. access. */
  258. for (int do_write = 0; do_write < 2; ++do_write)
  259. for (int allowed_key = 0; allowed_key < key_count; ++allowed_key)
  260. {
  261. for (int i = 0; i < key_count; ++i)
  262. if (i == allowed_key)
  263. {
  264. if (do_write)
  265. TEST_COMPARE (pkey_set (keys[i], 0), 0);
  266. else
  267. TEST_COMPARE (pkey_set (keys[i], PKEY_DISABLE_WRITE), 0);
  268. }
  269. else
  270. TEST_COMPARE (pkey_set (keys[i], PKEY_DISABLE_ACCESS), 0);
  271. if (test_verbose)
  272. printf ("info: key %d is allowed access for %s\n",
  273. allowed_key, do_write ? "writing" : "reading");
  274. for (int i = 0; i < key_count; ++i)
  275. if (i == allowed_key)
  276. {
  277. TEST_VERIFY (check_page_access (i, false));
  278. TEST_VERIFY (check_page_access (i, true) == do_write);
  279. }
  280. else
  281. {
  282. TEST_VERIFY (!check_page_access (i, false));
  283. TEST_VERIFY (!check_page_access (i, true));
  284. }
  285. }
  286. /* Restore access to all keys, and launch a thread which should
  287. inherit that access. */
  288. for (int i = 0; i < key_count; ++i)
  289. {
  290. TEST_COMPARE (pkey_set (keys[i], 0), 0);
  291. TEST_VERIFY (check_page_access (i, false));
  292. TEST_VERIFY (check_page_access (i, true));
  293. }
  294. delayed_thread_check_access = false;
  295. delayed_thread = xpthread_create
  296. (NULL, delayed_thread_func, &delayed_thread_check_access);
  297. TEST_COMPARE (pkey_free (keys[0]), 0);
  298. /* Second pkey_free will fail because the key has already been
  299. freed. */
  300. TEST_COMPARE (pkey_free (keys[0]),-1);
  301. TEST_COMPARE (errno, EINVAL);
  302. for (int i = 1; i < key_count; ++i)
  303. TEST_COMPARE (pkey_free (keys[i]), 0);
  304. /* Check what happens to running threads which have access to
  305. previously allocated protection keys. The implemented behavior
  306. is somewhat dubious: Ideally, pkey_free should revoke access to
  307. that key and pkey_alloc of the same (numeric) key should not
  308. implicitly confer access to already-running threads, but this is
  309. not what happens in practice. */
  310. {
  311. /* The limit is in place to avoid running indefinitely in case
  312. there many keys available. */
  313. int *keys_array = xcalloc (100000, sizeof (*keys_array));
  314. int keys_allocated = 0;
  315. while (keys_allocated < 100000)
  316. {
  317. int new_key = pkey_alloc (0, PKEY_DISABLE_WRITE);
  318. if (new_key < 0)
  319. {
  320. /* No key reuse observed before running out of keys. */
  321. TEST_COMPARE (errno, ENOSPC);
  322. break;
  323. }
  324. for (int i = 0; i < key_count; ++i)
  325. if (new_key == keys[i])
  326. {
  327. /* We allocated the key with disabled write access.
  328. This should affect the protection state of the
  329. existing page. */
  330. TEST_VERIFY (check_page_access (i, false));
  331. TEST_VERIFY (!check_page_access (i, true));
  332. xpthread_barrier_wait (&barrier);
  333. struct thread_result *result = xpthread_join (delayed_thread);
  334. /* The thread which was launched before should still have
  335. access to the key. */
  336. TEST_COMPARE (result->access_rights[i], 0);
  337. struct thread_result *result2
  338. = xpthread_join (result->next_thread);
  339. /* Same for a thread which is launched afterwards from
  340. the old thread. */
  341. TEST_COMPARE (result2->access_rights[i], 0);
  342. free (result);
  343. free (result2);
  344. keys_array[keys_allocated++] = new_key;
  345. goto after_key_search;
  346. }
  347. /* Save key for later deallocation. */
  348. keys_array[keys_allocated++] = new_key;
  349. }
  350. after_key_search:
  351. /* Deallocate the keys allocated for testing purposes. */
  352. for (int j = 0; j < keys_allocated; ++j)
  353. TEST_COMPARE (pkey_free (keys_array[j]), 0);
  354. free (keys_array);
  355. }
  356. for (int i = 0; i < key_count; ++i)
  357. xmunmap ((void *) pages[i], pagesize);
  358. xpthread_barrier_destroy (&barrier);
  359. return 0;
  360. }
  361. #include <support/test-driver.c>