summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog34
-rw-r--r--NEWS4
-rw-r--r--manual/memory.texi232
-rw-r--r--support/Makefile6
-rw-r--r--support/xraise.c27
-rw-r--r--support/xsigaction.c27
-rw-r--r--support/xsignal.c29
-rw-r--r--support/xsignal.h8
-rw-r--r--support/xsysconf.c36
-rw-r--r--support/xunistd.h1
-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
50 files changed, 1179 insertions, 4 deletions
diff --git a/ChangeLog b/ChangeLog
index 50ae82ba1d..333012ba81 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,39 @@
2017-12-05 Florian Weimer <fweimer@redhat.com>
+ Linux: Implement interfaces for memory protection keys
+ * support/Makefile (libsupport-routines): Add xraise, xsigaction,
+ xsignal, xsysconf.
+ * support/xsignal.h (xraise, xsignal, xsigaction): Declare.
+ * support/xunistd.h (xsysconf): Declare.
+ * support/xraise.c: New file.
+ * support/xsigaction.c: Likewise.
+ * support/xsignal.c: Likewise.
+ * support/xsysconf.c: Likewise.
+ * sysdeps/unix/sysv/linux/Makefile [misc] (routines): Add
+ pkey_set, pkey_get, pkey_mprotect.
+ [misc] (tests): Add tst-pkey.
+ (tst-pkey): Link with -lpthread.
+ * sysdeps/unix/sysv/linux/Versions (GLIBC_2.27): Add pkey_alloc,
+ pkey_free, pkey_set, pkey_get, pkey_mprotect.
+ * sysdeps/unix/sysv/linux/bits/mman-linux.h (PKEY_DISABLE_ACCESS)
+ (PKEY_DISABLE_WRITE): Define.
+ (pkey_alloc, pkey_free, pkey_set, pkey_get, pkey_mprotect):
+ Declare.
+ * sysdeps/unix/sysv/linux/bits/siginfo-consts.h (SEGV_BNDERR)
+ (SEGV_PKUERR): Add.
+ * sysdeps/unix/sysv/linux/pkey_get.c: New file.
+ * sysdeps/unix/sysv/linux/pkey_set.c: Likewise.
+ * sysdeps/unix/sysv/linux/pkey_mprotect.c: Likewise.
+ * sysdeps/unix/sysv/linux/syscalls.list (pkey_alloc, pkey_free):
+ Add.
+ * sysdeps/unix/sysv/linux/tst-pkey.c: New file.
+ * sysdeps/unix/sysv/linux/x86/arch-pkey.h: Likewise.
+ * sysdeps/unix/sysv/linux/x86/pkey_get.c: Likewise.
+ * sysdeps/unix/sysv/linux/x86/pkey_set.c: Likewise.
+ * sysdeps/unix/sysv/linux/**.abilist: Update.
+
+2017-12-05 Florian Weimer <fweimer@redhat.com>
+
* support/tst-test_compare.c (subprocess): Use long long instead
of long argument for consistent type width across 32-bit and
64-bit architectures.
diff --git a/NEWS b/NEWS
index 10f695aab1..6b1a2f92f7 100644
--- a/NEWS
+++ b/NEWS
@@ -43,6 +43,10 @@ Major new features:
* glibc now implements the memfd_create and mlock2 functions on Linux.
+* Support for memory protection keys was added. The <sys/mman.h> header now
+ declares the functions pkey_alloc, pkey_free, pkey_mprotect, pkey_set,
+ pkey_get.
+
Deprecated and removed features, and other changes affecting compatibility:
* On GNU/Linux, the obsolete Linux constant PTRACE_SEIZE_DEVEL is no longer
diff --git a/manual/memory.texi b/manual/memory.texi
index 1b431bf5da..b95f6aa1b9 100644
--- a/manual/memory.texi
+++ b/manual/memory.texi
@@ -3171,6 +3171,238 @@ process memory, no matter how it was allocated. However, portable use
of the function requires that it is only used with memory regions
returned by @code{mmap} or @code{mmap64}.
+@subsection Memory Protection Keys
+
+@cindex memory protection key
+@cindex protection key
+@cindex MPK
+On some systems, further restrictions can be added to specific pages
+using @dfn{memory protection keys}. These restrictions work as follows:
+
+@itemize @bullet
+@item
+All memory pages are associated with a protection key. The default
+protection key does not cause any additional protections to be applied
+during memory accesses. New keys can be allocated with the
+@code{pkey_alloc} function, and applied to pages using
+@code{pkey_mprotect}.
+
+@item
+Each thread has a set of separate access right restriction for each
+protection key. These access rights can be manipulated using the
+@code{pkey_set} and @code{pkey_get} functions.
+
+@item
+During a memory access, the system obtains the protection key for the
+accessed page and uses that to determine the applicable access rights,
+as configured for the current thread. If the access is restricted, a
+segmentation fault is the result ((@pxref{Program Error Signals}).
+These checks happen in addition to the @code{PROT_}* protection flags
+set by @code{mprotect} or @code{pkey_mprotect}.
+@end itemize
+
+New threads and subprocesses inherit the access rights of the current
+thread. If a protection key is allocated subsequently, existing threads
+(except the current) will use an unspecified system default for the
+access rights associated with newly allocated keys.
+
+Upon entering a signal handler, the system resets the access rights of
+the current thread so that pages with the default key can be accessed,
+but the access rights for other protection keys are unspecified.
+
+Applications are expected to allocate a key once using
+@code{pkey_alloc}, and apply the key to memory regions which need
+special protection with @code{pkey_mprotect}:
+
+@smallexample
+ int key = pkey_alloc (0, PKEY_DISABLE_ACCESS);
+ if (key < 0)
+ /* Perform error checking, including fallback for lack of support. */
+ ...;
+
+ /* Apply the key to a special memory region used to store critical
+ data. */
+ if (pkey_mprotect (region, region_length,
+ PROT_READ | PROT_WRITE, key) < 0)
+ ...; /* Perform error checking (generally fatal). */
+@end smallexample
+
+If the key allocation fails due to lack of support for memory protection
+keys, the @code{pkey_mprotect} call can usually be skipped. In this
+case, the region will not be protected by default. It is also possible
+to call @code{pkey_mprotect} with a key value of @math{-1}, in which
+case it will behave in the same way as @code{mprotect}.
+
+After key allocation assignment to memory pages, @code{pkey_set} can be
+used to temporarily acquire access to the memory region and relinquish
+it again:
+
+@smallexample
+ if (key >= 0 && pkey_set (key, 0) < 0)
+ ...; /* Perform error checking (generally fatal). */
+ /* At this point, the current thread has read-write access to the
+ memory region. */
+ ...
+ /* Revoke access again. */
+ if (key >= 0 && pkey_set (key, PKEY_DISABLE_ACCESS) < 0)
+ ...; /* Perform error checking (generally fatal). */
+@end smallexample
+
+In this example, a negative key value indicates that no key had been
+allocated, which means that the system lacks support for memory
+protection keys and it is not necessary to change the the access rights
+of the current thread (because it always has access).
+
+Compared to using @code{mprotect} to change the page protection flags,
+this approach has two advantages: It is thread-safe in the sense that
+the access rights are only changed for the current thread, so another
+thread which changes its own access rights concurrently to gain access
+to the mapping will not suddenly see its access rights revoked. And
+@code{pkey_set} typically does not involve a call into the kernel and a
+context switch, so it is more efficient.
+
+@deftypefun int pkey_alloc (unsigned int @var{flags}, unsigned int @var{restrictions})
+@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
+which are applied to the current thread (as if with @code{pkey_set}
+below). Access rights of other threads are not changed.
+
+The function returns the new protection key, a non-negative number, or
+@math{-1} on error.
+
+The following @code{errno} error conditions are defined for this
+function:
+
+@table @code
+@item ENOSYS
+The system does not implement memory protection keys.
+
+@item EINVAL
+The @var{flags} argument is not zero.
+
+The @var{restrictions} argument is invalid.
+
+The system does not implement memory protection keys or runs in a mode
+in which memory protection keys are disabled.
+
+@item ENOSPC
+All available protection keys already have been allocated.
+@end table
+@end deftypefun
+
+@deftypefun int pkey_free (int @var{key})
+@standards{Linux, sys/mman.h}
+@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
+Deallocate the protection key, so that it can be reused by
+@code{pkey_alloc}.
+
+Calling this function does not change the access rights of the freed
+protection key. The calling thread and other threads may retain access
+to it, even if it is subsequently allocated again. For this reason, it
+is not recommended to call the @code{pkey_free} function.
+
+@table @code
+@item ENOSYS
+The system does not implement memory protection keys.
+
+@item EINVAL
+The @var{key} argument is not a valid protection key.
+@end table
+@end deftypefun
+
+@deftypefun int pkey_mprotect (void *@var{address}, size_t @var{length}, int @var{protection}, int @var{key})
+@standards{Linux, sys/mman.h}
+@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
+Similar to @code{mprotect}, but also set the memory protection key for
+the memory region to @code{key}.
+
+Some systems use memory protection keys to emulate certain combinations
+of @var{protection} flags. Under such circumstances, specifying an
+explicit protection key may behave as if additional flags have been
+specified in @var{protection}, even though this does not happen with the
+default protection key. For example, some systems can support
+@code{PROT_EXEC}-only mappings only with a default protection key, and
+memory with a key which was allocated using @code{pkey_alloc} will still
+be readable if @code{PROT_EXEC} is specified without @code{PROT_READ}.
+
+If @var{key} is @math{-1}, the default protection key is applied to the
+mapping, just as if @code{mprotect} had been called.
+
+The @code{pkey_mprotect} function returns @math{0} on success and
+@math{-1} on failure. The same @code{errno} error conditions as for
+@code{mprotect} are defined for this function, with the following
+addition:
+
+@table @code
+@item EINVAL
+The @var{key} argument is not @math{-1} or a valid memory protection
+key allocated using @code{pkey_alloc}.
+
+@item ENOSYS
+The system does not implement memory protection keys, and @var{key} is
+not @math{-1}.
+@end table
+@end deftypefun
+
+@deftypefun int pkey_set (int @var{key}, unsigned int @var{rights})
+@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:
+
+@vtable @code
+@item PKEY_DISABLE_WRITE
+@standards{Linux, sys/mman.h}
+Subsequent attempts to write to memory with the specified protection
+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.
+@end vtable
+
+Operations not specified as flags are not restricted. In particular,
+this means that the memory region will remain executable if it was
+mapped with the @code{PROT_EXEC} protection flag and
+@code{PKEY_DISABLE_ACCESS} has been specified.
+
+Calling the @code{pkey_set} 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
+protection keys is undefined.
+
+The @code{pkey_set} function returns @math{0} on success and @math{-1}
+on failure.
+
+The following @code{errno} error conditions are defined for this
+function:
+
+@table @code
+@item EINVAL
+The system does not support the access rights restrictions expressed in
+the @var{rights} 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
+the @code{PKEY_DISABLE_}* flags; see the @code{pkey_set} function.
+
+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
+protection keys is undefined.
+@end deftypefun
+
@node Locking Pages
@section Locking Pages
@cindex locking pages
diff --git a/support/Makefile b/support/Makefile
index bb81825fc2..bfde79333e 100644
--- a/support/Makefile
+++ b/support/Makefile
@@ -87,8 +87,8 @@ libsupport-routines = \
xpthread_attr_destroy \
xpthread_attr_init \
xpthread_attr_setdetachstate \
- xpthread_attr_setstacksize \
xpthread_attr_setguardsize \
+ xpthread_attr_setstacksize \
xpthread_barrier_destroy \
xpthread_barrier_init \
xpthread_barrier_wait \
@@ -119,14 +119,18 @@ libsupport-routines = \
xpthread_sigmask \
xpthread_spin_lock \
xpthread_spin_unlock \
+ xraise \
xreadlink \
xrealloc \
xrecvfrom \
xsendto \
xsetsockopt \
+ xsigaction \
+ xsignal \
xsocket \
xstrdup \
xstrndup \
+ xsysconf \
xunlink \
xwaitpid \
xwrite \
diff --git a/support/xraise.c b/support/xraise.c
new file mode 100644
index 0000000000..9126c6c3ea
--- /dev/null
+++ b/support/xraise.c
@@ -0,0 +1,27 @@
+/* Error-checking wrapper for raise.
+ 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 <support/check.h>
+#include <support/xsignal.h>
+
+void
+xraise (int sig)
+{
+ if (raise (sig) != 0)
+ FAIL_EXIT1 ("raise (%d): %m" , sig);
+}
diff --git a/support/xsigaction.c b/support/xsigaction.c
new file mode 100644
index 0000000000..b74c69afae
--- /dev/null
+++ b/support/xsigaction.c
@@ -0,0 +1,27 @@
+/* Error-checking wrapper for sigaction.
+ 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 <support/check.h>
+#include <support/xsignal.h>
+
+void
+xsigaction (int sig, const struct sigaction *newact, struct sigaction *oldact)
+{
+ if (sigaction (sig, newact, oldact))
+ FAIL_EXIT1 ("sigaction (%d): %m" , sig);
+}
diff --git a/support/xsignal.c b/support/xsignal.c
new file mode 100644
index 0000000000..22a1dd74a7
--- /dev/null
+++ b/support/xsignal.c
@@ -0,0 +1,29 @@
+/* Error-checking wrapper for signal.
+ 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 <support/check.h>
+#include <support/xsignal.h>
+
+sighandler_t
+xsignal (int sig, sighandler_t handler)
+{
+ sighandler_t result = signal (sig, handler);
+ if (result == SIG_ERR)
+ FAIL_EXIT1 ("signal (%d, %p): %m", sig, handler);
+ return result;
+}
diff --git a/support/xsignal.h b/support/xsignal.h
index 3dc0d9d5ce..3087ed0082 100644
--- a/support/xsignal.h
+++ b/support/xsignal.h
@@ -24,6 +24,14 @@
__BEGIN_DECLS
+/* The following functions call the corresponding libc functions and
+ terminate the process on error. */
+
+void xraise (int sig);
+sighandler_t xsignal (int sig, sighandler_t handler);
+void xsigaction (int sig, const struct sigaction *newact,
+ struct sigaction *oldact);
+
/* The following functions call the corresponding libpthread functions
and terminate the process on error. */
diff --git a/support/xsysconf.c b/support/xsysconf.c
new file mode 100644
index 0000000000..15ab1e26c4
--- /dev/null
+++ b/support/xsysconf.c
@@ -0,0 +1,36 @@
+/* Error-checking wrapper for sysconf.
+ 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 <support/check.h>
+#include <support/xunistd.h>
+
+long
+xsysconf (int name)
+{
+ /* Detect errors by a changed errno value, in case -1 is a valid
+ value. Make sure that the caller does not see the zero value for
+ errno. */
+ int old_errno = errno;
+ errno = 0;
+ long result = sysconf (name);
+ if (errno != 0)
+ FAIL_EXIT1 ("sysconf (%d): %m", name);
+ errno = old_errno;
+ return result;
+}
diff --git a/support/xunistd.h b/support/xunistd.h
index 05c2626a7b..00376f7aae 100644
--- a/support/xunistd.h
+++ b/support/xunistd.h
@@ -39,6 +39,7 @@ void xstat (const char *path, struct stat64 *);
void xmkdir (const char *path, mode_t);
void xchroot (const char *path);
void xunlink (const char *path);
+long xsysconf (int name);
/* Read the link at PATH. The caller should free the returned string
with free. */
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