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