@@ -3166,7 +3166,7 @@ context switch, so it is more efficient.
@standards{Linux, sys/mman.h}
@safety{@prelim{}@mtsafe{}@assafe{}@acunsafe{@acucorrupt{}}}
Allocate a new protection key. The @var{flags} argument is reserved and
-must be zero. The @var{restrictions} argument specifies access rights
+must be zero. The @var{restrictions} argument specifies access restrictions
which are applied to the current thread (as if with @code{pkey_set}
below). Access rights of other threads are not changed.
@@ -3251,16 +3251,22 @@ not @math{-1}.
@end table
@end deftypefun
-@deftypefun int pkey_set (int @var{key}, unsigned int @var{rights})
+@deftypefun int pkey_set (int @var{key}, unsigned int @var{restrictions})
@standards{Linux, sys/mman.h}
@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
-Change the access rights of the current thread for memory pages with the
-protection key @var{key} to @var{rights}. If @var{rights} is zero, no
-additional access restrictions on top of the page protection flags are
-applied. Otherwise, @var{rights} is a combination of the following
-flags:
+Change the access restrictions of the current thread for memory pages with the
+protection key @var{key} to @var{restrictions}. If @var{restrictions} is
+@code{PKEY_UNRESTRICTED} (zero), no additional access restrictions on top of
+the page protection flags are applied. Otherwise, @var{restrictions} is a
+combination of the following flags:
@vtable @code
+@item PKEY_DISABLE_READ
+@standards{Linux, sys/mman.h}
+Subsequent attempts to read from memory with the specified protection
+key will fault. At present only AArch64 platforms with enabled Permission
+Overlay Extension support this type of restriction.
+
@item PKEY_DISABLE_WRITE
@standards{Linux, sys/mman.h}
Subsequent attempts to write to memory with the specified protection
@@ -3269,7 +3275,15 @@ key will fault.
@item PKEY_DISABLE_ACCESS
@standards{Linux, sys/mman.h}
Subsequent attempts to write to or read from memory with the specified
-protection key will fault.
+protection key will fault. On AArch64 platforms with enabled Permission
+Overlay Extension this restriction value has the same effect as combination
+of @code{PKEY_DISABLE_READ} and @code{PKEY_DISABLE_WRITE}.
+
+@item PKEY_DISABLE_EXECUTE
+@standards{Linux, sys/mman.h}
+Subsequent attempts to execute from memory with the specified protection
+key will fault. At present only AArch64 platforms with enabled Permission
+Overlay Extension support this type of restriction.
@end vtable
Operations not specified as flags are not restricted. In particular,
@@ -3291,17 +3305,21 @@ function:
@table @code
@item EINVAL
The system does not support the access rights restrictions expressed in
-the @var{rights} argument.
+the @var{restrictions} argument.
@end table
@end deftypefun
@deftypefun int pkey_get (int @var{key})
@standards{Linux, sys/mman.h}
@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
-Return the access rights of the current thread for memory pages with
-protection key @var{key}. The return value is zero or a combination of
+Return the access rights restrictions of the current thread for memory pages
+with protection key @var{key}. The return value is zero or a combination of
the @code{PKEY_DISABLE_}* flags; see the @code{pkey_set} function.
+The returned value should be checked for presence or absence of specific flags
+using bitwise operations. Comparing the returned value with any of the flags
+or their combination using equals will almost certainly fail.
+
Calling the @code{pkey_get} function with a protection key which was not
allocated by @code{pkey_alloc} results in undefined behavior. This
means that calling this function on systems which do not support memory
@@ -1,5 +1,8 @@
ifeq ($(subdir),misc)
sysdep_headers += sys/elf.h
+tests += \
+ tst-aarch64-pkey \
+ # tests
endif
ifeq ($(subdir),stdlib)
new file mode 100644
@@ -0,0 +1,53 @@
+/* Helper functions for manipulating memory protection keys.
+ Copyright (C) 2024 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
+ <https://www.gnu.org/licenses/>. */
+
+#ifndef _ARCH_PKEY_H
+#define _ARCH_PKEY_H
+
+#include <sys/cdefs.h>
+
+#define S1POE_PERM_NO_ACCESS 0b0000UL
+#define S1POE_PERM_R 0b0001UL
+#define S1POE_PERM_X 0b0010UL
+#define S1POE_PERM_RX 0b0011UL
+#define S1POE_PERM_W 0b0100UL
+#define S1POE_PERM_RW 0b0101UL
+#define S1POE_PERM_WX 0b0110UL
+#define S1POE_PERM_RWX 0b0111UL
+
+#define S1POE_PERM_MASK 0b1111UL
+
+#define S1POE_BITS_PER_POI 4UL
+
+/* Return the value of the POR_EL0 register. */
+static __always_inline unsigned long
+pkey_read (void)
+{
+ unsigned long r;
+ __asm__ volatile ("mrs %0, s3_3_c10_c2_4" : "=r" (r));
+ return r;
+}
+
+/* Overwrite the POR_EL0 register with VALUE. */
+static __always_inline void
+pkey_write (unsigned long value)
+{
+ __asm__ volatile ("msr s3_3_c10_c2_4, %0" : : "r" (value));
+}
+
+#endif /* _ARCH_PKEY_H */
@@ -118,3 +118,4 @@
#define HWCAP2_SME_SF8FMA (1UL << 60)
#define HWCAP2_SME_SF8DP4 (1UL << 61)
#define HWCAP2_SME_SF8DP2 (1UL << 62)
+#define HWCAP2_POE (1UL << 63)
@@ -26,6 +26,14 @@
#define PROT_BTI 0x10
#define PROT_MTE 0x20
+#ifdef __USE_GNU
+# define PKEY_UNRESTRICTED 0x0
+# define PKEY_DISABLE_ACCESS 0x1
+# define PKEY_DISABLE_WRITE 0x2
+# define PKEY_DISABLE_EXECUTE 0x4
+# define PKEY_DISABLE_READ 0x8
+#endif
+
#include <bits/mman-map-flags-generic.h>
/* Include generic Linux declarations. */
new file mode 100644
@@ -0,0 +1,73 @@
+/* Reading the per-thread memory protection key, AArch64 version.
+ Copyright (C) 2024 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
+ <https://www.gnu.org/licenses/>. */
+
+#include <arch-pkey.h>
+#include <errno.h>
+#include <sys/mman.h>
+
+int
+pkey_get (int key)
+{
+ if (key < 0 || key > 15)
+ {
+ __set_errno (EINVAL);
+ return -1;
+ }
+ unsigned long int por_el0 = pkey_read ();
+ unsigned long int perm = (por_el0 >> (S1POE_BITS_PER_POI * key))
+ & S1POE_PERM_MASK;
+
+ /* The following mapping between POR permission bits (4 bits)
+ and PKEY flags is supported:
+
+ -WXR POR to PKEY_ mapping
+ 0000 => DISABLE_ACCESS | DISABLE_READ | DISABLE_WRITE | DISABLE_EXECUTE
+ 0001 => DISABLE_WRITE | DISABLE_EXECUTE (read-only)
+ 0010 => DISABLE_ACCESS | DISABLE_READ | DISABLE_WRITE (execute-only)
+ 0011 => DISABLE_WRITE (read-execute)
+ 0100 => DISABLE_READ | DISABLE_EXECUTE (write-only)
+ 0101 => DISABLE_EXECUTE (read-write)
+ 0110 => DISABLE_READ (execute-write)
+ 0111 => UNRESTRICTED (no restrictions, read-write-execute)
+ else => undefined behavior
+
+ Note that pkey_set and pkey_alloc would only set these specific
+ values. The PKEY_DISABLE_ACCESS flag is redundant as it implies
+ PKEY_DISABLE_READ | PKEY_DISABLE_WRITE but is kept for backward
+ compatibility. */
+
+ if (perm == S1POE_PERM_NO_ACCESS)
+ return PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE | PKEY_DISABLE_EXECUTE
+ | PKEY_DISABLE_READ;
+ if (perm == S1POE_PERM_R)
+ return PKEY_DISABLE_WRITE | PKEY_DISABLE_EXECUTE;
+ if (perm == S1POE_PERM_X)
+ return PKEY_DISABLE_ACCESS | PKEY_DISABLE_READ | PKEY_DISABLE_WRITE;
+ if (perm == S1POE_PERM_RX)
+ return PKEY_DISABLE_WRITE;
+ if (perm == S1POE_PERM_W)
+ return PKEY_DISABLE_READ | PKEY_DISABLE_EXECUTE;
+ if (perm == S1POE_PERM_RW)
+ return PKEY_DISABLE_EXECUTE;
+ if (perm == S1POE_PERM_WX)
+ return PKEY_DISABLE_READ;
+ if (perm == S1POE_PERM_RWX)
+ return PKEY_UNRESTRICTED;
+
+ return PKEY_DISABLE_ACCESS; // undefined behavior
+}
new file mode 100644
@@ -0,0 +1,113 @@
+/* Changing the per-thread memory protection key, AArch64 version.
+ Copyright (C) 2024 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
+ <https://www.gnu.org/licenses/>. */
+
+#include <arch-pkey.h>
+#include <errno.h>
+#include <sys/mman.h>
+
+#define MAX_PKEY_RIGHTS (PKEY_DISABLE_ACCESS | \
+ PKEY_DISABLE_WRITE | PKEY_DISABLE_EXECUTE | PKEY_DISABLE_READ)
+
+int
+pkey_set (int key, unsigned int restrictions)
+{
+ if (key < 0 || key > 15 || restrictions > MAX_PKEY_RIGHTS)
+ {
+ __set_errno (EINVAL);
+ return -1;
+ }
+ unsigned long mask = S1POE_PERM_MASK << (S1POE_BITS_PER_POI * key);
+ unsigned long por_el0 = pkey_read ();
+ unsigned long perm;
+
+ /* POR ot PKEY mapping: -WXR
+ PKEY_UNRESTRICTED => 0111 (read-write-execute)
+ PKEY_DISABLE_ACCESS => removes R and W access
+ PKEY_DISABLE_READ => removes R access
+ PKEY_DISABLE_WRITE => removes W access
+ PKEY_DISABLE_EXECUTE => removes X access
+
+ Either of PKEY_DISABLE_ACCESS or PKEY_DISABLE_READ removes R access.
+ Either of PKEY_DISABLE_ACCESS or PKEY_DISABLE_WRITE removes W access.
+ Using PKEY_DISABLE_ACCESS along with only one of PKEY_DISABLE_READ or
+ PKEY_DISABLE_WRITE is considered to be in error.
+
+ Furthermore, for avoidance of doubt:
+
+ PKEY flags Permissions
+ rxwa -WXR
+ 1111 => 0000 S1POE_PERM_NO_ACCESS
+ 1110 => 0000 S1POE_PERM_NO_ACCESS
+ 1101 => EINVAL
+ 1100 => 0100 S1POE_PERM_W
+ 1011 => 0010 S1POE_PERM_X
+ 1010 => 0010 S1POE_PERM_X
+ 1001 => EINVAL
+ 1000 => 0110 S1POE_PERM_WX
+ 0111 => EINVAL
+ 0110 => 0001 S1POE_PERM_R
+ 0101 => 0000 S1POE_PERM_NO_ACCESS
+ 0100 => 0101 S1POE_PERM_RW
+ 0011 => EINVAL
+ 0010 => 0011 S1POE_PERM_RX
+ 0001 => 0010 S1POE_PERM_X
+ 0000 => 0111 S1POE_PERM_RWX */
+ switch (restrictions)
+ {
+ case PKEY_DISABLE_ACCESS | PKEY_DISABLE_READ | PKEY_DISABLE_WRITE
+ | PKEY_DISABLE_EXECUTE:
+ case PKEY_DISABLE_ACCESS | PKEY_DISABLE_EXECUTE:
+ case PKEY_DISABLE_ACCESS | PKEY_DISABLE_READ | PKEY_DISABLE_EXECUTE:
+ case PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE | PKEY_DISABLE_EXECUTE:
+ case PKEY_DISABLE_READ | PKEY_DISABLE_WRITE | PKEY_DISABLE_EXECUTE:
+ perm = S1POE_PERM_NO_ACCESS;
+ break;
+ case PKEY_DISABLE_READ | PKEY_DISABLE_EXECUTE:
+ perm = S1POE_PERM_W;
+ break;
+ case PKEY_DISABLE_ACCESS | PKEY_DISABLE_READ:
+ case PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE:
+ case PKEY_DISABLE_ACCESS | PKEY_DISABLE_READ | PKEY_DISABLE_WRITE:
+ case PKEY_DISABLE_READ | PKEY_DISABLE_WRITE:
+ case PKEY_DISABLE_ACCESS:
+ perm = S1POE_PERM_X;
+ break;
+ case PKEY_DISABLE_READ:
+ perm = S1POE_PERM_WX;
+ break;
+ case PKEY_DISABLE_WRITE | PKEY_DISABLE_EXECUTE:
+ perm = S1POE_PERM_R;
+ break;
+ case PKEY_DISABLE_EXECUTE:
+ perm = S1POE_PERM_RW;
+ break;
+ case PKEY_DISABLE_WRITE:
+ perm = S1POE_PERM_RX;
+ break;
+ case PKEY_UNRESTRICTED:
+ perm = S1POE_PERM_RWX;
+ break;
+ default:
+ __set_errno (EINVAL);
+ return -1;
+ }
+
+ por_el0 = (por_el0 & ~mask) | (perm << (S1POE_BITS_PER_POI * key));
+ pkey_write (por_el0);
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,181 @@
+/* AArch64 tests for memory protection keys.
+ Copyright (C) 2024 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
+ <https://www.gnu.org/licenses/>. */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <support/test-driver.h>
+#include <support/xsignal.h>
+#include <support/xunistd.h>
+#include <sys/mman.h>
+
+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");
+ FAIL_EXIT1 ("pkey_alloc: %m");
+ }
+
+ long int pagesize = xsysconf (_SC_PAGESIZE);
+
+ int *page = xmmap (NULL, pagesize, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE,
+ -1);
+ pkey = pkey_alloc (0, PKEY_UNRESTRICTED);
+
+ /* 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. */
+ unsigned int rrs[][2] = {
+ {
+ PKEY_UNRESTRICTED, // restriction being set
+ PKEY_UNRESTRICTED // expected restriction
+ },
+ {
+ 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 < (sizeof(rrs) / 2 / sizeof(unsigned int)); k++) {
+ TEST_COMPARE (k, rrs[k][0]);
+ TEST_COMPARE (pkey_set (pkey, rrs[k][0]), 0);
+ TEST_COMPARE (pkey_get (pkey), rrs[k][1]);
+ }
+
+ /* 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 <support/test-driver.c>
@@ -44,6 +44,7 @@
/* Access rights for pkey_alloc. */
# ifndef PKEY_DISABLE_ACCESS
+# define PKEY_UNRESTRICTED 0x0
# define PKEY_DISABLE_ACCESS 0x1
# define PKEY_DISABLE_WRITE 0x2
# endif
@@ -59,16 +60,16 @@ int memfd_create (const char *__name, unsigned int __flags) __THROW;
int mlock2 (const void *__addr, size_t __length, unsigned int __flags) __THROW;
/* Allocate a new protection key, with the PKEY_DISABLE_* bits
- specified in ACCESS_RIGHTS. The protection key mask for the
+ specified in RESTRICTIONS. The protection key mask for the
current thread is updated to match the access privilege for the new
key. */
-int pkey_alloc (unsigned int __flags, unsigned int __access_rights) __THROW;
+int pkey_alloc (unsigned int __flags, unsigned int __restrictions) __THROW;
/* Update the access rights for the current thread for KEY, which must
have been allocated using pkey_alloc. */
-int pkey_set (int __key, unsigned int __access_rights) __THROW;
+int pkey_set (int __key, unsigned int __restrictions) __THROW;
-/* Return the access rights for the current thread for KEY, which must
+/* Return the restrictions for the current thread for KEY, which must
have been allocated using pkey_alloc. */
int pkey_get (int __key) __THROW;
@@ -20,7 +20,7 @@
#include <sys/mman.h>
int
-__pkey_set (int key, unsigned int access_rights)
+__pkey_set (int key, unsigned int restrictions)
{
__set_errno (ENOSYS);
return -1;
@@ -120,7 +120,7 @@ sigusr1_handler (int signum)
TEST_COMPARE (signum, SIGUSR1);
for (int i = 0; i < key_count; ++i)
TEST_VERIFY (pkey_get (keys[i]) == PKEY_DISABLE_ACCESS
- || pkey_get (keys[i]) == i);
+ || (pkey_get (keys[i]) & i) == i);
sigusr1_handler_ran = 1;
}
@@ -185,12 +185,13 @@ do_test (void)
xmunmap (page, pagesize);
}
+ /* Create thread before setting up key in the current thread. */
xpthread_barrier_init (&barrier, NULL, 2);
bool delayed_thread_check_access = true;
pthread_t delayed_thread = xpthread_create
(NULL, &delayed_thread_func, &delayed_thread_check_access);
- keys[0] = pkey_alloc (0, 0);
+ keys[0] = pkey_alloc (0, PKEY_UNRESTRICTED);
if (keys[0] < 0)
{
if (errno == ENOSYS)
@@ -212,19 +213,47 @@ do_test (void)
("glibc does not support memory protection keys");
FAIL_EXIT1 ("pkey_get: %m");
}
+
+ /* Check that initial rights that are set via pkey_alloc
+ can be accessed via pkey_get. */
+ {
+ int pkey = -1;
+ pkey = pkey_alloc (0, PKEY_DISABLE_ACCESS);
+ TEST_COMPARE (pkey_get (pkey) & PKEY_DISABLE_ACCESS, PKEY_DISABLE_ACCESS);
+ pkey_free (pkey);
+ pkey = pkey_alloc (0, PKEY_DISABLE_WRITE);
+ TEST_COMPARE (pkey_get (pkey) & PKEY_DISABLE_WRITE, PKEY_DISABLE_WRITE);
+ pkey_free (pkey);
+ }
+
+ /* Check that unallocated pkey is not accepted by the
+ pkey_mprotect function. */
+ {
+ int pkey = -1;
+ pkey = pkey_alloc (0, PKEY_DISABLE_WRITE);
+ pkey_free (pkey);
+ int *page = xmmap (NULL, pagesize, PROT_NONE,
+ MAP_ANONYMOUS | MAP_PRIVATE, -1);
+ TEST_COMPARE (pkey_mprotect (page, pagesize, PROT_READ, pkey), -1);
+ TEST_COMPARE (errno, EINVAL);
+ xmunmap (page, pagesize);
+ }
+
for (int i = 1; i < key_count; ++i)
{
+ /* i == 1 corresponds to PKEY_DISABLE_ACCESS
+ i == 2 corresponds to PKEY_DISABLE_WRITE */
keys[i] = pkey_alloc (0, i);
if (keys[i] < 0)
FAIL_EXIT1 ("pkey_alloc (0, %d): %m", i);
/* pkey_alloc is supposed to change the current thread's access
rights for the new key. */
- TEST_COMPARE (pkey_get (keys[i]), i);
+ TEST_COMPARE (pkey_get (keys[i]) & i, i);
}
/* Check that all the keys have the expected access rights for the
current thread. */
for (int i = 0; i < key_count; ++i)
- TEST_COMPARE (pkey_get (keys[i]), i);
+ TEST_COMPARE (pkey_get (keys[i]) & i, i);
/* Allocate a test page for each key. */
for (int i = 0; i < key_count; ++i)
@@ -241,12 +270,12 @@ do_test (void)
pthread_barrier_wait (&barrier);
struct thread_result *result = xpthread_join (delayed_thread);
for (int i = 0; i < key_count; ++i)
- TEST_COMPARE (result->access_rights[i],
- PKEY_DISABLE_ACCESS);
+ TEST_COMPARE (result->access_rights[i] &
+ PKEY_DISABLE_ACCESS, PKEY_DISABLE_ACCESS);
struct thread_result *result2 = xpthread_join (result->next_thread);
for (int i = 0; i < key_count; ++i)
- TEST_COMPARE (result->access_rights[i],
- PKEY_DISABLE_ACCESS);
+ TEST_COMPARE (result->access_rights[i] &
+ PKEY_DISABLE_ACCESS, PKEY_DISABLE_ACCESS);
free (result);
free (result2);
}
@@ -257,12 +286,12 @@ do_test (void)
pthread_t get_thread = xpthread_create (NULL, get_thread_func, NULL);
struct thread_result *result = xpthread_join (get_thread);
for (int i = 0; i < key_count; ++i)
- TEST_COMPARE (result->access_rights[i], i);
+ TEST_COMPARE (result->access_rights[i] & i, i);
free (result);
}
for (int i = 0; i < key_count; ++i)
- TEST_COMPARE (pkey_get (keys[i]), i);
+ TEST_COMPARE (pkey_get (keys[i]) & i, i);
/* Check that in a signal handler, there is no access. */
xsignal (SIGUSR1, &sigusr1_handler);
@@ -281,7 +310,7 @@ do_test (void)
printf ("info: checking access for key %d, bits 0x%x\n",
i, pkey_get (keys[i]));
for (int j = 0; j < key_count; ++j)
- TEST_COMPARE (pkey_get (keys[j]), j);
+ TEST_COMPARE (pkey_get (keys[j]) & j, j);
if (i & PKEY_DISABLE_ACCESS)
{
TEST_VERIFY (!check_page_access (i, false));
@@ -304,7 +333,7 @@ do_test (void)
if (i == allowed_key)
{
if (do_write)
- TEST_COMPARE (pkey_set (keys[i], 0), 0);
+ TEST_COMPARE (pkey_set (keys[i], PKEY_UNRESTRICTED), 0);
else
TEST_COMPARE (pkey_set (keys[i], PKEY_DISABLE_WRITE), 0);
}
@@ -331,7 +360,7 @@ do_test (void)
inherit that access. */
for (int i = 0; i < key_count; ++i)
{
- TEST_COMPARE (pkey_set (keys[i], 0), 0);
+ TEST_COMPARE (pkey_set (keys[i], PKEY_UNRESTRICTED), 0);
TEST_VERIFY (check_page_access (i, false));
TEST_VERIFY (check_page_access (i, true));
}
@@ -355,7 +384,7 @@ do_test (void)
not what happens in practice. */
{
/* The limit is in place to avoid running indefinitely in case
- there many keys available. */
+ there are many keys available. */
int *keys_array = xcalloc (100000, sizeof (*keys_array));
int keys_allocated = 0;
while (keys_allocated < 100000)