aboutsummaryrefslogtreecommitdiff
path: root/sysdeps
diff options
context:
space:
mode:
Diffstat (limited to 'sysdeps')
-rw-r--r--sysdeps/unix/sysv/linux/Makefile6
-rw-r--r--sysdeps/unix/sysv/linux/Versions1
-rw-r--r--sysdeps/unix/sysv/linux/aarch64/libc.abilist5
-rw-r--r--sysdeps/unix/sysv/linux/alpha/libc.abilist5
-rw-r--r--sysdeps/unix/sysv/linux/arm/libc.abilist5
-rw-r--r--sysdeps/unix/sysv/linux/bits/mman-shared.h28
-rw-r--r--sysdeps/unix/sysv/linux/bits/siginfo-consts.h6
-rw-r--r--sysdeps/unix/sysv/linux/hppa/libc.abilist5
-rw-r--r--sysdeps/unix/sysv/linux/i386/libc.abilist5
-rw-r--r--sysdeps/unix/sysv/linux/ia64/libc.abilist5
-rw-r--r--sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist5
-rw-r--r--sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist5
-rw-r--r--sysdeps/unix/sysv/linux/microblaze/libc.abilist5
-rw-r--r--sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist5
-rw-r--r--sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist5
-rw-r--r--sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist5
-rw-r--r--sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist5
-rw-r--r--sysdeps/unix/sysv/linux/nios2/libc.abilist5
-rw-r--r--sysdeps/unix/sysv/linux/pkey_get.c26
-rw-r--r--sysdeps/unix/sysv/linux/pkey_mprotect.c37
-rw-r--r--sysdeps/unix/sysv/linux/pkey_set.c26
-rw-r--r--sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist5
-rw-r--r--sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist5
-rw-r--r--sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist5
-rw-r--r--sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist5
-rw-r--r--sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist5
-rw-r--r--sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist5
-rw-r--r--sysdeps/unix/sysv/linux/sh/libc.abilist5
-rw-r--r--sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist5
-rw-r--r--sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist5
-rw-r--r--sysdeps/unix/sysv/linux/syscalls.list2
-rw-r--r--sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist5
-rw-r--r--sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist5
-rw-r--r--sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist5
-rw-r--r--sysdeps/unix/sysv/linux/tst-pkey.c399
-rw-r--r--sysdeps/unix/sysv/linux/x86/arch-pkey.h40
-rw-r--r--sysdeps/unix/sysv/linux/x86/pkey_get.c33
-rw-r--r--sysdeps/unix/sysv/linux/x86/pkey_set.c35
-rw-r--r--sysdeps/unix/sysv/linux/x86_64/64/libc.abilist5
-rw-r--r--sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist5
40 files changed, 776 insertions, 3 deletions
diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
index 478f7e3d4d..8a17828d9d 100644
--- a/sysdeps/unix/sysv/linux/Makefile
+++ b/sysdeps/unix/sysv/linux/Makefile
@@ -18,7 +18,7 @@ sysdep_routines += clone umount umount2 readahead \
setfsuid setfsgid epoll_pwait signalfd \
eventfd eventfd_read eventfd_write prlimit \
personality epoll_wait tee vmsplice splice \
- open_by_handle_at mlock2
+ open_by_handle_at mlock2 pkey_mprotect pkey_set pkey_get
CFLAGS-gethostid.c = -fexceptions
CFLAGS-tee.c = -fexceptions -fasynchronous-unwind-tables
@@ -44,7 +44,7 @@ sysdep_headers += sys/mount.h sys/acct.h sys/sysctl.h \
tests += tst-clone tst-clone2 tst-clone3 tst-fanotify tst-personality \
tst-quota tst-sync_file_range tst-sysconf-iov_max tst-ttyname \
- test-errno-linux tst-memfd_create tst-mlock2
+ test-errno-linux tst-memfd_create tst-mlock2 tst-pkey
# Generate the list of SYS_* macros for the system calls (__NR_*
# macros). The file syscall-names.list contains all possible system
@@ -92,6 +92,8 @@ $(objpfx)tst-syscall-list.out: \
# Separate object file for access to the constant from the UAPI header.
$(objpfx)tst-sysconf-iov_max: $(objpfx)tst-sysconf-iov_max-uapi.o
+$(objpfx)tst-pkey: $(shared-thread-library)
+
endif # $(subdir) == misc
ifeq ($(subdir),time)
diff --git a/sysdeps/unix/sysv/linux/Versions b/sysdeps/unix/sysv/linux/Versions
index e799b62285..336c13b57d 100644
--- a/sysdeps/unix/sysv/linux/Versions
+++ b/sysdeps/unix/sysv/linux/Versions
@@ -169,6 +169,7 @@ libc {
GLIBC_2.27 {
memfd_create;
mlock2;
+ pkey_alloc; pkey_free; pkey_set; pkey_get; pkey_mprotect;
}
GLIBC_PRIVATE {
# functions used in other libraries
diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
index 3448d62cee..bae2ebc087 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
@@ -2108,6 +2108,11 @@ GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
GLIBC_2.27 mlock2 F
+GLIBC_2.27 pkey_alloc F
+GLIBC_2.27 pkey_free F
+GLIBC_2.27 pkey_get F
+GLIBC_2.27 pkey_mprotect F
+GLIBC_2.27 pkey_set F
GLIBC_2.27 strfromf128 F
GLIBC_2.27 strfromf64x F
GLIBC_2.27 strtof128 F
diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
index d064f5445e..16c3c905cb 100644
--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
@@ -2019,6 +2019,11 @@ GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
GLIBC_2.27 mlock2 F
+GLIBC_2.27 pkey_alloc F
+GLIBC_2.27 pkey_free F
+GLIBC_2.27 pkey_get F
+GLIBC_2.27 pkey_mprotect F
+GLIBC_2.27 pkey_set F
GLIBC_2.27 strfromf128 F
GLIBC_2.27 strfromf64x F
GLIBC_2.27 strtof128 F
diff --git a/sysdeps/unix/sysv/linux/arm/libc.abilist b/sysdeps/unix/sysv/linux/arm/libc.abilist
index a5ce7964d0..27ccdabc0c 100644
--- a/sysdeps/unix/sysv/linux/arm/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/libc.abilist
@@ -109,6 +109,11 @@ GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
GLIBC_2.27 mlock2 F
+GLIBC_2.27 pkey_alloc F
+GLIBC_2.27 pkey_free F
+GLIBC_2.27 pkey_get F
+GLIBC_2.27 pkey_mprotect F
+GLIBC_2.27 pkey_set F
GLIBC_2.4 GLIBC_2.4 A
GLIBC_2.4 _Exit F
GLIBC_2.4 _IO_2_1_stderr_ D 0xa0
diff --git a/sysdeps/unix/sysv/linux/bits/mman-shared.h b/sysdeps/unix/sysv/linux/bits/mman-shared.h
index bee99c2384..9e532adb23 100644
--- a/sysdeps/unix/sysv/linux/bits/mman-shared.h
+++ b/sysdeps/unix/sysv/linux/bits/mman-shared.h
@@ -33,6 +33,12 @@
# define MLOCK_ONFAULT 1U
# endif
+/* Access rights for pkey_alloc. */
+# ifndef PKEY_DISABLE_ACCESS
+# define PKEY_DISABLE_ACCESS 0x1
+# define PKEY_DISABLE_WRITE 0x2
+# endif
+
__BEGIN_DECLS
/* Create a new memory file descriptor. NAME is a name for debugging.
@@ -43,6 +49,28 @@ int memfd_create (const char *__name, unsigned int __flags) __THROW;
memory. FLAGS is a combination of the MLOCK_* flags above. */
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
+ current thread is updated to match the access privilege for the new
+ key. */
+int pkey_alloc (unsigned int __flags, unsigned int __access_rights) __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;
+
+/* Return the access rights for the current thread for KEY, which must
+ have been allocated using pkey_alloc. */
+int pkey_get (int _key) __THROW;
+
+/* Free an allocated protection key, which must have been allocated
+ using pkey_alloc. */
+int pkey_free (int __key) __THROW;
+
+/* Apply memory protection flags for KEY to the specified address
+ range. */
+int pkey_mprotect (void *__addr, size_t __len, int __prot, int __pkey) __THROW;
+
__END_DECLS
#endif /* __USE_GNU */
diff --git a/sysdeps/unix/sysv/linux/bits/siginfo-consts.h b/sysdeps/unix/sysv/linux/bits/siginfo-consts.h
index 525840cea1..e86b933040 100644
--- a/sysdeps/unix/sysv/linux/bits/siginfo-consts.h
+++ b/sysdeps/unix/sysv/linux/bits/siginfo-consts.h
@@ -111,8 +111,12 @@ enum
{
SEGV_MAPERR = 1, /* Address not mapped to object. */
# define SEGV_MAPERR SEGV_MAPERR
- SEGV_ACCERR /* Invalid permissions for mapped object. */
+ SEGV_ACCERR, /* Invalid permissions for mapped object. */
# define SEGV_ACCERR SEGV_ACCERR
+ SEGV_BNDERR, /* Bounds checking failure. */
+# define SEGV_BNDERR SEGV_BNDERR
+ SEGV_PKUERR /* Protection key checking failure. */
+# define SEGV_PKUERR SEGV_PKUERR
};
/* `si_code' values for SIGBUS signal. */
diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
index 69ddf15361..d7e656b13b 100644
--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
+++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
@@ -1873,6 +1873,11 @@ GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
GLIBC_2.27 mlock2 F
+GLIBC_2.27 pkey_alloc F
+GLIBC_2.27 pkey_free F
+GLIBC_2.27 pkey_get F
+GLIBC_2.27 pkey_mprotect F
+GLIBC_2.27 pkey_set F
GLIBC_2.3 GLIBC_2.3 A
GLIBC_2.3 __ctype_b_loc F
GLIBC_2.3 __ctype_tolower_loc F
diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
index a140edd4a3..8e10641162 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -2038,6 +2038,11 @@ GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
GLIBC_2.27 mlock2 F
+GLIBC_2.27 pkey_alloc F
+GLIBC_2.27 pkey_free F
+GLIBC_2.27 pkey_get F
+GLIBC_2.27 pkey_mprotect F
+GLIBC_2.27 pkey_set F
GLIBC_2.27 strfromf64x F
GLIBC_2.27 strtof64x F
GLIBC_2.27 strtof64x_l F
diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
index 178c0a45ec..81ec4d6761 100644
--- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
@@ -1902,6 +1902,11 @@ GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
GLIBC_2.27 mlock2 F
+GLIBC_2.27 pkey_alloc F
+GLIBC_2.27 pkey_free F
+GLIBC_2.27 pkey_get F
+GLIBC_2.27 pkey_mprotect F
+GLIBC_2.27 pkey_set F
GLIBC_2.27 strfromf64x F
GLIBC_2.27 strtof64x F
GLIBC_2.27 strtof64x_l F
diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
index 01d10d907c..9655afe395 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
@@ -110,6 +110,11 @@ GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
GLIBC_2.27 mlock2 F
+GLIBC_2.27 pkey_alloc F
+GLIBC_2.27 pkey_free F
+GLIBC_2.27 pkey_get F
+GLIBC_2.27 pkey_mprotect F
+GLIBC_2.27 pkey_set F
GLIBC_2.4 GLIBC_2.4 A
GLIBC_2.4 _Exit F
GLIBC_2.4 _IO_2_1_stderr_ D 0x98
diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
index 3ad08c20bf..2d9cd557ad 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
@@ -1987,6 +1987,11 @@ GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
GLIBC_2.27 mlock2 F
+GLIBC_2.27 pkey_alloc F
+GLIBC_2.27 pkey_free F
+GLIBC_2.27 pkey_get F
+GLIBC_2.27 pkey_mprotect F
+GLIBC_2.27 pkey_set F
GLIBC_2.3 GLIBC_2.3 A
GLIBC_2.3 __ctype_b_loc F
GLIBC_2.3 __ctype_tolower_loc F
diff --git a/sysdeps/unix/sysv/linux/microblaze/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
index 6bd7be1929..256117b3a0 100644
--- a/sysdeps/unix/sysv/linux/microblaze/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
@@ -2108,3 +2108,8 @@ GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
GLIBC_2.27 mlock2 F
+GLIBC_2.27 pkey_alloc F
+GLIBC_2.27 pkey_free F
+GLIBC_2.27 pkey_get F
+GLIBC_2.27 pkey_mprotect F
+GLIBC_2.27 pkey_set F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
index 9b1e890eda..42e4770341 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
@@ -1962,6 +1962,11 @@ GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
GLIBC_2.27 mlock2 F
+GLIBC_2.27 pkey_alloc F
+GLIBC_2.27 pkey_free F
+GLIBC_2.27 pkey_get F
+GLIBC_2.27 pkey_mprotect F
+GLIBC_2.27 pkey_set F
GLIBC_2.3 GLIBC_2.3 A
GLIBC_2.3 __ctype_b_loc F
GLIBC_2.3 __ctype_tolower_loc F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
index 3eb5b66f8b..a28a21f842 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
@@ -1960,6 +1960,11 @@ GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
GLIBC_2.27 mlock2 F
+GLIBC_2.27 pkey_alloc F
+GLIBC_2.27 pkey_free F
+GLIBC_2.27 pkey_get F
+GLIBC_2.27 pkey_mprotect F
+GLIBC_2.27 pkey_set F
GLIBC_2.3 GLIBC_2.3 A
GLIBC_2.3 __ctype_b_loc F
GLIBC_2.3 __ctype_tolower_loc F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
index 543a725114..b725205f4f 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
@@ -1958,6 +1958,11 @@ GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
GLIBC_2.27 mlock2 F
+GLIBC_2.27 pkey_alloc F
+GLIBC_2.27 pkey_free F
+GLIBC_2.27 pkey_get F
+GLIBC_2.27 pkey_mprotect F
+GLIBC_2.27 pkey_set F
GLIBC_2.27 strfromf128 F
GLIBC_2.27 strfromf64x F
GLIBC_2.27 strtof128 F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
index a9198a3936..373801d4f3 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
@@ -1953,6 +1953,11 @@ GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
GLIBC_2.27 mlock2 F
+GLIBC_2.27 pkey_alloc F
+GLIBC_2.27 pkey_free F
+GLIBC_2.27 pkey_get F
+GLIBC_2.27 pkey_mprotect F
+GLIBC_2.27 pkey_set F
GLIBC_2.27 strfromf128 F
GLIBC_2.27 strfromf64x F
GLIBC_2.27 strtof128 F
diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
index afacf1ff2d..7ff042045f 100644
--- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
+++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
@@ -2149,3 +2149,8 @@ GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
GLIBC_2.27 mlock2 F
+GLIBC_2.27 pkey_alloc F
+GLIBC_2.27 pkey_free F
+GLIBC_2.27 pkey_get F
+GLIBC_2.27 pkey_mprotect F
+GLIBC_2.27 pkey_set F
diff --git a/sysdeps/unix/sysv/linux/pkey_get.c b/sysdeps/unix/sysv/linux/pkey_get.c
new file mode 100644
index 0000000000..fc3204c82f
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/pkey_get.c
@@ -0,0 +1,26 @@
+/* Obtaining the thread memory protection key, generic stub.
+ Copyright (C) 2017 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
+ <http://www.gnu.org/licenses/>. */
+
+#include <errno.h>
+
+int
+pkey_get (int key)
+{
+ __set_errno (ENOSYS);
+ return -1;
+}
diff --git a/sysdeps/unix/sysv/linux/pkey_mprotect.c b/sysdeps/unix/sysv/linux/pkey_mprotect.c
new file mode 100644
index 0000000000..a78fe293b2
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/pkey_mprotect.c
@@ -0,0 +1,37 @@
+/* mprotect with a memory protection key.
+ Copyright (C) 2017 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
+ <http://www.gnu.org/licenses/>. */
+
+#include <errno.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <sysdep.h>
+
+int
+pkey_mprotect (void *addr, size_t len, int prot, int pkey)
+{
+ if (pkey == -1)
+ /* If the key is -1, the system call is precisely equivalent to
+ mprotect. */
+ return __mprotect (addr, len, prot);
+#ifdef __NR_pkey_mprotect
+ return INLINE_SYSCALL_CALL (pkey_mprotect, addr, len, prot, pkey);
+#else
+ __set_errno (ENOSYS);
+ return -1;
+#endif
+}
diff --git a/sysdeps/unix/sysv/linux/pkey_set.c b/sysdeps/unix/sysv/linux/pkey_set.c
new file mode 100644
index 0000000000..f686c4373c
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/pkey_set.c
@@ -0,0 +1,26 @@
+/* Changing the thread memory protection key, generic stub.
+ Copyright (C) 2017 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
+ <http://www.gnu.org/licenses/>. */
+
+#include <errno.h>
+
+int
+pkey_set (int key, unsigned int access_rights)
+{
+ __set_errno (ENOSYS);
+ return -1;
+}
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
index 48af097b6a..a074a05005 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
@@ -1991,6 +1991,11 @@ GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
GLIBC_2.27 mlock2 F
+GLIBC_2.27 pkey_alloc F
+GLIBC_2.27 pkey_free F
+GLIBC_2.27 pkey_get F
+GLIBC_2.27 pkey_mprotect F
+GLIBC_2.27 pkey_set F
GLIBC_2.3 GLIBC_2.3 A
GLIBC_2.3 __ctype_b_loc F
GLIBC_2.3 __ctype_tolower_loc F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
index e30535dac9..5271f6dab3 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
@@ -1996,6 +1996,11 @@ GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
GLIBC_2.27 mlock2 F
+GLIBC_2.27 pkey_alloc F
+GLIBC_2.27 pkey_free F
+GLIBC_2.27 pkey_get F
+GLIBC_2.27 pkey_mprotect F
+GLIBC_2.27 pkey_set F
GLIBC_2.3 GLIBC_2.3 A
GLIBC_2.3 __ctype_b_loc F
GLIBC_2.3 __ctype_tolower_loc F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
index f522700890..96cc6cdb1d 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
@@ -2203,6 +2203,11 @@ GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
GLIBC_2.27 mlock2 F
+GLIBC_2.27 pkey_alloc F
+GLIBC_2.27 pkey_free F
+GLIBC_2.27 pkey_get F
+GLIBC_2.27 pkey_mprotect F
+GLIBC_2.27 pkey_set F
GLIBC_2.27 strfromf64x F
GLIBC_2.27 strtof64x F
GLIBC_2.27 strtof64x_l F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
index d3092afd25..3665895b76 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
@@ -110,6 +110,11 @@ GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
GLIBC_2.27 mlock2 F
+GLIBC_2.27 pkey_alloc F
+GLIBC_2.27 pkey_free F
+GLIBC_2.27 pkey_get F
+GLIBC_2.27 pkey_mprotect F
+GLIBC_2.27 pkey_set F
GLIBC_2.3 GLIBC_2.3 A
GLIBC_2.3 _Exit F
GLIBC_2.3 _IO_2_1_stderr_ D 0xe0
diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
index 752176108e..1aa0994c0a 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
@@ -1991,6 +1991,11 @@ GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
GLIBC_2.27 mlock2 F
+GLIBC_2.27 pkey_alloc F
+GLIBC_2.27 pkey_free F
+GLIBC_2.27 pkey_get F
+GLIBC_2.27 pkey_mprotect F
+GLIBC_2.27 pkey_set F
GLIBC_2.27 strfromf128 F
GLIBC_2.27 strfromf64x F
GLIBC_2.27 strtof128 F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
index b6d4c73635..538f7cca8c 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
@@ -1892,6 +1892,11 @@ GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
GLIBC_2.27 mlock2 F
+GLIBC_2.27 pkey_alloc F
+GLIBC_2.27 pkey_free F
+GLIBC_2.27 pkey_get F
+GLIBC_2.27 pkey_mprotect F
+GLIBC_2.27 pkey_set F
GLIBC_2.27 strfromf128 F
GLIBC_2.27 strfromf64x F
GLIBC_2.27 strtof128 F
diff --git a/sysdeps/unix/sysv/linux/sh/libc.abilist b/sysdeps/unix/sysv/linux/sh/libc.abilist
index 1ee21fe8e8..17d5c0c7eb 100644
--- a/sysdeps/unix/sysv/linux/sh/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/libc.abilist
@@ -1877,6 +1877,11 @@ GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
GLIBC_2.27 mlock2 F
+GLIBC_2.27 pkey_alloc F
+GLIBC_2.27 pkey_free F
+GLIBC_2.27 pkey_get F
+GLIBC_2.27 pkey_mprotect F
+GLIBC_2.27 pkey_set F
GLIBC_2.3 GLIBC_2.3 A
GLIBC_2.3 __ctype_b_loc F
GLIBC_2.3 __ctype_tolower_loc F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
index e652191c60..e635f380b7 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
@@ -1984,6 +1984,11 @@ GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
GLIBC_2.27 mlock2 F
+GLIBC_2.27 pkey_alloc F
+GLIBC_2.27 pkey_free F
+GLIBC_2.27 pkey_get F
+GLIBC_2.27 pkey_mprotect F
+GLIBC_2.27 pkey_set F
GLIBC_2.27 strfromf128 F
GLIBC_2.27 strfromf64x F
GLIBC_2.27 strtof128 F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
index 37cf8713a5..8cea2c0a8d 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
@@ -1921,6 +1921,11 @@ GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
GLIBC_2.27 mlock2 F
+GLIBC_2.27 pkey_alloc F
+GLIBC_2.27 pkey_free F
+GLIBC_2.27 pkey_get F
+GLIBC_2.27 pkey_mprotect F
+GLIBC_2.27 pkey_set F
GLIBC_2.27 strfromf128 F
GLIBC_2.27 strfromf64x F
GLIBC_2.27 strtof128 F
diff --git a/sysdeps/unix/sysv/linux/syscalls.list b/sysdeps/unix/sysv/linux/syscalls.list
index 40c4fbb9ea..e3dfd0c8db 100644
--- a/sysdeps/unix/sysv/linux/syscalls.list
+++ b/sysdeps/unix/sysv/linux/syscalls.list
@@ -110,3 +110,5 @@ setns EXTRA setns i:ii setns
process_vm_readv EXTRA process_vm_readv i:ipipii process_vm_readv
process_vm_writev EXTRA process_vm_writev i:ipipii process_vm_writev
memfd_create EXTRA memfd_create i:si memfd_create
+pkey_alloc EXTRA pkey_alloc i:ii pkey_alloc
+pkey_free EXTRA pkey_free i:i pkey_free
diff --git a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
index 57427eb3ee..d7b49335ab 100644
--- a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
@@ -2115,3 +2115,8 @@ GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
GLIBC_2.27 mlock2 F
+GLIBC_2.27 pkey_alloc F
+GLIBC_2.27 pkey_free F
+GLIBC_2.27 pkey_get F
+GLIBC_2.27 pkey_mprotect F
+GLIBC_2.27 pkey_set F
diff --git a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
index 321f65c600..e96a45818c 100644
--- a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
@@ -2115,3 +2115,8 @@ GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
GLIBC_2.27 mlock2 F
+GLIBC_2.27 pkey_alloc F
+GLIBC_2.27 pkey_free F
+GLIBC_2.27 pkey_get F
+GLIBC_2.27 pkey_mprotect F
+GLIBC_2.27 pkey_set F
diff --git a/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
index 57427eb3ee..d7b49335ab 100644
--- a/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
+++ b/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
@@ -2115,3 +2115,8 @@ GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
GLIBC_2.27 mlock2 F
+GLIBC_2.27 pkey_alloc F
+GLIBC_2.27 pkey_free F
+GLIBC_2.27 pkey_get F
+GLIBC_2.27 pkey_mprotect F
+GLIBC_2.27 pkey_set F
diff --git a/sysdeps/unix/sysv/linux/tst-pkey.c b/sysdeps/unix/sysv/linux/tst-pkey.c
new file mode 100644
index 0000000000..e7205dba1f
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/tst-pkey.c
@@ -0,0 +1,399 @@
+/* Tests for memory protection keys.
+ Copyright (C) 2017 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
+ <http://www.gnu.org/licenses/>. */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <setjmp.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/xthread.h>
+#include <support/xunistd.h>
+#include <sys/mman.h>
+
+/* Used to force threads to wait until the main thread has set up the
+ keys as intended. */
+static pthread_barrier_t barrier;
+
+/* The keys used for testing. These have been allocated with access
+ rights set based on their array index. */
+enum { key_count = 4 };
+static int keys[key_count];
+static volatile int *pages[key_count];
+
+/* Used to report results from the signal handler. */
+static volatile void *sigsegv_addr;
+static volatile int sigsegv_code;
+static volatile int sigsegv_pkey;
+static sigjmp_buf sigsegv_jmp;
+
+/* Used to handle expected read or write faults. */
+static void
+sigsegv_handler (int signum, siginfo_t *info, void *context)
+{
+ sigsegv_addr = info->si_addr;
+ sigsegv_code = info->si_code;
+ sigsegv_pkey = info->si_pkey;
+ siglongjmp (sigsegv_jmp, 2);
+}
+
+static const struct sigaction sigsegv_sigaction =
+ {
+ .sa_flags = SA_RESETHAND | SA_SIGINFO,
+ .sa_sigaction = &sigsegv_handler,
+ };
+
+/* Check if PAGE is readable (if !WRITE) or writable (if WRITE). */
+static bool
+check_page_access (int page, bool write)
+{
+ /* This is needed to work around bug 22396: On x86-64, siglongjmp
+ does not restore the protection key access rights for the current
+ thread. We restore only the access rights for the keys under
+ test. (This is not a general solution to this problem, but it
+ allows testing to proceed after a fault.) */
+ unsigned saved_rights[key_count];
+ for (int i = 0; i < key_count; ++i)
+ saved_rights[i] = pkey_get (keys[i]);
+
+ volatile int *addr = pages[page];
+ if (test_verbose > 0)
+ {
+ printf ("info: checking access at %p (page %d) for %s\n",
+ addr, page, write ? "writing" : "reading");
+ }
+ int result = sigsetjmp (sigsegv_jmp, 1);
+ if (result == 0)
+ {
+ xsigaction (SIGSEGV, &sigsegv_sigaction, NULL);
+ if (write)
+ *addr = 3;
+ else
+ (void) *addr;
+ xsignal (SIGSEGV, SIG_DFL);
+ if (test_verbose > 0)
+ puts (" --> access allowed");
+ return true;
+ }
+ else
+ {
+ xsignal (SIGSEGV, SIG_DFL);
+ if (test_verbose > 0)
+ puts (" --> access denied");
+ TEST_COMPARE (result, 2);
+ TEST_COMPARE ((uintptr_t) sigsegv_addr, (uintptr_t) addr);
+ TEST_COMPARE (sigsegv_code, SEGV_PKUERR);
+ TEST_COMPARE (sigsegv_pkey, keys[page]);
+ for (int i = 0; i < key_count; ++i)
+ TEST_COMPARE (pkey_set (keys[i], saved_rights[i]), 0);
+ return false;
+ }
+}
+
+static volatile sig_atomic_t sigusr1_handler_ran;
+
+/* Used to check that access is revoked in signal handlers. */
+static void
+sigusr1_handler (int signum)
+{
+ TEST_COMPARE (signum, SIGUSR1);
+ for (int i = 0; i < key_count; ++i)
+ TEST_COMPARE (pkey_get (keys[i]), PKEY_DISABLE_ACCESS);
+ sigusr1_handler_ran = 1;
+}
+
+/* Used to report results from other threads. */
+struct thread_result
+{
+ int access_rights[key_count];
+ pthread_t next_thread;
+};
+
+/* Return the thread's access rights for the keys under test. */
+static void *
+get_thread_func (void *closure)
+{
+ struct thread_result *result = xmalloc (sizeof (*result));
+ for (int i = 0; i < key_count; ++i)
+ result->access_rights[i] = pkey_get (keys[i]);
+ memset (&result->next_thread, 0, sizeof (result->next_thread));
+ return result;
+}
+
+/* Wait for initialization and then check that the current thread does
+ not have access through the keys under test. */
+static void *
+delayed_thread_func (void *closure)
+{
+ bool check_access = *(bool *) closure;
+ pthread_barrier_wait (&barrier);
+ struct thread_result *result = get_thread_func (NULL);
+
+ if (check_access)
+ {
+ /* Also check directly. This code should not run with other
+ threads in parallel because of the SIGSEGV handler which is
+ installed by check_page_access. */
+ for (int i = 0; i < key_count; ++i)
+ {
+ TEST_VERIFY (!check_page_access (i, false));
+ TEST_VERIFY (!check_page_access (i, true));
+ }
+ }
+
+ result->next_thread = xpthread_create (NULL, get_thread_func, NULL);
+ return result;
+}
+
+static int
+do_test (void)
+{
+ long pagesize = xsysconf (_SC_PAGESIZE);
+
+ /* pkey_mprotect with key -1 should work even when there is no
+ protection key support. */
+ {
+ int *page = xmmap (NULL, pagesize, PROT_NONE,
+ MAP_ANONYMOUS | MAP_PRIVATE, -1);
+ TEST_COMPARE (pkey_mprotect (page, pagesize, PROT_READ | PROT_WRITE, -1),
+ 0);
+ volatile int *vpage = page;
+ *vpage = 5;
+ TEST_COMPARE (*vpage, 5);
+ xmunmap (page, pagesize);
+ }
+
+ 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);
+ if (keys[0] < 0)
+ {
+ if (errno == ENOSYS)
+ FAIL_UNSUPPORTED
+ ("kernel does not support memory protection keys");
+ if (errno == EINVAL)
+ FAIL_UNSUPPORTED
+ ("CPU does not support memory protection keys: %m");
+ FAIL_EXIT1 ("pkey_alloc: %m");
+ }
+ TEST_COMPARE (pkey_get (keys[0]), 0);
+ for (int i = 1; i < key_count; ++i)
+ {
+ 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);
+ }
+ /* 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);
+
+ /* Allocate a test page for each key. */
+ for (int i = 0; i < key_count; ++i)
+ {
+ pages[i] = xmmap (NULL, pagesize, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE, -1);
+ TEST_COMPARE (pkey_mprotect ((void *) pages[i], pagesize,
+ PROT_READ | PROT_WRITE, keys[i]), 0);
+ }
+
+ /* Check that the initial thread does not have access to the new
+ keys. */
+ {
+ 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);
+ 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);
+ free (result);
+ free (result2);
+ }
+
+ /* Check that the current thread access rights are inherited by new
+ threads. */
+ {
+ 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);
+ free (result);
+ }
+
+ for (int i = 0; i < key_count; ++i)
+ TEST_COMPARE (pkey_get (keys[i]), i);
+
+ /* Check that in a signal handler, there is no access. */
+ xsignal (SIGUSR1, &sigusr1_handler);
+ xraise (SIGUSR1);
+ xsignal (SIGUSR1, SIG_DFL);
+ TEST_COMPARE (sigusr1_handler_ran, 1);
+
+ /* The first key results in a writable page. */
+ TEST_VERIFY (check_page_access (0, false));
+ TEST_VERIFY (check_page_access (0, true));
+
+ /* The other keys do not. */
+ for (int i = 1; i < key_count; ++i)
+ {
+ if (test_verbose)
+ 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);
+ if (i & PKEY_DISABLE_ACCESS)
+ {
+ TEST_VERIFY (!check_page_access (i, false));
+ TEST_VERIFY (!check_page_access (i, true));
+ }
+ else
+ {
+ TEST_VERIFY (i & PKEY_DISABLE_WRITE);
+ TEST_VERIFY (check_page_access (i, false));
+ TEST_VERIFY (!check_page_access (i, true));
+ }
+ }
+
+ /* But if we set the current thread's access rights, we gain
+ access. */
+ for (int do_write = 0; do_write < 2; ++do_write)
+ for (int allowed_key = 0; allowed_key < key_count; ++allowed_key)
+ {
+ for (int i = 0; i < key_count; ++i)
+ if (i == allowed_key)
+ {
+ if (do_write)
+ TEST_COMPARE (pkey_set (keys[i], 0), 0);
+ else
+ TEST_COMPARE (pkey_set (keys[i], PKEY_DISABLE_WRITE), 0);
+ }
+ else
+ TEST_COMPARE (pkey_set (keys[i], PKEY_DISABLE_ACCESS), 0);
+
+ if (test_verbose)
+ printf ("info: key %d is allowed access for %s\n",
+ allowed_key, do_write ? "writing" : "reading");
+ for (int i = 0; i < key_count; ++i)
+ if (i == allowed_key)
+ {
+ TEST_VERIFY (check_page_access (i, false));
+ TEST_VERIFY (check_page_access (i, true) == do_write);
+ }
+ else
+ {
+ TEST_VERIFY (!check_page_access (i, false));
+ TEST_VERIFY (!check_page_access (i, true));
+ }
+ }
+
+ /* Restore access to all keys, and launch a thread which should
+ inherit that access. */
+ for (int i = 0; i < key_count; ++i)
+ {
+ TEST_COMPARE (pkey_set (keys[i], 0), 0);
+ TEST_VERIFY (check_page_access (i, false));
+ TEST_VERIFY (check_page_access (i, true));
+ }
+ delayed_thread_check_access = false;
+ delayed_thread = xpthread_create
+ (NULL, delayed_thread_func, &delayed_thread_check_access);
+
+ TEST_COMPARE (pkey_free (keys[0]), 0);
+ /* Second pkey_free will fail because the key has already been
+ freed. */
+ TEST_COMPARE (pkey_free (keys[0]),-1);
+ TEST_COMPARE (errno, EINVAL);
+ for (int i = 1; i < key_count; ++i)
+ TEST_COMPARE (pkey_free (keys[i]), 0);
+
+ /* Check what happens to running threads which have access to
+ previously allocated protection keys. The implemented behavior
+ is somewhat dubious: Ideally, pkey_free should revoke access to
+ that key and pkey_alloc of the same (numeric) key should not
+ implicitly confer access to already-running threads, but this is
+ not what happens in practice. */
+ {
+ /* The limit is in place to avoid running indefinitely in case
+ there many keys available. */
+ int *keys_array = xcalloc (100000, sizeof (*keys_array));
+ int keys_allocated = 0;
+ while (keys_allocated < 100000)
+ {
+ int new_key = pkey_alloc (0, PKEY_DISABLE_WRITE);
+ if (new_key < 0)
+ {
+ /* No key reuse observed before running out of keys. */
+ TEST_COMPARE (errno, ENOSPC);
+ break;
+ }
+ for (int i = 0; i < key_count; ++i)
+ if (new_key == keys[i])
+ {
+ /* We allocated the key with disabled write access.
+ This should affect the protection state of the
+ existing page. */
+ TEST_VERIFY (check_page_access (i, false));
+ TEST_VERIFY (!check_page_access (i, true));
+
+ xpthread_barrier_wait (&barrier);
+ struct thread_result *result = xpthread_join (delayed_thread);
+ /* The thread which was launched before should still have
+ access to the key. */
+ TEST_COMPARE (result->access_rights[i], 0);
+ struct thread_result *result2
+ = xpthread_join (result->next_thread);
+ /* Same for a thread which is launched afterwards from
+ the old thread. */
+ TEST_COMPARE (result2->access_rights[i], 0);
+ free (result);
+ free (result2);
+ keys_array[keys_allocated++] = new_key;
+ goto after_key_search;
+ }
+ /* Save key for later deallocation. */
+ keys_array[keys_allocated++] = new_key;
+ }
+ after_key_search:
+ /* Deallocate the keys allocated for testing purposes. */
+ for (int j = 0; j < keys_allocated; ++j)
+ TEST_COMPARE (pkey_free (keys_array[j]), 0);
+ free (keys_array);
+ }
+
+ for (int i = 0; i < key_count; ++i)
+ xmunmap ((void *) pages[i], pagesize);
+
+ xpthread_barrier_destroy (&barrier);
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/sysdeps/unix/sysv/linux/x86/arch-pkey.h b/sysdeps/unix/sysv/linux/x86/arch-pkey.h
new file mode 100644
index 0000000000..8e9bfdae96
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/x86/arch-pkey.h
@@ -0,0 +1,40 @@
+/* Helper functions for manipulating memory protection keys.
+ Copyright (C) 2017 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
+ <http://www.gnu.org/licenses/>. */
+
+#ifndef _ARCH_PKEY_H
+#define _ARCH_PKEY_H
+
+/* Return the value of the PKRU register. */
+static inline unsigned int
+pkey_read (void)
+{
+ unsigned int result;
+ __asm__ volatile (".byte 0x0f, 0x01, 0xee"
+ : "=a" (result) : "c" (0) : "rdx");
+ return result;
+}
+
+/* Overwrite the PKRU register with VALUE. */
+static inline void
+pkey_write (unsigned int value)
+{
+ __asm__ volatile (".byte 0x0f, 0x01, 0xef"
+ : : "a" (value), "c" (0), "d" (0));
+}
+
+#endif /* _ARCH_PKEY_H */
diff --git a/sysdeps/unix/sysv/linux/x86/pkey_get.c b/sysdeps/unix/sysv/linux/x86/pkey_get.c
new file mode 100644
index 0000000000..3a9bfbe676
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/x86/pkey_get.c
@@ -0,0 +1,33 @@
+/* Reading the per-thread memory protection key, x86_64 version.
+ Copyright (C) 2017 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
+ <http://www.gnu.org/licenses/>. */
+
+#include <arch-pkey.h>
+#include <errno.h>
+
+int
+pkey_get (int key)
+{
+ if (key < 0 || key > 15)
+ {
+ __set_errno (EINVAL);
+ return -1;
+ }
+ unsigned int pkru = pkey_read ();
+ return (pkru >> (2 * key)) & 3;
+ return 0;
+}
diff --git a/sysdeps/unix/sysv/linux/x86/pkey_set.c b/sysdeps/unix/sysv/linux/x86/pkey_set.c
new file mode 100644
index 0000000000..91dffd22c3
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/x86/pkey_set.c
@@ -0,0 +1,35 @@
+/* Changing the per-thread memory protection key, x86_64 version.
+ Copyright (C) 2017 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
+ <http://www.gnu.org/licenses/>. */
+
+#include <arch-pkey.h>
+#include <errno.h>
+
+int
+pkey_set (int key, unsigned int rights)
+{
+ if (key < 0 || key > 15 || rights > 3)
+ {
+ __set_errno (EINVAL);
+ return -1;
+ }
+ unsigned int mask = 3 << (2 * key);
+ unsigned int pkru = pkey_read ();
+ pkru = (pkru & ~mask) | (rights << (2 * key));
+ pkey_write (pkru);
+ return 0;
+}
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
index f26c8b99d5..7317d17e7b 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -1879,6 +1879,11 @@ GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
GLIBC_2.27 mlock2 F
+GLIBC_2.27 pkey_alloc F
+GLIBC_2.27 pkey_free F
+GLIBC_2.27 pkey_get F
+GLIBC_2.27 pkey_mprotect F
+GLIBC_2.27 pkey_set F
GLIBC_2.27 strfromf64x F
GLIBC_2.27 strtof64x F
GLIBC_2.27 strtof64x_l F
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
index 2a6057154b..0a9334f822 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2122,6 +2122,11 @@ GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
GLIBC_2.27 mlock2 F
+GLIBC_2.27 pkey_alloc F
+GLIBC_2.27 pkey_free F
+GLIBC_2.27 pkey_get F
+GLIBC_2.27 pkey_mprotect F
+GLIBC_2.27 pkey_set F
GLIBC_2.27 strfromf64x F
GLIBC_2.27 strtof64x F
GLIBC_2.27 strtof64x_l F