/* AArch64 tests for memory protection keys. Copyright (C) 2024-2025 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include static sig_atomic_t sigusr1_handler_ran; static int pkey; /* On AArch64 access is revoked during signal handling for pkey > 0 because POR is reset to the default value 0x7. */ static void sigusr1_handler (int signum) { TEST_COMPARE (signum, SIGUSR1); TEST_COMPARE (pkey_get (pkey) & PKEY_DISABLE_ACCESS, PKEY_DISABLE_ACCESS); TEST_COMPARE (pkey_get (pkey) & PKEY_DISABLE_READ, PKEY_DISABLE_READ); TEST_COMPARE (pkey_get (pkey) & PKEY_DISABLE_WRITE, PKEY_DISABLE_WRITE); TEST_COMPARE (pkey_get (pkey) & PKEY_DISABLE_EXECUTE, PKEY_DISABLE_EXECUTE); sigusr1_handler_ran += 1; } static int do_test (void) { pkey = pkey_alloc (0, PKEY_UNRESTRICTED); if (pkey < 0) { if (errno == ENOSYS || errno == EINVAL) FAIL_UNSUPPORTED ("kernel or CPU does not support memory protection keys"); if (errno == ENOSPC) FAIL_UNSUPPORTED ("no keys available or kernel does not support memory" " protection keys"); FAIL_EXIT1 ("pkey_alloc: %m"); } long int pagesize = xsysconf (_SC_PAGESIZE); int *page = xmmap (NULL, pagesize, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1); /* On AArch64 pkey == 0 is reserved and should never be allocated. */ TEST_VERIFY (pkey > 0); TEST_COMPARE (pkey_get(pkey), PKEY_UNRESTRICTED); /* Check that access is revoked during signal handling with initial rights being set to no restrictions. */ TEST_COMPARE (pkey_mprotect ((void *) page, pagesize, PROT_READ | PROT_WRITE, pkey), 0); xsignal (SIGUSR1, &sigusr1_handler); xraise (SIGUSR1); xsignal (SIGUSR1, SIG_DFL); TEST_COMPARE (sigusr1_handler_ran, 1); /* Check that access is revoked during signal handling with initial rights being set to PKEY_DISABLE_WRITE. */ TEST_COMPARE (pkey_set (pkey, PKEY_DISABLE_WRITE), 0); xsignal (SIGUSR1, &sigusr1_handler); xraise (SIGUSR1); xsignal (SIGUSR1, SIG_DFL); TEST_COMPARE (sigusr1_handler_ran, 2); /* Check that all combinations of PKEY flags used in pkey_set result in consistent values obtained via pkey_get. Note that whenever flags PKEY_DISABLE_READ and PKEY_DISABLE_WRITE are set, the PKEY_DISABLE_ACCESS is also set. */ struct { unsigned int set; unsigned int expected; } rrs[] = { { PKEY_UNRESTRICTED, PKEY_UNRESTRICTED }, { PKEY_DISABLE_ACCESS, PKEY_DISABLE_ACCESS | PKEY_DISABLE_READ | PKEY_DISABLE_WRITE }, { PKEY_DISABLE_WRITE, PKEY_DISABLE_WRITE }, { PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE, PKEY_DISABLE_ACCESS | PKEY_DISABLE_READ | PKEY_DISABLE_WRITE }, { PKEY_DISABLE_EXECUTE, PKEY_DISABLE_EXECUTE }, { PKEY_DISABLE_ACCESS | PKEY_DISABLE_EXECUTE, PKEY_DISABLE_ACCESS | PKEY_DISABLE_READ | PKEY_DISABLE_WRITE | PKEY_DISABLE_EXECUTE }, { PKEY_DISABLE_WRITE | PKEY_DISABLE_EXECUTE, PKEY_DISABLE_WRITE | PKEY_DISABLE_EXECUTE }, { PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE | PKEY_DISABLE_EXECUTE, PKEY_DISABLE_ACCESS | PKEY_DISABLE_READ | PKEY_DISABLE_WRITE | PKEY_DISABLE_EXECUTE }, { PKEY_DISABLE_READ, PKEY_DISABLE_READ }, { PKEY_DISABLE_ACCESS | PKEY_DISABLE_READ, PKEY_DISABLE_ACCESS | PKEY_DISABLE_READ | PKEY_DISABLE_WRITE }, { PKEY_DISABLE_WRITE | PKEY_DISABLE_READ, PKEY_DISABLE_ACCESS | PKEY_DISABLE_READ | PKEY_DISABLE_WRITE }, { PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE | PKEY_DISABLE_READ, PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE | PKEY_DISABLE_READ }, { PKEY_DISABLE_EXECUTE | PKEY_DISABLE_READ, PKEY_DISABLE_EXECUTE | PKEY_DISABLE_READ }, { PKEY_DISABLE_ACCESS | PKEY_DISABLE_EXECUTE | PKEY_DISABLE_READ, PKEY_DISABLE_ACCESS | PKEY_DISABLE_READ | PKEY_DISABLE_WRITE | PKEY_DISABLE_EXECUTE }, { PKEY_DISABLE_WRITE | PKEY_DISABLE_EXECUTE | PKEY_DISABLE_READ, PKEY_DISABLE_ACCESS | PKEY_DISABLE_READ | PKEY_DISABLE_WRITE | PKEY_DISABLE_EXECUTE }, { PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE | PKEY_DISABLE_EXECUTE | PKEY_DISABLE_READ, PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE | PKEY_DISABLE_EXECUTE | PKEY_DISABLE_READ }, }; for (int k = 0; k < (array_length (rrs) / 2); k++) { TEST_COMPARE (k, rrs[k].set); TEST_COMPARE (pkey_set (pkey, rrs[k].set), 0); TEST_COMPARE (pkey_get (pkey), rrs[k].expected); } /* Check that restrictions above maximum allowed value are rejected. */ TEST_COMPARE (pkey_set (pkey, 16), -1); TEST_COMPARE (errno, EINVAL); TEST_COMPARE (pkey_free (pkey), 0); xmunmap ((void *) page, pagesize); return 0; } #include