diff mbox series

Linux: Implement interfaces for memory protection keys

Message ID 20171104174948.A79F6400EA2DF@oldenburg.str.redhat.com
State New
Headers show
Series Linux: Implement interfaces for memory protection keys | expand

Commit Message

Florian Weimer Nov. 4, 2017, 5:49 p.m. UTC
This adds system call wrappers for pkey_alloc, pkey_free, pkey_mprotect,
and x86-64 implementations of pkey_get and pkey_set, which abstract over
the PKRU CPU register and hide the actual number of memory protection
keys supported by the CPU.

The system call wrapers use unsigned int instead of unsigned long for
parameters, so that no special treatment for x32 is needed.  The flags
argument is currently unused, and the access rights bit mask is limited
to two bits by the current PKRU register layout anyway.

2017-11-04  Florian Weimer  <fweimer@redhat.com>

	Linux: Implement interfaces for memory protection keys
	* support/Makefile (libsupport-routines): Add
	support_test_compare_failure, xraise, xsigaction, xsignal,
	xsysconf.
	* support/check.h (TEST_COMPARE): New macro.
	(support_test_compare_failure): Declare.
	* support/xsignal.h (xraise, xsignal, xsigaction): Declare.
	* support/xunistd.h (xsysconf): Declare.
	* support/support_test_compare_failure.c: New file.
	* support/xraise.c: Likewise.
	* support/xsigaction.c: Likewise.
	* support/xsignal.c: Likewise.
	* support/xsysconf.c: Likewise.
	* sysdeps/unix/sysv/linux/Makefile [misc] (routines): Add
	pkey_set, pkey_get.
	[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/syscalls.list (pkey_alloc, pkey_free)
	(pkey_mprotect): Add.
	* sysdeps/unix/sysv/linux/tst-pkey.c: New file.
	* sysdeps/unix/sysv/linux/x86_64/arch-pkey.h: Likewise.
	* sysdeps/unix/sysv/linux/x86_64/pkey_get.c: Likewise.
	* sysdeps/unix/sysv/linux/x86_64/pkey_set.c: Likewise.
	* sysdeps/unix/sysv/linux/**.abilist: Update.

Comments

Florian Weimer Nov. 5, 2017, 7:33 a.m. UTC | #1
On 11/04/2017 06:49 PM, Florian Weimer wrote:
> This adds system call wrappers for pkey_alloc, pkey_free, pkey_mprotect,
> and x86-64 implementations of pkey_get and pkey_set, which abstract over
> the PKRU CPU register and hide the actual number of memory protection
> keys supported by the CPU.
> 
> The system call wrapers use unsigned int instead of unsigned long for
> parameters, so that no special treatment for x32 is needed.  The flags
> argument is currently unused, and the access rights bit mask is limited
> to two bits by the current PKRU register layout anyway.

I again forgot to add a NEWS entry.

Note that the manual pages for pkey_alloc etc. are currently quite 
confusing (and we'd have to add one for pkey_set/pkey_get).

Thanks,
Florian
Joseph Myers Nov. 6, 2017, 1:29 p.m. UTC | #2
On Sun, 5 Nov 2017, Florian Weimer wrote:

> On 11/04/2017 06:49 PM, Florian Weimer wrote:
> > This adds system call wrappers for pkey_alloc, pkey_free, pkey_mprotect,
> > and x86-64 implementations of pkey_get and pkey_set, which abstract over
> > the PKRU CPU register and hide the actual number of memory protection
> > keys supported by the CPU.
> > 
> > The system call wrapers use unsigned int instead of unsigned long for
> > parameters, so that no special treatment for x32 is needed.  The flags
> > argument is currently unused, and the access rights bit mask is limited
> > to two bits by the current PKRU register layout anyway.
> 
> I again forgot to add a NEWS entry.

Documentation in the glibc manual is also needed.
Florian Weimer Nov. 6, 2017, 1:40 p.m. UTC | #3
On 11/06/2017 02:29 PM, Joseph Myers wrote:
> On Sun, 5 Nov 2017, Florian Weimer wrote:
> 
>> On 11/04/2017 06:49 PM, Florian Weimer wrote:
>>> This adds system call wrappers for pkey_alloc, pkey_free, pkey_mprotect,
>>> and x86-64 implementations of pkey_get and pkey_set, which abstract over
>>> the PKRU CPU register and hide the actual number of memory protection
>>> keys supported by the CPU.
>>>
>>> The system call wrapers use unsigned int instead of unsigned long for
>>> parameters, so that no special treatment for x32 is needed.  The flags
>>> argument is currently unused, and the access rights bit mask is limited
>>> to two bits by the current PKRU register layout anyway.
>>
>> I again forgot to add a NEWS entry.
> 
> Documentation in the glibc manual is also needed.

What's the general feeling about these interfaces?  Shall we add them, 
despite their limitations (especially with regard to key reuse)?

Then I'll write documentation.

Thanks,
Florian
Adhemerval Zanella Netto Nov. 6, 2017, 4:49 p.m. UTC | #4
> 
> What's the general feeling about these interfaces?  Shall we add them, despite their limitations (especially with regard to key reuse)?
> 
> Then I'll write documentation.

This is currently only supported on x86 and I am not sure if it will be 
ever used by other architectures, but Linux thinks its interface is 
sufficient generic since it decided to extend it be part of generic
ABI.  I also do not foresee this interface to be deprecated by any other
soon, so I think it should be a valuable adition.

Do you know any project that is already using it?


On 04/11/2017 15:49, Florian Weimer wrote:
> This adds system call wrappers for pkey_alloc, pkey_free, pkey_mprotect,
> and x86-64 implementations of pkey_get and pkey_set, which abstract over
> the PKRU CPU register and hide the actual number of memory protection
> keys supported by the CPU.
> 
> The system call wrapers use unsigned int instead of unsigned long for
> parameters, so that no special treatment for x32 is needed.  The flags
> argument is currently unused, and the access rights bit mask is limited
> to two bits by the current PKRU register layout anyway.

AFAIK there is no need to special handling of unsigned long, which is
32 bits on x32.  There were issues of using {INLINE,INTERNAL}_SYSCALL
along with 64 bits arguments (off_t for instance) and it should be fixed
by 78ca091cdd2c.  The only special handling is required for syscalls that
return 64 bits arguments, for instance 'times', which can't be handled
by {INLINE,INTERNAL}_SYSCALLS (x32 times.c get around by redefining
internal_syscall1 to return a 64 bits value).

So I think there is no reason to use a different symbol signature than
kernel.

> 
> 2017-11-04  Florian Weimer  <fweimer@redhat.com>
> 
> 	Linux: Implement interfaces for memory protection keys
> 	* support/Makefile (libsupport-routines): Add
> 	support_test_compare_failure, xraise, xsigaction, xsignal,
> 	xsysconf.
> 	* support/check.h (TEST_COMPARE): New macro.
> 	(support_test_compare_failure): Declare.
> 	* support/xsignal.h (xraise, xsignal, xsigaction): Declare.
> 	* support/xunistd.h (xsysconf): Declare.
> 	* support/support_test_compare_failure.c: New file.
> 	* support/xraise.c: Likewise.
> 	* support/xsigaction.c: Likewise.
> 	* support/xsignal.c: Likewise.
> 	* support/xsysconf.c: Likewise.
> 	* sysdeps/unix/sysv/linux/Makefile [misc] (routines): Add
> 	pkey_set, pkey_get.
> 	[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/syscalls.list (pkey_alloc, pkey_free)
> 	(pkey_mprotect): Add.
> 	* sysdeps/unix/sysv/linux/tst-pkey.c: New file.
> 	* sysdeps/unix/sysv/linux/x86_64/arch-pkey.h: Likewise.
> 	* sysdeps/unix/sysv/linux/x86_64/pkey_get.c: Likewise.
> 	* sysdeps/unix/sysv/linux/x86_64/pkey_set.c: Likewise.
> 	* sysdeps/unix/sysv/linux/**.abilist: Update.
> 
> diff --git a/support/Makefile b/support/Makefile
> index dafb1737a4..50d4269e24 100644
> --- a/support/Makefile
> +++ b/support/Makefile
> @@ -52,9 +52,10 @@ libsupport-routines = \
>    support_record_failure \
>    support_run_diff \
>    support_shared_allocate \
> -  support_write_file_string \
> +  support_test_compare_failure \
>    support_test_main \
>    support_test_verify_impl \
> +  support_write_file_string \
>    temp_file \
>    write_message \
>    xaccept \
> @@ -84,8 +85,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 \
> @@ -116,14 +117,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/check.h b/support/check.h
> index bdcd12952a..29b709c2b0 100644
> --- a/support/check.h
> +++ b/support/check.h
> @@ -86,6 +86,35 @@ void support_test_verify_exit_impl (int status, const char *file, int line,
>     does not support reporting failures from a DSO.  */
>  void support_record_failure (void);
>  
> +/* Compare the two numbers LEFT and RIGHT and report failure if they
> +   are different.  */
> +#define TEST_COMPARE(left, right)                                       \
> +  ({                                                                    \
> +    __typeof__ (left) __left_value = (left);                            \
> +    __typeof__ (right) __right_value = (right);                         \
> +    _Static_assert (sizeof (__left_value) <= sizeof (long long),        \
> +                    "left value fits into long long");                  \
> +    _Static_assert (sizeof (__right_value) <= sizeof (long long),       \
> +                    "right value fits into long long");                 \
> +    if (__left_value != __right_value                                   \
> +        || ((__left_value > 0) != (__right_value > 0)))                 \
> +      support_test_compare_failure                                      \
> +        (__FILE__, __LINE__,                                            \
> +         #left, __left_value, __left_value > 0,                         \
> +         #right, __right_value, __right_value > 0);                     \
> +  })
> +

I think the macro name should be more explicit since it is not only
comparing two number but also their size.

> +/* Internal implementation of TEST_COMPARE.  LEFT_POSITIVE and
> +   RIGHT_POSITIVE are used to fit both unsigned long long and long
> +   long arguments into LEFT_VALUE and RIGHT_VALUE.  */
> +void support_test_compare_failure (const char *file, int line,
> +                                   const char *left_expr,
> +                                   long long left_value,
> +                                   int left_positive,
> +                                   const char *right_expr,
> +                                   long long right_value,
> +                                   int right_positive);
> +
>  /* Internal function called by the test driver.  */
>  int support_report_failure (int status)
>    __attribute__ ((weak, warn_unused_result));
> diff --git a/support/support_test_compare_failure.c b/support/support_test_compare_failure.c
> new file mode 100644
> index 0000000000..38fec1ca89
> --- /dev/null
> +++ b/support/support_test_compare_failure.c
> @@ -0,0 +1,46 @@
> +/* Reporting mumeric comparison failure.

Typo.

> +   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 <stdio.h>
> +#include <support/check.h>
> +
> +static void
> +report (const char *which, const char *expr, long long value, int positive)
> +{
> +  printf ("  %s: ", which);
> +  if (positive)
> +    printf ("%llu", (unsigned long long) value);
> +  else
> +    printf ("%lld", value);
> +  printf (" (0x%llx); from: %s\n", (unsigned long long) value, expr);
> +}
> +
> +void
> +support_test_compare_failure (const char *file, int line,
> +                              const char *left_expr,
> +                              long long left_value,
> +                              int left_positive,
> +                              const char *right_expr,
> +                              long long right_value,
> +                              int right_positive)
> +{
> +  support_record_failure ();
> +  printf ("%s:%d: numeric comparison failure\n", file, line);
> +  report (" left", left_expr, left_value, left_positive);
> +  report ("right", right_expr, right_value, right_positive);
> +}

Ok.

> 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

Ok.

> 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

Ok.

> 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

Ok.

> 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.  */
>  

Ok.

> 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;
> +}

Ok.

> 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 53e41510e3..095cf93892 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
> +		   open_by_handle_at 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 test-errno-linux tst-sysconf-iov_max \
> -	 tst-memfd_create
> +	 tst-memfd_create 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 992c19729f..798ffc7660 100644
> --- a/sysdeps/unix/sysv/linux/Versions
> +++ b/sysdeps/unix/sysv/linux/Versions
> @@ -168,6 +168,7 @@ libc {
>    }
>    GLIBC_2.27 {
>      memfd_create;
> +    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 140ca28abc..85788be12b 100644
> --- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> @@ -2107,6 +2107,11 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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 strtof128 F
>  GLIBC_2.27 strtof128_l F
> diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
> index f698e1b2f4..3b463dacbe 100644
> --- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
> @@ -2018,6 +2018,11 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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 strtof128 F
>  GLIBC_2.27 strtof128_l F
> diff --git a/sysdeps/unix/sysv/linux/arm/libc.abilist b/sysdeps/unix/sysv/linux/arm/libc.abilist
> index 8a8af3e3e4..a1315aef35 100644
> --- a/sysdeps/unix/sysv/linux/arm/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/arm/libc.abilist
> @@ -108,6 +108,11 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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-linux.h b/sysdeps/unix/sysv/linux/bits/mman-linux.h
> index b091181960..da5ec79334 100644
> --- a/sysdeps/unix/sysv/linux/bits/mman-linux.h
> +++ b/sysdeps/unix/sysv/linux/bits/mman-linux.h
> @@ -109,3 +109,38 @@
>  # define MCL_ONFAULT	4		/* Lock all pages that are
>  					   faulted in.  */
>  #endif
> +
> +/* Memory protection key support.  */
> +#ifdef __USE_GNU
> +
> +/* FLags for pkey_alloc.  */
> +# define PKEY_DISABLE_ACCESS 0x1
> +# define PKEY_DISABLE_WRITE 0x2

Typo on 'FLags'.

> +
> +__BEGIN_DECLS
> +
> +/* 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.  */

Ok.

> diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
> index 5b81a6cd7d..7397d728f2 100644
> --- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
> @@ -1872,6 +1872,11 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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 51ead9e867..cffdf251d6 100644
> --- a/sysdeps/unix/sysv/linux/i386/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
> @@ -2037,6 +2037,11 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
> index 78b4ee8d40..3292510a55 100644
> --- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
> @@ -1901,6 +1901,11 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> index d9c97779e4..636bbdd1a7 100644
> --- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> @@ -109,6 +109,11 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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 4acbf7eeed..6952863f86 100644
> --- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
> @@ -1986,6 +1986,11 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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 93f02f08ce..ac5b56abab 100644
> --- a/sysdeps/unix/sysv/linux/microblaze/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
> @@ -2107,3 +2107,8 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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 795e85de70..bb0958e842 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> @@ -1961,6 +1961,11 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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 dc714057b7..9104eb4d6d 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> @@ -1959,6 +1959,11 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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 ce7bc9b175..58a5d5e141 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> @@ -1957,6 +1957,11 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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 strtof128 F
>  GLIBC_2.27 strtof128_l F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> index 3fdd85eace..2efac14a7d 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> @@ -1952,6 +1952,11 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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 strtof128 F
>  GLIBC_2.27 strtof128_l F
> diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
> index 3e0bcb2a5c..9ef29e4e98 100644
> --- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
> @@ -2148,3 +2148,8 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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;
> +}

Ok.

> 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;
> +}

Ok.

> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> index 375c69d9d1..60c024096f 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> @@ -1990,6 +1990,11 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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 a88172a906..327933c973 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> @@ -1995,6 +1995,11 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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 fa026a332c..b04c31bc10 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
> @@ -2202,3 +2202,8 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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/powerpc/powerpc64/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
> index 838f395d78..e0645e9e25 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
> @@ -109,6 +109,11 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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 41b79c496a..ef434c61a7 100644
> --- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> @@ -1990,6 +1990,11 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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 strtof128 F
>  GLIBC_2.27 strtof128_l F
> diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> index 68251a0e69..4114a4ce57 100644
> --- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> @@ -1891,6 +1891,11 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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 strtof128 F
>  GLIBC_2.27 strtof128_l F
> diff --git a/sysdeps/unix/sysv/linux/sh/libc.abilist b/sysdeps/unix/sysv/linux/sh/libc.abilist
> index bc1aae275e..f4478b0cc5 100644
> --- a/sysdeps/unix/sysv/linux/sh/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sh/libc.abilist
> @@ -1876,6 +1876,11 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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 93e6d092ac..136a57fc0e 100644
> --- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> @@ -1983,6 +1983,11 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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 strtof128 F
>  GLIBC_2.27 strtof128_l F
> diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> index b11d6764d4..9ad0790829 100644
> --- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> @@ -1920,6 +1920,11 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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 strtof128 F
>  GLIBC_2.27 strtof128_l F
> diff --git a/sysdeps/unix/sysv/linux/syscalls.list b/sysdeps/unix/sysv/linux/syscalls.list
> index 40c4fbb9ea..6f657eea2e 100644
> --- a/sysdeps/unix/sysv/linux/syscalls.list
> +++ b/sysdeps/unix/sysv/linux/syscalls.list
> @@ -110,3 +110,6 @@ 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
> +pkey_mprotect	EXTRA	pkey_mprotect	i:aiii  pkey_mprotect

I do not think we can them as default since build against kernel headers
older than v4.9 won't have __NR_pkey_{alloc,free,mprotect).  We need
default implementation with #ifdef __NR_....

> diff --git a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
> index e9eb4ff7bd..d4f2094027 100644
> --- a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
> @@ -2114,3 +2114,8 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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 8f08e909cd..4916dbabb5 100644
> --- a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
> @@ -2114,3 +2114,8 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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 e9eb4ff7bd..d4f2094027 100644
> --- a/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
> @@ -2114,3 +2114,8 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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..3c1f3cfa39
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/tst-pkey.c
> @@ -0,0 +1,320 @@
> +/* 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 *
> +initial_thread_func (void *closure)
> +{
> +  pthread_barrier_wait (&barrier);
> +  struct thread_result *result = get_thread_func (NULL);
> +
> +  /* 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);
> +
> +  xpthread_barrier_init (&barrier, NULL, 2);
> +  pthread_t initial_thread = xpthread_create
> +    (NULL, &initial_thread_func, NULL);
> +
> +  keys[0] = pkey_alloc (0, 0);
> +  if (keys[0] < 0)
> +    {
> +      if (errno == ENOSYS)
> +        {
> +          puts ("warning: kernel does not support memory protection keys");
> +          return EXIT_UNSUPPORTED;
> +        }
> +      if (errno == ENOSPC)
> +        {
> +          puts ("warning: CPU does not support memory protection keys");
> +          return EXIT_UNSUPPORTED;
> +        }
> +      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 (initial_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));
> +            }
> +      }
> +
> +
> +  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);
> +
> +  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_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> index 0a4f7797ac..1ea74f9e8c 100644
> --- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> @@ -1878,6 +1878,11 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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/x86_64/arch-pkey.h b/sysdeps/unix/sysv/linux/x86_64/arch-pkey.h
> new file mode 100644
> index 0000000000..8e9bfdae96
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/x86_64/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;
> +}

I think we should also constrain 'edx' since it is explict cleared on 
'rdpkru' instruction:

  static inline unsigned int
  pkey_read (void)
  {
    unsigned int result, edx;
    __asm__ volatile (".byte 0x0f, 0x01, 0xee"
                      : "=a" (result), "=d" (edx)
		      : "c" (0));
    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_64/pkey_get.c b/sysdeps/unix/sysv/linux/x86_64/pkey_get.c
> new file mode 100644
> index 0000000000..3a9bfbe676
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/x86_64/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;
> +}

I think the kernel example to implement pkey_get is more parametrized and
may lead to a more portable code if the idea is to extend this syscalls
to other architectures than x86.  It adds a arch-specific PKRU_BITS_PER_PKEY
and sets the mask based on current supported flags (which I think won't
be extended at least for x86 since there is no more available unused bits):

  uint32_t mask = (PKEY_DISABLE_ACCESS|PKEY_DISABLE_WRITE);
  uint32_t pkru = pkey_read ();
  uint32_t shifted_pkru = (pkru >> (pkey * PKRU_BITS_PER_PKEY));
  return (shifted_pkru & mask);

I think we can on arch-pkey.h two macros:

  PKRU_BITS_PER_PKEY  - number of bits for the pkey
  PKRU_BITS_MASK      - mask to get the bits

My idea is if eventually this syscalls is used on another architecture
it will follow similat semanthic (otherwise a different method might
lead to different syscalls) and by paratrizing it we can add a generic
interface for Linux instead of multiple implementations with similar
code.


> diff --git a/sysdeps/unix/sysv/linux/x86_64/pkey_set.c b/sysdeps/unix/sysv/linux/x86_64/pkey_set.c
> new file mode 100644
> index 0000000000..91dffd22c3
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/x86_64/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;
> +}

According to this LWN article [2] and man pages [3], the pkey 0 is set 
aside for the default one and this key will never be allocated with 
pkey_alloc(), and its restrictions cannot be changed.  Assuming this 
is still true (since the article is based on non upstream code), what 
happen if try issue a pkey_set with a key 0? Does the kernel silent
ignore it or trap with an invalid instruction?

Also, I think we should parametrize it as for pkey_get to try make as
generic as possible.

[1] https://lwn.net/Articles/689395/
[2] http://man7.org/linux/man-pages/man7/pkeys.7.html

> diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> index 23f6a91429..1d3d598618 100644
> --- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> @@ -2121,3 +2121,8 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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
>
Florian Weimer Nov. 6, 2017, 8:01 p.m. UTC | #5
On 11/06/2017 05:49 PM, Adhemerval Zanella wrote:
>>
>> What's the general feeling about these interfaces?  Shall we add them, despite their limitations (especially with regard to key reuse)?
>>
>> Then I'll write documentation.
> 
> This is currently only supported on x86 and I am not sure if it will be
> ever used by other architectures, but Linux thinks its interface is
> sufficient generic since it decided to extend it be part of generic
> ABI.  I also do not foresee this interface to be deprecated by any other
> soon, so I think it should be a valuable adition.
> 
> Do you know any project that is already using it?

I'm not aware of any users.  To be honest, I just needed a distraction 
over the weekend.

> On 04/11/2017 15:49, Florian Weimer wrote:
>> This adds system call wrappers for pkey_alloc, pkey_free, pkey_mprotect,
>> and x86-64 implementations of pkey_get and pkey_set, which abstract over
>> the PKRU CPU register and hide the actual number of memory protection
>> keys supported by the CPU.
>>
>> The system call wrapers use unsigned int instead of unsigned long for
>> parameters, so that no special treatment for x32 is needed.  The flags
>> argument is currently unused, and the access rights bit mask is limited
>> to two bits by the current PKRU register layout anyway.
> 
> AFAIK there is no need to special handling of unsigned long, which is
> 32 bits on x32.  There were issues of using {INLINE,INTERNAL}_SYSCALL
> along with 64 bits arguments (off_t for instance) and it should be fixed
> by 78ca091cdd2c.  The only special handling is required for syscalls that
> return 64 bits arguments, for instance 'times', which can't be handled
> by {INLINE,INTERNAL}_SYSCALLS (x32 times.c get around by redefining
> internal_syscall1 to return a 64 bits value).

Oh, I was worried that these arguments were expected to be true 64-bit 
values eventually.  Not today, but perhaps in the future.   Then using 
unsigned long would be no good on x32.  But this does not seem to be the 
case, so we can just use unsigned int, to make clear that these 
arguments are actually 32 bits only.

> So I think there is no reason to use a different symbol signature than
> kernel.

I still think using unsigned long should be avoided.

>> +/* Compare the two numbers LEFT and RIGHT and report failure if they
>> +   are different.  */
>> +#define TEST_COMPARE(left, right)                                       \
>> +  ({                                                                    \
>> +    __typeof__ (left) __left_value = (left);                            \
>> +    __typeof__ (right) __right_value = (right);                         \
>> +    _Static_assert (sizeof (__left_value) <= sizeof (long long),        \
>> +                    "left value fits into long long");                  \
>> +    _Static_assert (sizeof (__right_value) <= sizeof (long long),       \
>> +                    "right value fits into long long");                 \
>> +    if (__left_value != __right_value                                   \
>> +        || ((__left_value > 0) != (__right_value > 0)))                 \
>> +      support_test_compare_failure                                      \
>> +        (__FILE__, __LINE__,                                            \
>> +         #left, __left_value, __left_value > 0,                         \
>> +         #right, __right_value, __right_value > 0);                     \
>> +  })
>> +
> 
> I think the macro name should be more explicit since it is not only
> comparing two number but also their size.

Huh?  No, it checks that conversion to long long plus the sign bit is 
not lossy and that the signs match.  So I think the name is accurate.

> 
>> +/* Internal implementation of TEST_COMPARE.  LEFT_POSITIVE and
>> +   RIGHT_POSITIVE are used to fit both unsigned long long and long
>> +   long arguments into LEFT_VALUE and RIGHT_VALUE.  */
>> +void support_test_compare_failure (const char *file, int line,
>> +                                   const char *left_expr,
>> +                                   long long left_value,
>> +                                   int left_positive,
>> +                                   const char *right_expr,
>> +                                   long long right_value,
>> +                                   int right_positive);
>> +
>>   /* Internal function called by the test driver.  */
>>   int support_report_failure (int status)
>>     __attribute__ ((weak, warn_unused_result));
>> diff --git a/support/support_test_compare_failure.c b/support/support_test_compare_failure.c
>> new file mode 100644
>> index 0000000000..38fec1ca89
>> --- /dev/null
>> +++ b/support/support_test_compare_failure.c
>> @@ -0,0 +1,46 @@
>> +/* Reporting mumeric comparison failure.
> 
> Typo.

Thanks, fixed.

>> diff --git a/sysdeps/unix/sysv/linux/bits/mman-linux.h b/sysdeps/unix/sysv/linux/bits/mman-linux.h
>> index b091181960..da5ec79334 100644
>> --- a/sysdeps/unix/sysv/linux/bits/mman-linux.h
>> +++ b/sysdeps/unix/sysv/linux/bits/mman-linux.h
>> @@ -109,3 +109,38 @@
>>   # define MCL_ONFAULT	4		/* Lock all pages that are
>>   					   faulted in.  */
>>   #endif
>> +
>> +/* Memory protection key support.  */
>> +#ifdef __USE_GNU
>> +
>> +/* FLags for pkey_alloc.  */
>> +# define PKEY_DISABLE_ACCESS 0x1
>> +# define PKEY_DISABLE_WRITE 0x2
> 
> Typo on 'FLags'.

Fixed.

>> diff --git a/sysdeps/unix/sysv/linux/syscalls.list b/sysdeps/unix/sysv/linux/syscalls.list
>> index 40c4fbb9ea..6f657eea2e 100644
>> --- a/sysdeps/unix/sysv/linux/syscalls.list
>> +++ b/sysdeps/unix/sysv/linux/syscalls.list
>> @@ -110,3 +110,6 @@ 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
>> +pkey_mprotect	EXTRA	pkey_mprotect	i:aiii  pkey_mprotect
> 
> I do not think we can them as default since build against kernel headers
> older than v4.9 won't have __NR_pkey_{alloc,free,mprotect).  We need
> default implementation with #ifdef __NR_....

Hmm.  I checked that we'd fall back to an ENOSYS stub, but this was for 
memfd_create, which had the stub in the generic API.  That won't apply 
here, so I will have to rework this a bit.

I will sent an updated patch.

>> +/* 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;
>> +}
> 
> I think we should also constrain 'edx' since it is explict cleared on
> 'rdpkru' instruction:
> 
>    static inline unsigned int
>    pkey_read (void)
>    {
>      unsigned int result, edx;
>      __asm__ volatile (".byte 0x0f, 0x01, 0xee"
>                        : "=a" (result), "=d" (edx)
> 		      : "c" (0));
>      return result;
>    }

I think the clobber for rdx takes care of that.

> I think the kernel example to implement pkey_get is more parametrized and
> may lead to a more portable code if the idea is to extend this syscalls
> to other architectures than x86.  It adds a arch-specific PKRU_BITS_PER_PKEY
> and sets the mask based on current supported flags (which I think won't
> be extended at least for x86 since there is no more available unused bits):

Sorry, I disagree with that.  Premature generalization usually does not 
work out because you don't know which parts to generalize.

If we get key revocation on pkey_alloc, pkey_set would have to turn into 
a vsyscall anyway (where the code sequence is known to the kernel, and 
the revocation code can override the PKRU value the pkey_set 
implementation has read, thus closing the race).

>> +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;
>> +}
> 
> According to this LWN article [2] and man pages [3], the pkey 0 is set
> aside for the default one and this key will never be allocated with
> pkey_alloc(), and its restrictions cannot be changed.  Assuming this
> is still true (since the article is based on non upstream code), what
> happen if try issue a pkey_set with a key 0? Does the kernel silent
> ignore it or trap with an invalid instruction?

I think userspace can write whatever it wants to the PKRU register.  The 
register update is always valid.  It's just that some values will not be 
very useful.

I added

   TEST_COMPARE (pkey_set (0, PKEY_DISABLE_WRITE), 0);

to the test case, and it crashes here:

    0x00000000004023c3 <+2083>:  xor    %edi,%edi
    0x00000000004023c5 <+2085>:  mov    $0x2,%esi
    0x00000000004023ca <+2090>:  callq  0x4013d0 <pkey_set@plt>
    0x00000000004023cf <+2095>:  cmp    $0x0,%eax
    0x00000000004023d2 <+2098>:  jne    0x4026d0 <do_test+2864>
    0x00000000004023d8 <+2104>:  mov    $0x6062e0,%ebx
    0x00000000004023dd <+2109>:  mov    (%rbx),%rdi
    0x00000000004023e0 <+2112>:  mov    %r12,%rsi
    0x00000000004023e3 <+2115>:  add    $0x8,%rbx
=> 0x00000000004023e7 <+2119>:  callq  0x403730 <xmunmap>

This is the first memory write after the PKRU update.  It is exactly 
what I expected because the stack has key 0, and we revoked write 
access.  With PKEY_DISABLE_ACCESS, things get a bit weird because the 
coredump writer can't read the memory.  But with single-stepping, I was 
able to confirm that the SIGSEGV happens on the retq instruction in 
pkey_set, which is again as expected.

So I don't think we need to document key 0 as special in any way.  It is 
just a key that will never be returned from pkey_alloc, so unexpected 
things may happen.  I think key 1 is currently special as well because 
it is PKEY_DISABLE_ACCESS, and execute-only mappings will use that.

Thanks,
Florian
Adhemerval Zanella Netto Nov. 6, 2017, 9:46 p.m. UTC | #6
On 06/11/2017 18:01, Florian Weimer wrote:
> On 11/06/2017 05:49 PM, Adhemerval Zanella wrote:
>>>
>>> What's the general feeling about these interfaces?  Shall we add them, despite their limitations (especially with regard to key reuse)?
>>>
>>> Then I'll write documentation.
>>
>> This is currently only supported on x86 and I am not sure if it will be
>> ever used by other architectures, but Linux thinks its interface is
>> sufficient generic since it decided to extend it be part of generic
>> ABI.  I also do not foresee this interface to be deprecated by any other
>> soon, so I think it should be a valuable adition.
>>
>> Do you know any project that is already using it?
> 
> I'm not aware of any users.  To be honest, I just needed a distraction over the weekend.
> 
>> On 04/11/2017 15:49, Florian Weimer wrote:
>>> This adds system call wrappers for pkey_alloc, pkey_free, pkey_mprotect,
>>> and x86-64 implementations of pkey_get and pkey_set, which abstract over
>>> the PKRU CPU register and hide the actual number of memory protection
>>> keys supported by the CPU.
>>>
>>> The system call wrapers use unsigned int instead of unsigned long for
>>> parameters, so that no special treatment for x32 is needed.  The flags
>>> argument is currently unused, and the access rights bit mask is limited
>>> to two bits by the current PKRU register layout anyway.
>>
>> AFAIK there is no need to special handling of unsigned long, which is
>> 32 bits on x32.  There were issues of using {INLINE,INTERNAL}_SYSCALL
>> along with 64 bits arguments (off_t for instance) and it should be fixed
>> by 78ca091cdd2c.  The only special handling is required for syscalls that
>> return 64 bits arguments, for instance 'times', which can't be handled
>> by {INLINE,INTERNAL}_SYSCALLS (x32 times.c get around by redefining
>> internal_syscall1 to return a 64 bits value).
> 
> Oh, I was worried that these arguments were expected to be true 64-bit values eventually.  Not today, but perhaps in the future.   Then using unsigned long would be no good on x32.  But this does not seem to be the case, so we can just use unsigned int, to make clear that these arguments are actually 32 bits only.

Even they were 64 bits argument, current {INLINE,INTERNAL}_SYSCALL on
x32 pass them correctly in syscall arguments.

> 
>> So I think there is no reason to use a different symbol signature than
>> kernel.
> 
> I still think using unsigned long should be avoided.

Is your objection because it will have different argument types for 32
and 64 bits? Currently for x86 indeed it does not make difference since
for 'wrpkru' instruction on x86_64 the high-order 32-bits of RCX, RDX 
and RAX are ignored.

But if we aim to make it a generic API I think we should stick to what
kernel provides for main reason we can not foresee what kind of usage
future architecture might do with this value.

> 
>>> +/* Compare the two numbers LEFT and RIGHT and report failure if they
>>> +   are different.  */
>>> +#define TEST_COMPARE(left, right)                                       \
>>> +  ({                                                                    \
>>> +    __typeof__ (left) __left_value = (left);                            \
>>> +    __typeof__ (right) __right_value = (right);                         \
>>> +    _Static_assert (sizeof (__left_value) <= sizeof (long long),        \
>>> +                    "left value fits into long long");                  \
>>> +    _Static_assert (sizeof (__right_value) <= sizeof (long long),       \
>>> +                    "right value fits into long long");                 \
>>> +    if (__left_value != __right_value                                   \
>>> +        || ((__left_value > 0) != (__right_value > 0)))                 \
>>> +      support_test_compare_failure                                      \
>>> +        (__FILE__, __LINE__,                                            \
>>> +         #left, __left_value, __left_value > 0,                         \
>>> +         #right, __right_value, __right_value > 0);                     \
>>> +  })
>>> +
>>
>> I think the macro name should be more explicit since it is not only
>> comparing two number but also their size.
> 
> Huh?  No, it checks that conversion to long long plus the sign bit is not lossy and that the signs match.  So I think the name is accurate.

Yeah, but it ties some type (long long) and also some sign (since it
is comparing if both sides are positive) to a quite generic name.
Maybe TEST_COMPARE_LL_INT_POSITIVE?

> 
>>
>>> +/* Internal implementation of TEST_COMPARE.  LEFT_POSITIVE and
>>> +   RIGHT_POSITIVE are used to fit both unsigned long long and long
>>> +   long arguments into LEFT_VALUE and RIGHT_VALUE.  */
>>> +void support_test_compare_failure (const char *file, int line,
>>> +                                   const char *left_expr,
>>> +                                   long long left_value,
>>> +                                   int left_positive,
>>> +                                   const char *right_expr,
>>> +                                   long long right_value,
>>> +                                   int right_positive);
>>> +
>>>   /* Internal function called by the test driver.  */
>>>   int support_report_failure (int status)
>>>     __attribute__ ((weak, warn_unused_result));
>>> diff --git a/support/support_test_compare_failure.c b/support/support_test_compare_failure.c
>>> new file mode 100644
>>> index 0000000000..38fec1ca89
>>> --- /dev/null
>>> +++ b/support/support_test_compare_failure.c
>>> @@ -0,0 +1,46 @@
>>> +/* Reporting mumeric comparison failure.
>>
>> Typo.
> 
> Thanks, fixed.
> 
>>> diff --git a/sysdeps/unix/sysv/linux/bits/mman-linux.h b/sysdeps/unix/sysv/linux/bits/mman-linux.h
>>> index b091181960..da5ec79334 100644
>>> --- a/sysdeps/unix/sysv/linux/bits/mman-linux.h
>>> +++ b/sysdeps/unix/sysv/linux/bits/mman-linux.h
>>> @@ -109,3 +109,38 @@
>>>   # define MCL_ONFAULT    4        /* Lock all pages that are
>>>                          faulted in.  */
>>>   #endif
>>> +
>>> +/* Memory protection key support.  */
>>> +#ifdef __USE_GNU
>>> +
>>> +/* FLags for pkey_alloc.  */
>>> +# define PKEY_DISABLE_ACCESS 0x1
>>> +# define PKEY_DISABLE_WRITE 0x2
>>
>> Typo on 'FLags'.
> 
> Fixed.
> 
>>> diff --git a/sysdeps/unix/sysv/linux/syscalls.list b/sysdeps/unix/sysv/linux/syscalls.list
>>> index 40c4fbb9ea..6f657eea2e 100644
>>> --- a/sysdeps/unix/sysv/linux/syscalls.list
>>> +++ b/sysdeps/unix/sysv/linux/syscalls.list
>>> @@ -110,3 +110,6 @@ 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
>>> +pkey_mprotect    EXTRA    pkey_mprotect    i:aiii  pkey_mprotect
>>
>> I do not think we can them as default since build against kernel headers
>> older than v4.9 won't have __NR_pkey_{alloc,free,mprotect).  We need
>> default implementation with #ifdef __NR_....
> 
> Hmm.  I checked that we'd fall back to an ENOSYS stub, but this was for memfd_create, which had the stub in the generic API.  That won't apply here, so I will have to rework this a bit.
> 
> I will sent an updated patch.
> 
>>> +/* 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;
>>> +}
>>
>> I think we should also constrain 'edx' since it is explict cleared on
>> 'rdpkru' instruction:
>>
>>    static inline unsigned int
>>    pkey_read (void)
>>    {
>>      unsigned int result, edx;
>>      __asm__ volatile (".byte 0x0f, 0x01, 0xee"
>>                        : "=a" (result), "=d" (edx)
>>               : "c" (0));
>>      return result;
>>    }
> 
> I think the clobber for rdx takes care of that.
> 
>> I think the kernel example to implement pkey_get is more parametrized and
>> may lead to a more portable code if the idea is to extend this syscalls
>> to other architectures than x86.  It adds a arch-specific PKRU_BITS_PER_PKEY
>> and sets the mask based on current supported flags (which I think won't
>> be extended at least for x86 since there is no more available unused bits):
> 
> Sorry, I disagree with that.  Premature generalization usually does not work out because you don't know which parts to generalize.
> 
> If we get key revocation on pkey_alloc, pkey_set would have to turn into a vsyscall anyway (where the code sequence is known to the kernel, and the revocation code can override the PKRU value the pkey_set implementation has read, thus closing the race).

Alright, that is fair point. I am thinking more about the sense of
what should be generic such a maximum number of bits for the mask,
the possible mask value and to set them on the internal flag.  But
I think if it were the case in the future we can work a more generic
implementation.

> 
>>> +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;
>>> +}
>>
>> According to this LWN article [2] and man pages [3], the pkey 0 is set
>> aside for the default one and this key will never be allocated with
>> pkey_alloc(), and its restrictions cannot be changed.  Assuming this
>> is still true (since the article is based on non upstream code), what
>> happen if try issue a pkey_set with a key 0? Does the kernel silent
>> ignore it or trap with an invalid instruction?
> 
> I think userspace can write whatever it wants to the PKRU register.  The register update is always valid.  It's just that some values will not be very useful.
> 
> I added
> 
>   TEST_COMPARE (pkey_set (0, PKEY_DISABLE_WRITE), 0);
> 
> to the test case, and it crashes here:
> 
>    0x00000000004023c3 <+2083>:  xor    %edi,%edi
>    0x00000000004023c5 <+2085>:  mov    $0x2,%esi
>    0x00000000004023ca <+2090>:  callq  0x4013d0 <pkey_set@plt>
>    0x00000000004023cf <+2095>:  cmp    $0x0,%eax
>    0x00000000004023d2 <+2098>:  jne    0x4026d0 <do_test+2864>
>    0x00000000004023d8 <+2104>:  mov    $0x6062e0,%ebx
>    0x00000000004023dd <+2109>:  mov    (%rbx),%rdi
>    0x00000000004023e0 <+2112>:  mov    %r12,%rsi
>    0x00000000004023e3 <+2115>:  add    $0x8,%rbx
> => 0x00000000004023e7 <+2119>:  callq  0x403730 <xmunmap>
> 
> This is the first memory write after the PKRU update.  It is exactly what I expected because the stack has key 0, and we revoked write access.  With PKEY_DISABLE_ACCESS, things get a bit weird because the coredump writer can't read the memory.  But with single-stepping, I was able to confirm that the SIGSEGV happens on the retq instruction in pkey_set, which is again as expected.
> 
> So I don't think we need to document key 0 as special in any way.  It is just a key that will never be returned from pkey_alloc, so unexpected things may happen.  I think key 1 is currently special as well because it is PKEY_DISABLE_ACCESS, and execute-only mappings will use that.
> 
> Thanks,
> Florian

What about new pkey_mprotect (..., 0) after pkey_set (0, ...)? Will it follow
the new key setting for key 0 or kernel will use a default value?

My question is of you think we need to filter and/or document key 0 (and 1 as
you indicate) as being special?
Florian Weimer Nov. 6, 2017, 10:03 p.m. UTC | #7
* Adhemerval Zanella:

>> Oh, I was worried that these arguments were expected to be true
>> 64-bit values eventually.  Not today, but perhaps in the future.  
>> Then using unsigned long would be no good on x32.  But this does not
>> seem to be the case, so we can just use unsigned int, to make clear
>> that these arguments are actually 32 bits only.
>
> Even they were 64 bits argument, current {INLINE,INTERNAL}_SYSCALL on
> x32 pass them correctly in syscall arguments.

But there would only be 32 bits to pass.

>>> So I think there is no reason to use a different symbol signature than
>>> kernel.
>> 
>> I still think using unsigned long should be avoided.
>
> Is your objection because it will have different argument types for 32
> and 64 bits?

Yes, if 64 bits were needed, then it would have to be unsigned long
long on x32.  But we need only 32 actually (0 and 3 bits for the
upcoming POWER support), so unsigned int is the right type, I think.

> Currently for x86 indeed it does not make difference since for
> 'wrpkru' instruction on x86_64 the high-order 32-bits of RCX, RDX
> and RAX are ignored.

The size of the PKRU register does not matter.  pkey_alloc does not
use a bitmap of keys, it returns a key number (which happens to be an
index into PKRU on x86-64, but this is not exposed in the API).

> But if we aim to make it a generic API I think we should stick to
> what kernel provides for main reason we can not foresee what kind of
> usage future architecture might do with this value.

Well, it's a generic interface, so if there's an expectation that the
generic code might eventually use a flag beyond 32 bits, we'd better
using uint64_t today.  But that seems quite an overkill.

>>>> +/* Compare the two numbers LEFT and RIGHT and report failure if they
>>>> +   are different.  */
>>>> +#define TEST_COMPARE(left, right)                                       \
>>>> +  ({                                                                    \
>>>> +    __typeof__ (left) __left_value = (left);                            \
>>>> +    __typeof__ (right) __right_value = (right);                         \
>>>> +    _Static_assert (sizeof (__left_value) <= sizeof (long long),        \
>>>> +                    "left value fits into long long");                  \
>>>> +    _Static_assert (sizeof (__right_value) <= sizeof (long long),       \
>>>> +                    "right value fits into long long");                 \
>>>> +    if (__left_value != __right_value                                   \
>>>> +        || ((__left_value > 0) != (__right_value > 0)))                 \
>>>> +      support_test_compare_failure                                      \
>>>> +        (__FILE__, __LINE__,                                            \
>>>> +         #left, __left_value, __left_value > 0,                         \
>>>> +         #right, __right_value, __right_value > 0);                     \
>>>> +  })
>>>> +
>>>
>>> I think the macro name should be more explicit since it is not only
>>> comparing two number but also their size.
>> 
>> Huh?  No, it checks that conversion to long long plus the sign bit
>> is not lossy and that the signs match.  So I think the name is
>> accurate.
>
> Yeah, but it ties some type (long long) and also some sign (since it
> is comparing if both sides are positive) to a quite generic name.
> Maybe TEST_COMPARE_LL_INT_POSITIVE?

It uses long long as a proxy for intmax_t.  I don't want to include
<inttypes.h> due to namespace pollution.

The compile-time check is there to prevent the accidental use of the
macro with 128-bit integer types.

>> Sorry, I disagree with that.  Premature generalization usually does
>> not work out because you don't know which parts to generalize.
>> 
>> If we get key revocation on pkey_alloc, pkey_set would have to turn
>> into a vsyscall anyway (where the code sequence is known to the
>> kernel, and the revocation code can override the PKRU value the
>> pkey_set implementation has read, thus closing the race).
>
> Alright, that is fair point. I am thinking more about the sense of
> what should be generic such a maximum number of bits for the mask,
> the possible mask value and to set them on the internal flag.  But
> I think if it were the case in the future we can work a more generic
> implementation.

I just saw POWER patches fly by.  There, the executable permission
seems to be in a separate register which cannot be updated directly
from userspace. 8-/

> What about new pkey_mprotect (..., 0) after pkey_set (0, ...)? Will
> it follow the new key setting for key 0 or kernel will use a default
> value?

It's important that the keys are basically additional tag bits on a
page.  In isolation they do not grant or reject access.  These tag
bits only select which access rights associated with the current
thread should be applied to the page.  So pkey_mprotect and pkey_set
are completely indepedent: pkey_mprotect changes the tag bits on the
page (but nothing else) and does not depend on the current thread's
access rights, and pkey_set changes the current thread's access rights
only (without changing the page tag bits set by pkey_mprotect before
or after).  Crucially, these keys do not any additional protection
flags to pages.  The tag bits gain meaning only in combination with
the access rights of a running thread.

> My question is of you think we need to filter and/or document key 0
> (and 1 as you indicate) as being special?

No, we shouldn't do that.  The kernel will do it for us as part of
pkey_alloc, and that should be sufficient.
Joseph Myers Nov. 6, 2017, 10:59 p.m. UTC | #8
On Mon, 6 Nov 2017, Florian Weimer wrote:

> > > diff --git a/sysdeps/unix/sysv/linux/syscalls.list
> > > b/sysdeps/unix/sysv/linux/syscalls.list
> > > index 40c4fbb9ea..6f657eea2e 100644
> > > --- a/sysdeps/unix/sysv/linux/syscalls.list
> > > +++ b/sysdeps/unix/sysv/linux/syscalls.list
> > > @@ -110,3 +110,6 @@ 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
> > > +pkey_mprotect	EXTRA	pkey_mprotect	i:aiii  pkey_mprotect
> > 
> > I do not think we can them as default since build against kernel headers
> > older than v4.9 won't have __NR_pkey_{alloc,free,mprotect).  We need
> > default implementation with #ifdef __NR_....
> 
> Hmm.  I checked that we'd fall back to an ENOSYS stub, but this was for
> memfd_create, which had the stub in the generic API.  That won't apply here,
> so I will have to rework this a bit.

I'd expect the stub-syscalls code in sysdeps/unix/Makefile to apply here.  
These entries are essentially much like the bdflush one, which exercises 
the ENOSYS generation on x86_64 as a syscall not existing there.
Adhemerval Zanella Netto Nov. 7, 2017, 2:16 a.m. UTC | #9
On 06/11/2017 20:59, Joseph Myers wrote:
> On Mon, 6 Nov 2017, Florian Weimer wrote:
> 
>>>> diff --git a/sysdeps/unix/sysv/linux/syscalls.list
>>>> b/sysdeps/unix/sysv/linux/syscalls.list
>>>> index 40c4fbb9ea..6f657eea2e 100644
>>>> --- a/sysdeps/unix/sysv/linux/syscalls.list
>>>> +++ b/sysdeps/unix/sysv/linux/syscalls.list
>>>> @@ -110,3 +110,6 @@ 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
>>>> +pkey_mprotect	EXTRA	pkey_mprotect	i:aiii  pkey_mprotect
>>>
>>> I do not think we can them as default since build against kernel headers
>>> older than v4.9 won't have __NR_pkey_{alloc,free,mprotect).  We need
>>> default implementation with #ifdef __NR_....
>>
>> Hmm.  I checked that we'd fall back to an ENOSYS stub, but this was for
>> memfd_create, which had the stub in the generic API.  That won't apply here,
>> so I will have to rework this a bit.
> 
> I'd expect the stub-syscalls code in sysdeps/unix/Makefile to apply here.  
> These entries are essentially much like the bdflush one, which exercises 
> the ENOSYS generation on x86_64 as a syscall not existing there.
> 

Indeed this is what happens for non defined __NR_ syscalls, sorry for the trouble.
Adhemerval Zanella Netto Nov. 7, 2017, 7:59 p.m. UTC | #10
On 06/11/2017 20:03, Florian Weimer wrote:
> * Adhemerval Zanella:
> 
>>> Oh, I was worried that these arguments were expected to be true
>>> 64-bit values eventually.  Not today, but perhaps in the future.  
>>> Then using unsigned long would be no good on x32.  But this does not
>>> seem to be the case, so we can just use unsigned int, to make clear
>>> that these arguments are actually 32 bits only.
>>
>> Even they were 64 bits argument, current {INLINE,INTERNAL}_SYSCALL on
>> x32 pass them correctly in syscall arguments.
> 
> But there would only be 32 bits to pass.
> 
>>>> So I think there is no reason to use a different symbol signature than
>>>> kernel.
>>>
>>> I still think using unsigned long should be avoided.
>>
>> Is your objection because it will have different argument types for 32
>> and 64 bits?
> 
> Yes, if 64 bits were needed, then it would have to be unsigned long
> long on x32.  But we need only 32 actually (0 and 3 bits for the
> upcoming POWER support), so unsigned int is the right type, I think.

Right, I would like to try deviate as little as possible from the
kernel but I think we can have a different signature for these function.

> 
>> Currently for x86 indeed it does not make difference since for
>> 'wrpkru' instruction on x86_64 the high-order 32-bits of RCX, RDX
>> and RAX are ignored.
> 
> The size of the PKRU register does not matter.  pkey_alloc does not
> use a bitmap of keys, it returns a key number (which happens to be an
> index into PKRU on x86-64, but this is not exposed in the API).
> 
>> But if we aim to make it a generic API I think we should stick to
>> what kernel provides for main reason we can not foresee what kind of
>> usage future architecture might do with this value.
> 
> Well, it's a generic interface, so if there's an expectation that the
> generic code might eventually use a flag beyond 32 bits, we'd better
> using uint64_t today.  But that seems quite an overkill.

Ok, seems a reasonable approach.

> 
>>>>> +/* Compare the two numbers LEFT and RIGHT and report failure if they
>>>>> +   are different.  */
>>>>> +#define TEST_COMPARE(left, right)                                       \
>>>>> +  ({                                                                    \
>>>>> +    __typeof__ (left) __left_value = (left);                            \
>>>>> +    __typeof__ (right) __right_value = (right);                         \
>>>>> +    _Static_assert (sizeof (__left_value) <= sizeof (long long),        \
>>>>> +                    "left value fits into long long");                  \
>>>>> +    _Static_assert (sizeof (__right_value) <= sizeof (long long),       \
>>>>> +                    "right value fits into long long");                 \
>>>>> +    if (__left_value != __right_value                                   \
>>>>> +        || ((__left_value > 0) != (__right_value > 0)))                 \
>>>>> +      support_test_compare_failure                                      \
>>>>> +        (__FILE__, __LINE__,                                            \
>>>>> +         #left, __left_value, __left_value > 0,                         \
>>>>> +         #right, __right_value, __right_value > 0);                     \
>>>>> +  })
>>>>> +
>>>>
>>>> I think the macro name should be more explicit since it is not only
>>>> comparing two number but also their size.
>>>
>>> Huh?  No, it checks that conversion to long long plus the sign bit
>>> is not lossy and that the signs match.  So I think the name is
>>> accurate.
>>
>> Yeah, but it ties some type (long long) and also some sign (since it
>> is comparing if both sides are positive) to a quite generic name.
>> Maybe TEST_COMPARE_LL_INT_POSITIVE?
> 
> It uses long long as a proxy for intmax_t.  I don't want to include
> <inttypes.h> due to namespace pollution.
> 
> The compile-time check is there to prevent the accidental use of the
> macro with 128-bit integer types.

Ok, would you mind adding this comment on macro implementation?

> 
>>> Sorry, I disagree with that.  Premature generalization usually does
>>> not work out because you don't know which parts to generalize.
>>>
>>> If we get key revocation on pkey_alloc, pkey_set would have to turn
>>> into a vsyscall anyway (where the code sequence is known to the
>>> kernel, and the revocation code can override the PKRU value the
>>> pkey_set implementation has read, thus closing the race).
>>
>> Alright, that is fair point. I am thinking more about the sense of
>> what should be generic such a maximum number of bits for the mask,
>> the possible mask value and to set them on the internal flag.  But
>> I think if it were the case in the future we can work a more generic
>> implementation.
> 
> I just saw POWER patches fly by.  There, the executable permission
> seems to be in a separate register which cannot be updated directly
> from userspace. 8-/

It is at v9 now [1] (and you seems to be already aware since you replied
about key reuse issue). And they are solving the executable permission
userspace issue it by adding a new syscall (__NR_pkey_modify).

Reading powerpc patchset it seems to me that it will be worth add
some generalization on arch-pkey.h and pkey_{set,get}

  - Based on current patchset, powerpc seems to define different
    value for PKEY_DISABLE_ACCESS, so I think it would be worth
    to parametrize by moving it to arch-defined header.

  - What about adding this on arch-pkey.h

   ---
   #define HAVE_ARCH_PKEY          1
   #define NR_PKEYS                16
   #define PKEY_BITS_PER_PKEY      2

   static inline uint32_t
   pkey_shift (uint32_t pkey)
   {
     return pkey * PKEY_BITS_PER_PKEY;
   }
   ---

   And define as pkey_get:

   ---
   uint32_t pkey_get (int pkey, unsigned long flags)
   {
   #if HAVE_ARCH_PKEY
     uint32_t mask = (PKEY_DISABLE_ACCESS|PKEY_DISABLE_WRITE);

     pkey_reg_t pkey_reg = pkey_read ();
     pkey_reg_t pkey_shifted = pkey_reg >> pkey_shift (pkey);

     return pkey_shifted & mask;
   #else
     return -1;
   #endif
   }
   ---

   With this a possible future powerpc patch would just need to
   add code on arch-pkey.h instead of reimplement pkey_set.c as
   well.

   - I would expect something similar to pkey_set, but also issuing
     the syscall for executable permission masking.   
 

[1] https://lists.ozlabs.org/pipermail/linuxppc-dev/2017-November/165602.html

> 
>> What about new pkey_mprotect (..., 0) after pkey_set (0, ...)? Will
>> it follow the new key setting for key 0 or kernel will use a default
>> value?
> 
> It's important that the keys are basically additional tag bits on a
> page.  In isolation they do not grant or reject access.  These tag
> bits only select which access rights associated with the current
> thread should be applied to the page.  So pkey_mprotect and pkey_set
> are completely indepedent: pkey_mprotect changes the tag bits on the
> page (but nothing else) and does not depend on the current thread's
> access rights, and pkey_set changes the current thread's access rights
> only (without changing the page tag bits set by pkey_mprotect before
> or after).  Crucially, these keys do not any additional protection
> flags to pages.  The tag bits gain meaning only in combination with
> the access rights of a running thread.

My understanding from documentation it key 0 is default one and it
is assigned to any memory regions (through mmap) which has not been 
explicitly assigned through pkey_mprotect. My questioning whether we
should filter out key 0 is to:

  1. Issue an error when application tries to use a key which has
     not been previously allocated by pkey_alloc (since key 0 is
     pre-allocated by the kernel).

  2. Avoid changing default masking bits for pages which has not
     been explicitly assigned through a pkey_mprotect.

However this does not prevent uses from issuing a RDPKRU/WRPKRU
directly.

> 
>> My question is of you think we need to filter and/or document key 0
>> (and 1 as you indicate) as being special?
> 
> No, we shouldn't do that.  The kernel will do it for us as part of
> pkey_alloc, and that should be sufficient.
>
Florian Weimer Nov. 23, 2017, 2:36 p.m. UTC | #11
On 11/07/2017 08:59 PM, Adhemerval Zanella wrote:
>> The compile-time check is there to prevent the accidental use of the
>> macro with 128-bit integer types.
> 
> Ok, would you mind adding this comment on macro implementation?

It's included in the separate patch I posted.

> Reading powerpc patchset it seems to me that it will be worth add
> some generalization on arch-pkey.h and pkey_{set,get}
> 
>    - Based on current patchset, powerpc seems to define different
>      value for PKEY_DISABLE_ACCESS, so I think it would be worth
>      to parametrize by moving it to arch-defined header.
> 
>    - What about adding this on arch-pkey.h

I'm still against this premature generalization.  We can revisit this 
topic once POWER support lands in the kernel.  s390 has hardware support 
as well, and if there are three architectures with this features, a 
pattern hopefully emerges.

> My understanding from documentation it key 0 is default one and it
> is assigned to any memory regions (through mmap) which has not been
> explicitly assigned through pkey_mprotect. My questioning whether we
> should filter out key 0 is to:
> 
>    1. Issue an error when application tries to use a key which has
>       not been previously allocated by pkey_alloc (since key 0 is
>       pre-allocated by the kernel).
> 
>    2. Avoid changing default masking bits for pages which has not
>       been explicitly assigned through a pkey_mprotect.
> 
> However this does not prevent uses from issuing a RDPKRU/WRPKRU
> directly.

There is no way to probe allocated keys, so I don't think we can 
implement more extensive checking without further kernel interfaces.

I'm attaching an updated patch with a first stab at documentation.  The 
documentation is at odds with the manual pages, but this is because what 
I see in tst-pkey does not match the behavior described in the manual page.

I changed the implementation of pkey_mprotect to use plain mprotect for 
-1 keys, so that application usage is simplified.

Thanks,
Florian
Subject: [PATCH] Linux: Implement interfaces for memory protection keys
To: libc-alpha@sourceware.org

This adds system call wrappers for pkey_alloc, pkey_free, pkey_mprotect,
and x86-64 implementations of pkey_get and pkey_set, which abstract over
the PKRU CPU register and hide the actual number of memory protection
keys supported by the CPU.  pkey_mprotect with a -1 key is implemented
using mprotect, so it will work even if the kernel does not support the
pkey_mprotect system call.

The system call wrapers use unsigned int instead of unsigned long for
parameters, so that no special treatment for x32 is needed.  The flags
argument is currently unused, and the access rights bit mask is limited
to two bits by the current PKRU register layout anyway.

2017-11-23  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: Likewise.
	* 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_64/arch-pkey.h: Likewise.
	* sysdeps/unix/sysv/linux/x86_64/pkey_get.c: Likewise.
	* sysdeps/unix/sysv/linux/x86_64/pkey_set.c: Likewise.
	* sysdeps/unix/sysv/linux/**.abilist: Update.

diff --git a/NEWS b/NEWS
index ab14d1eb1b..af7871f4c1 100644
--- a/NEWS
+++ b/NEWS
@@ -37,6 +37,10 @@ Major new features:
 
 * glibc now implements the memfd_create function on Linux.
 
+* Support for memory protection keys was added.  The <sys/mman.h> header now
+  declares the functions pkey_alloc, pkey_free, pkey_memprotect, 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 3f5dd90260..618e3960cb 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 supported
+@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 c484d2688a..255f4e4a94 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
+		   open_by_handle_at 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
+	 test-errno-linux tst-memfd_create 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 6f2fe516d7..7d10a96eef 100644
--- a/sysdeps/unix/sysv/linux/Versions
+++ b/sysdeps/unix/sysv/linux/Versions
@@ -168,6 +168,7 @@ libc {
   }
   GLIBC_2.27 {
     memfd_create;
+    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 140ca28abc..85788be12b 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
@@ -2107,6 +2107,11 @@ GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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 strtof128 F
 GLIBC_2.27 strtof128_l F
diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
index f698e1b2f4..3b463dacbe 100644
--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
@@ -2018,6 +2018,11 @@ GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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 strtof128 F
 GLIBC_2.27 strtof128_l F
diff --git a/sysdeps/unix/sysv/linux/arm/libc.abilist b/sysdeps/unix/sysv/linux/arm/libc.abilist
index 8a8af3e3e4..a1315aef35 100644
--- a/sysdeps/unix/sysv/linux/arm/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/libc.abilist
@@ -108,6 +108,11 @@ GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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-linux.h b/sysdeps/unix/sysv/linux/bits/mman-linux.h
index 1ffa5490af..dc79e5cb0b 100644
--- a/sysdeps/unix/sysv/linux/bits/mman-linux.h
+++ b/sysdeps/unix/sysv/linux/bits/mman-linux.h
@@ -118,12 +118,38 @@
 # define MFD_ALLOW_SEALING 2U
 # define MFD_HUGETLB 4U
 
+/* Access rights for pkey_alloc.  */
+# define PKEY_DISABLE_ACCESS 0x1
+# define PKEY_DISABLE_WRITE 0x2
+
 __BEGIN_DECLS
 
 /* Create a new memory file descriptor.  NAME is a name for debugging.
    FLAGS is a combination of the MFD_* constants.  */
 int memfd_create (const char *__name, 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 5b81a6cd7d..7397d728f2 100644
--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
+++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
@@ -1872,6 +1872,11 @@ GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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 51ead9e867..cffdf251d6 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -2037,6 +2037,11 @@ GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
index 78b4ee8d40..3292510a55 100644
--- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
@@ -1901,6 +1901,11 @@ GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
index d9c97779e4..636bbdd1a7 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
@@ -109,6 +109,11 @@ GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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 4acbf7eeed..6952863f86 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
@@ -1986,6 +1986,11 @@ GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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 93f02f08ce..ac5b56abab 100644
--- a/sysdeps/unix/sysv/linux/microblaze/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
@@ -2107,3 +2107,8 @@ GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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 795e85de70..bb0958e842 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
@@ -1961,6 +1961,11 @@ GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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 dc714057b7..9104eb4d6d 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
@@ -1959,6 +1959,11 @@ GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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 ce7bc9b175..58a5d5e141 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
@@ -1957,6 +1957,11 @@ GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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 strtof128 F
 GLIBC_2.27 strtof128_l F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
index 3fdd85eace..2efac14a7d 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
@@ -1952,6 +1952,11 @@ GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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 strtof128 F
 GLIBC_2.27 strtof128_l F
diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
index 3e0bcb2a5c..9ef29e4e98 100644
--- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
+++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
@@ -2148,3 +2148,8 @@ GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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..683f070e98
--- /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 (pkey_mprotect, 4, 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 375c69d9d1..60c024096f 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
@@ -1990,6 +1990,11 @@ GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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 a88172a906..327933c973 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
@@ -1995,6 +1995,11 @@ GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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 fa026a332c..b04c31bc10 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
@@ -2202,3 +2202,8 @@ GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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/powerpc/powerpc64/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
index 838f395d78..e0645e9e25 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
@@ -109,6 +109,11 @@ GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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 41b79c496a..ef434c61a7 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
@@ -1990,6 +1990,11 @@ GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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 strtof128 F
 GLIBC_2.27 strtof128_l F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
index 68251a0e69..4114a4ce57 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
@@ -1891,6 +1891,11 @@ GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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 strtof128 F
 GLIBC_2.27 strtof128_l F
diff --git a/sysdeps/unix/sysv/linux/sh/libc.abilist b/sysdeps/unix/sysv/linux/sh/libc.abilist
index bc1aae275e..f4478b0cc5 100644
--- a/sysdeps/unix/sysv/linux/sh/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/libc.abilist
@@ -1876,6 +1876,11 @@ GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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 93e6d092ac..136a57fc0e 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
@@ -1983,6 +1983,11 @@ GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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 strtof128 F
 GLIBC_2.27 strtof128_l F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
index b11d6764d4..9ad0790829 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
@@ -1920,6 +1920,11 @@ GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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 strtof128 F
 GLIBC_2.27 strtof128_l 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 e9eb4ff7bd..d4f2094027 100644
--- a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
@@ -2114,3 +2114,8 @@ GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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 8f08e909cd..4916dbabb5 100644
--- a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
@@ -2114,3 +2114,8 @@ GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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 e9eb4ff7bd..d4f2094027 100644
--- a/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
+++ b/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
@@ -2114,3 +2114,8 @@ GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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..5a9ef175ac
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/tst-pkey.c
@@ -0,0 +1,405 @@
+/* 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)
+        {
+          puts ("warning: kernel does not support memory protection keys");
+          return EXIT_UNSUPPORTED;
+        }
+      /* The manual page says that ENOSPC is used to report lack of
+         CPU support, but the kernel returns EINVAL.  */
+      if (errno == ENOSPC || errno == EINVAL)
+        {
+          printf ("warning: CPU does not support memory protection keys: %m");
+          return EXIT_UNSUPPORTED;
+        }
+      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_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
index 0a4f7797ac..1ea74f9e8c 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -1878,6 +1878,11 @@ GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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/x86_64/arch-pkey.h b/sysdeps/unix/sysv/linux/x86_64/arch-pkey.h
new file mode 100644
index 0000000000..8e9bfdae96
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/x86_64/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_64/pkey_get.c b/sysdeps/unix/sysv/linux/x86_64/pkey_get.c
new file mode 100644
index 0000000000..3a9bfbe676
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/x86_64/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_64/pkey_set.c b/sysdeps/unix/sysv/linux/x86_64/pkey_set.c
new file mode 100644
index 0000000000..91dffd22c3
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/x86_64/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/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
index 23f6a91429..1d3d598618 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2121,3 +2121,8 @@ GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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
Adhemerval Zanella Netto Nov. 28, 2017, 7:01 p.m. UTC | #12
On 23/11/2017 12:36, Florian Weimer wrote:
> On 11/07/2017 08:59 PM, Adhemerval Zanella wrote:
>>> The compile-time check is there to prevent the accidental use of the
>>> macro with 128-bit integer types.
>>
>> Ok, would you mind adding this comment on macro implementation?
> 
> It's included in the separate patch I posted.

Ok.

> 
>> Reading powerpc patchset it seems to me that it will be worth add
>> some generalization on arch-pkey.h and pkey_{set,get}
>>
>>    - Based on current patchset, powerpc seems to define different
>>      value for PKEY_DISABLE_ACCESS, so I think it would be worth
>>      to parametrize by moving it to arch-defined header.
>>
>>    - What about adding this on arch-pkey.h
> 
> I'm still against this premature generalization.  We can revisit this topic once POWER support lands in the kernel.  s390 has hardware support as well, and if there are three architectures with this features, a pattern hopefully emerges.

Fair enough.

> 
>> My understanding from documentation it key 0 is default one and it
>> is assigned to any memory regions (through mmap) which has not been
>> explicitly assigned through pkey_mprotect. My questioning whether we
>> should filter out key 0 is to:
>>
>>    1. Issue an error when application tries to use a key which has
>>       not been previously allocated by pkey_alloc (since key 0 is
>>       pre-allocated by the kernel).
>>
>>    2. Avoid changing default masking bits for pages which has not
>>       been explicitly assigned through a pkey_mprotect.
>>
>> However this does not prevent uses from issuing a RDPKRU/WRPKRU
>> directly.
> 
> There is no way to probe allocated keys, so I don't think we can implement more extensive checking without further kernel interfaces.

That was my understanding as well, I was just checking if I missed something
reading pkey documentation (from both architecture and kernel level). I
noticed there is some idea of extending the sysfs to provide more information
about the system supported keys [1], but even through it also does not
really provide the information of the allocated keys.

[1] https://marc.info/?l=linux-kernel&m=150995950219669&w=2

> 
> I'm attaching an updated patch with a first stab at documentation.  The documentation is at odds with the manual pages, but this is because what I see in tst-pkey does not match the behavior described in the manual page.
> 
> I changed the implementation of pkey_mprotect to use plain mprotect for -1 keys, so that application usage is simplified.
> 
> Thanks,
> Florian
> 
> mpk.patch
> 
> 
> Subject: [PATCH] Linux: Implement interfaces for memory protection keys
> To: libc-alpha@sourceware.org
> 
> This adds system call wrappers for pkey_alloc, pkey_free, pkey_mprotect,
> and x86-64 implementations of pkey_get and pkey_set, which abstract over
> the PKRU CPU register and hide the actual number of memory protection
> keys supported by the CPU.  pkey_mprotect with a -1 key is implemented
> using mprotect, so it will work even if the kernel does not support the
> pkey_mprotect system call.
> 
> The system call wrapers use unsigned int instead of unsigned long for
> parameters, so that no special treatment for x32 is needed.  The flags
> argument is currently unused, and the access rights bit mask is limited
> to two bits by the current PKRU register layout anyway.
> 
> 2017-11-23  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: Likewise.
> 	* 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_64/arch-pkey.h: Likewise.
> 	* sysdeps/unix/sysv/linux/x86_64/pkey_get.c: Likewise.
> 	* sysdeps/unix/sysv/linux/x86_64/pkey_set.c: Likewise.
> 	* sysdeps/unix/sysv/linux/**.abilist: Update.
> 
> diff --git a/NEWS b/NEWS
> index ab14d1eb1b..af7871f4c1 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -37,6 +37,10 @@ Major new features:
>  
>  * glibc now implements the memfd_create function on Linux.
>  
> +* Support for memory protection keys was added.  The <sys/mman.h> header now
> +  declares the functions pkey_alloc, pkey_free, pkey_memprotect, pkey_set,
> +  pkey_get.
> +

Typo here (s/memprotect/mprotect).

>  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 3f5dd90260..618e3960cb 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.

For subprocesses isn't the case that the default key 0 is used instead,
instead of the 'unspecified' one? 

> +
> +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.

I presume you are referring to your 'MPK: pkey_free and key reuse' discussion,
but should we add a note that 'pkey_free' should be as long as the application
make sure to munmap the pages associated with the key meant to free first?

> +
> +@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 supported

Shouldn't it be '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 c484d2688a..255f4e4a94 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
> +		   open_by_handle_at 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
> +	 test-errno-linux tst-memfd_create 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 6f2fe516d7..7d10a96eef 100644
> --- a/sysdeps/unix/sysv/linux/Versions
> +++ b/sysdeps/unix/sysv/linux/Versions
> @@ -168,6 +168,7 @@ libc {
>    }
>    GLIBC_2.27 {
>      memfd_create;
> +    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 140ca28abc..85788be12b 100644
> --- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> @@ -2107,6 +2107,11 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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 strtof128 F
>  GLIBC_2.27 strtof128_l F
> diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
> index f698e1b2f4..3b463dacbe 100644
> --- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
> @@ -2018,6 +2018,11 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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 strtof128 F
>  GLIBC_2.27 strtof128_l F
> diff --git a/sysdeps/unix/sysv/linux/arm/libc.abilist b/sysdeps/unix/sysv/linux/arm/libc.abilist
> index 8a8af3e3e4..a1315aef35 100644
> --- a/sysdeps/unix/sysv/linux/arm/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/arm/libc.abilist
> @@ -108,6 +108,11 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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-linux.h b/sysdeps/unix/sysv/linux/bits/mman-linux.h
> index 1ffa5490af..dc79e5cb0b 100644
> --- a/sysdeps/unix/sysv/linux/bits/mman-linux.h
> +++ b/sysdeps/unix/sysv/linux/bits/mman-linux.h
> @@ -118,12 +118,38 @@
>  # define MFD_ALLOW_SEALING 2U
>  # define MFD_HUGETLB 4U
>  
> +/* Access rights for pkey_alloc.  */
> +# define PKEY_DISABLE_ACCESS 0x1
> +# define PKEY_DISABLE_WRITE 0x2
> +
>  __BEGIN_DECLS
>  
>  /* Create a new memory file descriptor.  NAME is a name for debugging.
>     FLAGS is a combination of the MFD_* constants.  */
>  int memfd_create (const char *__name, 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 5b81a6cd7d..7397d728f2 100644
> --- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
> @@ -1872,6 +1872,11 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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 51ead9e867..cffdf251d6 100644
> --- a/sysdeps/unix/sysv/linux/i386/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
> @@ -2037,6 +2037,11 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
> index 78b4ee8d40..3292510a55 100644
> --- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
> @@ -1901,6 +1901,11 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> index d9c97779e4..636bbdd1a7 100644
> --- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> @@ -109,6 +109,11 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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 4acbf7eeed..6952863f86 100644
> --- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
> @@ -1986,6 +1986,11 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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 93f02f08ce..ac5b56abab 100644
> --- a/sysdeps/unix/sysv/linux/microblaze/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
> @@ -2107,3 +2107,8 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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 795e85de70..bb0958e842 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> @@ -1961,6 +1961,11 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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 dc714057b7..9104eb4d6d 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> @@ -1959,6 +1959,11 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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 ce7bc9b175..58a5d5e141 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> @@ -1957,6 +1957,11 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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 strtof128 F
>  GLIBC_2.27 strtof128_l F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> index 3fdd85eace..2efac14a7d 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> @@ -1952,6 +1952,11 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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 strtof128 F
>  GLIBC_2.27 strtof128_l F
> diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
> index 3e0bcb2a5c..9ef29e4e98 100644
> --- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
> @@ -2148,3 +2148,8 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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..683f070e98
> --- /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 (pkey_mprotect, 4, addr, len, prot, pkey);
> +#else
> +  __set_errno (ENOSYS);
> +  return -1;
> +#endif
> +}

I would suggest to use INLINE_SYSCALL_CALL here to omit the argument number.

> 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 375c69d9d1..60c024096f 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> @@ -1990,6 +1990,11 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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 a88172a906..327933c973 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> @@ -1995,6 +1995,11 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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 fa026a332c..b04c31bc10 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
> @@ -2202,3 +2202,8 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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/powerpc/powerpc64/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
> index 838f395d78..e0645e9e25 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
> @@ -109,6 +109,11 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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 41b79c496a..ef434c61a7 100644
> --- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> @@ -1990,6 +1990,11 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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 strtof128 F
>  GLIBC_2.27 strtof128_l F
> diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> index 68251a0e69..4114a4ce57 100644
> --- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> @@ -1891,6 +1891,11 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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 strtof128 F
>  GLIBC_2.27 strtof128_l F
> diff --git a/sysdeps/unix/sysv/linux/sh/libc.abilist b/sysdeps/unix/sysv/linux/sh/libc.abilist
> index bc1aae275e..f4478b0cc5 100644
> --- a/sysdeps/unix/sysv/linux/sh/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sh/libc.abilist
> @@ -1876,6 +1876,11 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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 93e6d092ac..136a57fc0e 100644
> --- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> @@ -1983,6 +1983,11 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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 strtof128 F
>  GLIBC_2.27 strtof128_l F
> diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> index b11d6764d4..9ad0790829 100644
> --- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> @@ -1920,6 +1920,11 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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 strtof128 F
>  GLIBC_2.27 strtof128_l 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 e9eb4ff7bd..d4f2094027 100644
> --- a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
> @@ -2114,3 +2114,8 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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 8f08e909cd..4916dbabb5 100644
> --- a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
> @@ -2114,3 +2114,8 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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 e9eb4ff7bd..d4f2094027 100644
> --- a/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
> @@ -2114,3 +2114,8 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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..5a9ef175ac
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/tst-pkey.c
> @@ -0,0 +1,405 @@
> +/* 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)
> +        {
> +          puts ("warning: kernel does not support memory protection keys");
> +          return EXIT_UNSUPPORTED;
> +        }

We have FAIL_UNSUPPORTED for these cases.


> +      /* The manual page says that ENOSPC is used to report lack of
> +         CPU support, but the kernel returns EINVAL.  */
> +      if (errno == ENOSPC || errno == EINVAL)
> +        {
> +          printf ("warning: CPU does not support memory protection keys: %m");
> +          return EXIT_UNSUPPORTED;
> +        }
> +      FAIL_EXIT1 ("pkey_alloc: %m");

I do not think we should comment the manpages description because it could
changed.

> +    }
> +  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_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> index 0a4f7797ac..1ea74f9e8c 100644
> --- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> @@ -1878,6 +1878,11 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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/x86_64/arch-pkey.h b/sysdeps/unix/sysv/linux/x86_64/arch-pkey.h
> new file mode 100644
> index 0000000000..8e9bfdae96
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/x86_64/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_64/pkey_get.c b/sysdeps/unix/sysv/linux/x86_64/pkey_get.c
> new file mode 100644
> index 0000000000..3a9bfbe676
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/x86_64/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_64/pkey_set.c b/sysdeps/unix/sysv/linux/x86_64/pkey_set.c
> new file mode 100644
> index 0000000000..91dffd22c3
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/x86_64/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/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> index 23f6a91429..1d3d598618 100644
> --- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> @@ -2121,3 +2121,8 @@ GLIBC_2.27 GLIBC_2.27 A
>  GLIBC_2.27 glob F
>  GLIBC_2.27 glob64 F
>  GLIBC_2.27 memfd_create 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
>
Florian Weimer Dec. 1, 2017, 4:30 p.m. UTC | #13
On 11/28/2017 08:01 PM, Adhemerval Zanella wrote:

>>> My understanding from documentation it key 0 is default one and it
>>> is assigned to any memory regions (through mmap) which has not been
>>> explicitly assigned through pkey_mprotect. My questioning whether we
>>> should filter out key 0 is to:
>>>
>>>     1. Issue an error when application tries to use a key which has
>>>        not been previously allocated by pkey_alloc (since key 0 is
>>>        pre-allocated by the kernel).
>>>
>>>     2. Avoid changing default masking bits for pages which has not
>>>        been explicitly assigned through a pkey_mprotect.
>>>
>>> However this does not prevent uses from issuing a RDPKRU/WRPKRU
>>> directly.
>>
>> There is no way to probe allocated keys, so I don't think we can implement more extensive checking without further kernel interfaces.
> 
> That was my understanding as well, I was just checking if I missed something
> reading pkey documentation (from both architecture and kernel level). I
> noticed there is some idea of extending the sysfs to provide more information
> about the system supported keys [1], but even through it also does not
> really provide the information of the allocated keys.
> 
> [1] https://marc.info/?l=linux-kernel&m=150995950219669&w=2

I've since learned that the PROT_EXEC key is dynamically allocated, too. 
  It's not always 1.  So this means that we can't consistently reject 
keys used by the kernel.

>> +* Support for memory protection keys was added.  The <sys/mman.h> header now
>> +  declares the functions pkey_alloc, pkey_free, pkey_memprotect, pkey_set,
>> +  pkey_get.
>> +
> > Typo here (s/memprotect/mprotect).

Thanks, fixed.

>> +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.
> 
> For subprocesses isn't the case that the default key 0 is used instead,
> instead of the 'unspecified' one?

Only after execve, I hope.  It would be weird if PROT_EXEC memory turned 
readable in the subprocess after fork.  It would also totally break my 
idea to use this for write-protecting the GOT most of the time. 8-P

I just verified this on actual hardware.  Should I add another test for 
that, maybe tst-pkey-fork?  Maybe as a separate patch?

>> +@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.
> 
> I presume you are referring to your 'MPK: pkey_free and key reuse' discussion,
> but should we add a note that 'pkey_free' should be as long as the application
> make sure to munmap the pages associated with the key meant to free first?

No, it's also relevant that threads retain access when the key is reused.

>> +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 supported
> 
> Shouldn't it be 'can support'?

Yes, fixed.

>> +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 (pkey_mprotect, 4, addr, len, prot, pkey);
>> +#else
>> +  __set_errno (ENOSYS);
>> +  return -1;
>> +#endif
>> +}
> 
> I would suggest to use INLINE_SYSCALL_CALL here to omit the argument number.

Right, I made the change.


>> +  keys[0] = pkey_alloc (0, 0);
>> +  if (keys[0] < 0)
>> +    {
>> +      if (errno == ENOSYS)
>> +        {
>> +          puts ("warning: kernel does not support memory protection keys");
>> +          return EXIT_UNSUPPORTED;
>> +        }
> 
> We have FAIL_UNSUPPORTED for these cases.

Okay, I switched to that.

>> +      /* The manual page says that ENOSPC is used to report lack of
>> +         CPU support, but the kernel returns EINVAL.  */
>> +      if (errno == ENOSPC || errno == EINVAL)
>> +        {
>> +          printf ("warning: CPU does not support memory protection keys: %m");
>> +          return EXIT_UNSUPPORTED;
>> +        }
>> +      FAIL_EXIT1 ("pkey_alloc: %m");
> 
> I do not think we should comment the manpages description because it could
> changed.

Fair enough, I dropped the commend and the check for ENOSPC.

The attached patch moves the implementation to the x86 directory because 
it turned out the syscalls work on i386 as well (but presumably only 
with a 64-bit kernel).

Thanks,
Florian
Subject: [PATCH] Linux: Implement interfaces for memory protection keys
To: libc-alpha@sourceware.org

This adds system call wrappers for pkey_alloc, pkey_free, pkey_mprotect,
and x86-64 implementations of pkey_get and pkey_set, which abstract over
the PKRU CPU register and hide the actual number of memory protection
keys supported by the CPU.  pkey_mprotect with a -1 key is implemented
using mprotect, so it will work even if the kernel does not support the
pkey_mprotect system call.

The system call wrapers use unsigned int instead of unsigned long for
parameters, so that no special treatment for x32 is needed.  The flags
argument is currently unused, and the access rights bit mask is limited
to two bits by the current PKRU register layout anyway.

2017-12-01  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: Likewise.
	* 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.

diff --git a/NEWS b/NEWS
index 48af4acaea..7252a0cad5 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
Florian Weimer Dec. 5, 2017, 1:52 p.m. UTC | #14
On 12/01/2017 05:30 PM, Florian Weimer wrote:
> The attached patch moves the implementation to the x86 directory because 
> it turned out the syscalls work on i386 as well (but presumably only 
> with a 64-bit kernel).

Any further comments on this version?

https://sourceware.org/ml/libc-alpha/2017-12/msg00028.html

Thanks,
Florian
Adhemerval Zanella Netto Dec. 5, 2017, 2:06 p.m. UTC | #15
On 01/12/2017 14:30, Florian Weimer wrote:
> On 11/28/2017 08:01 PM, Adhemerval Zanella wrote:
> 
>>>> My understanding from documentation it key 0 is default one and it
>>>> is assigned to any memory regions (through mmap) which has not been
>>>> explicitly assigned through pkey_mprotect. My questioning whether we
>>>> should filter out key 0 is to:
>>>>
>>>>     1. Issue an error when application tries to use a key which has
>>>>        not been previously allocated by pkey_alloc (since key 0 is
>>>>        pre-allocated by the kernel).
>>>>
>>>>     2. Avoid changing default masking bits for pages which has not
>>>>        been explicitly assigned through a pkey_mprotect.
>>>>
>>>> However this does not prevent uses from issuing a RDPKRU/WRPKRU
>>>> directly.
>>>
>>> There is no way to probe allocated keys, so I don't think we can implement more extensive checking without further kernel interfaces.
>>
>> That was my understanding as well, I was just checking if I missed something
>> reading pkey documentation (from both architecture and kernel level). I
>> noticed there is some idea of extending the sysfs to provide more information
>> about the system supported keys [1], but even through it also does not
>> really provide the information of the allocated keys.
>>
>> [1] https://marc.info/?l=linux-kernel&m=150995950219669&w=2
> 
> I've since learned that the PROT_EXEC key is dynamically allocated, too.  It's not always 1.  So this means that we can't consistently reject keys used by the kernel.

Right, I noted it also checking on kernel tools/testing/selftests/x86/protection_keys.c
test.

> 
>>> +* Support for memory protection keys was added.  The <sys/mman.h> header now
>>> +  declares the functions pkey_alloc, pkey_free, pkey_memprotect, pkey_set,
>>> +  pkey_get.
>>> +
>> > Typo here (s/memprotect/mprotect).
> 
> Thanks, fixed.
> 
>>> +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.
>>
>> For subprocesses isn't the case that the default key 0 is used instead,
>> instead of the 'unspecified' one?
> 
> Only after execve, I hope.  It would be weird if PROT_EXEC memory turned readable in the subprocess after fork.  It would also totally break my idea to use this for write-protecting the GOT most of the time. 8-P
> 
> I just verified this on actual hardware.  Should I add another test for that, maybe tst-pkey-fork?  Maybe as a separate patch?

I think it could be a follow up patch.

> 
>>> +@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.
>>
>> I presume you are referring to your 'MPK: pkey_free and key reuse' discussion,
>> but should we add a note that 'pkey_free' should be as long as the application
>> make sure to munmap the pages associated with the key meant to free first?
> 
> No, it's also relevant that threads retain access when the key is reused.

My understanding is if we pkey_alloc -> mprotect -> munmap -> pkey_free,
it should not create any other side effects even for key reuse.  Or am 
I missing something here?

> 
>>> +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 supported
>>
>> Shouldn't it be 'can support'?
> 
> Yes, fixed.
> 
>>> +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 (pkey_mprotect, 4, addr, len, prot, pkey);
>>> +#else
>>> +  __set_errno (ENOSYS);
>>> +  return -1;
>>> +#endif
>>> +}
>>
>> I would suggest to use INLINE_SYSCALL_CALL here to omit the argument number.
> 
> Right, I made the change.
> 
> 
>>> +  keys[0] = pkey_alloc (0, 0);
>>> +  if (keys[0] < 0)
>>> +    {
>>> +      if (errno == ENOSYS)
>>> +        {
>>> +          puts ("warning: kernel does not support memory protection keys");
>>> +          return EXIT_UNSUPPORTED;
>>> +        }
>>
>> We have FAIL_UNSUPPORTED for these cases.
> 
> Okay, I switched to that.
> 
>>> +      /* The manual page says that ENOSPC is used to report lack of
>>> +         CPU support, but the kernel returns EINVAL.  */
>>> +      if (errno == ENOSPC || errno == EINVAL)
>>> +        {
>>> +          printf ("warning: CPU does not support memory protection keys: %m");
>>> +          return EXIT_UNSUPPORTED;
>>> +        }
>>> +      FAIL_EXIT1 ("pkey_alloc: %m");
>>
>> I do not think we should comment the manpages description because it could
>> changed.
> 
> Fair enough, I dropped the commend and the check for ENOSPC.
> 
> The attached patch moves the implementation to the x86 directory because it turned out the syscalls work on i386 as well (but presumably only with a 64-bit kernel).

LGTM and we may extend the pkey_free documentation later if it were the case.
As a side note what are you ideas regarding using pkey on glibc (and could you
extend the idea of the GOT protection as well)?

Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Florian Weimer Dec. 5, 2017, 2:18 p.m. UTC | #16
On 12/05/2017 03:06 PM, Adhemerval Zanella wrote:

>>>> +@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.
>>>
>>> I presume you are referring to your 'MPK: pkey_free and key reuse' discussion,
>>> but should we add a note that 'pkey_free' should be as long as the application
>>> make sure to munmap the pages associated with the key meant to free first?
>>
>> No, it's also relevant that threads retain access when the key is reused.
> 
> My understanding is if we pkey_alloc -> mprotect -> munmap -> pkey_free,
> it should not create any other side effects even for key reuse.  Or am
> I missing something here?

If there's a pthread_create call between pkey_alloc and pkey_free, and 
the thread is still running after the pkey_free, then that thread will 
retain the access rights of the original thread at the time 
pthread_create was called.

> LGTM and we may extend the pkey_free documentation later if it were the case.

Thanks.

> As a side note what are you ideas regarding using pkey on glibc (and could you
> extend the idea of the GOT protection as well)?

Nick Clifton wrote a binutils patch for me which isolates the GOT on a 
dedicated page and adds a new DT_* entry with the GOT size.  We can then 
allocate a protection key and apply it to all GOTs in the process. 
Default access rights for all threads is PKEY_DISABLE_WRITE.  _dl_fixup 
will temporarily disable that to retain write access to the GOT before 
storing the pointer.

This will need a kernel patch to allow us to specify that we want 
PKEY_DISABLE_WRITE for the key in signal handlers, not 
PKEY_DISABLE_ACCESS (as it is the case for some configurations). 
Without that, lazy binding from signal handlers will be broken.  We can 
implement this with a pkey_alloc flag, so the existing APIs are fine.

Florian
diff mbox series

Patch

diff --git a/support/Makefile b/support/Makefile
index dafb1737a4..50d4269e24 100644
--- a/support/Makefile
+++ b/support/Makefile
@@ -52,9 +52,10 @@  libsupport-routines = \
   support_record_failure \
   support_run_diff \
   support_shared_allocate \
-  support_write_file_string \
+  support_test_compare_failure \
   support_test_main \
   support_test_verify_impl \
+  support_write_file_string \
   temp_file \
   write_message \
   xaccept \
@@ -84,8 +85,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 \
@@ -116,14 +117,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/check.h b/support/check.h
index bdcd12952a..29b709c2b0 100644
--- a/support/check.h
+++ b/support/check.h
@@ -86,6 +86,35 @@  void support_test_verify_exit_impl (int status, const char *file, int line,
    does not support reporting failures from a DSO.  */
 void support_record_failure (void);
 
+/* Compare the two numbers LEFT and RIGHT and report failure if they
+   are different.  */
+#define TEST_COMPARE(left, right)                                       \
+  ({                                                                    \
+    __typeof__ (left) __left_value = (left);                            \
+    __typeof__ (right) __right_value = (right);                         \
+    _Static_assert (sizeof (__left_value) <= sizeof (long long),        \
+                    "left value fits into long long");                  \
+    _Static_assert (sizeof (__right_value) <= sizeof (long long),       \
+                    "right value fits into long long");                 \
+    if (__left_value != __right_value                                   \
+        || ((__left_value > 0) != (__right_value > 0)))                 \
+      support_test_compare_failure                                      \
+        (__FILE__, __LINE__,                                            \
+         #left, __left_value, __left_value > 0,                         \
+         #right, __right_value, __right_value > 0);                     \
+  })
+
+/* Internal implementation of TEST_COMPARE.  LEFT_POSITIVE and
+   RIGHT_POSITIVE are used to fit both unsigned long long and long
+   long arguments into LEFT_VALUE and RIGHT_VALUE.  */
+void support_test_compare_failure (const char *file, int line,
+                                   const char *left_expr,
+                                   long long left_value,
+                                   int left_positive,
+                                   const char *right_expr,
+                                   long long right_value,
+                                   int right_positive);
+
 /* Internal function called by the test driver.  */
 int support_report_failure (int status)
   __attribute__ ((weak, warn_unused_result));
diff --git a/support/support_test_compare_failure.c b/support/support_test_compare_failure.c
new file mode 100644
index 0000000000..38fec1ca89
--- /dev/null
+++ b/support/support_test_compare_failure.c
@@ -0,0 +1,46 @@ 
+/* Reporting mumeric comparison failure.
+   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 <stdio.h>
+#include <support/check.h>
+
+static void
+report (const char *which, const char *expr, long long value, int positive)
+{
+  printf ("  %s: ", which);
+  if (positive)
+    printf ("%llu", (unsigned long long) value);
+  else
+    printf ("%lld", value);
+  printf (" (0x%llx); from: %s\n", (unsigned long long) value, expr);
+}
+
+void
+support_test_compare_failure (const char *file, int line,
+                              const char *left_expr,
+                              long long left_value,
+                              int left_positive,
+                              const char *right_expr,
+                              long long right_value,
+                              int right_positive)
+{
+  support_record_failure ();
+  printf ("%s:%d: numeric comparison failure\n", file, line);
+  report (" left", left_expr, left_value, left_positive);
+  report ("right", right_expr, right_value, right_positive);
+}
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 53e41510e3..095cf93892 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
+		   open_by_handle_at 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 test-errno-linux tst-sysconf-iov_max \
-	 tst-memfd_create
+	 tst-memfd_create 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 992c19729f..798ffc7660 100644
--- a/sysdeps/unix/sysv/linux/Versions
+++ b/sysdeps/unix/sysv/linux/Versions
@@ -168,6 +168,7 @@  libc {
   }
   GLIBC_2.27 {
     memfd_create;
+    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 140ca28abc..85788be12b 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
@@ -2107,6 +2107,11 @@  GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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 strtof128 F
 GLIBC_2.27 strtof128_l F
diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
index f698e1b2f4..3b463dacbe 100644
--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
@@ -2018,6 +2018,11 @@  GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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 strtof128 F
 GLIBC_2.27 strtof128_l F
diff --git a/sysdeps/unix/sysv/linux/arm/libc.abilist b/sysdeps/unix/sysv/linux/arm/libc.abilist
index 8a8af3e3e4..a1315aef35 100644
--- a/sysdeps/unix/sysv/linux/arm/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/libc.abilist
@@ -108,6 +108,11 @@  GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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-linux.h b/sysdeps/unix/sysv/linux/bits/mman-linux.h
index b091181960..da5ec79334 100644
--- a/sysdeps/unix/sysv/linux/bits/mman-linux.h
+++ b/sysdeps/unix/sysv/linux/bits/mman-linux.h
@@ -109,3 +109,38 @@ 
 # define MCL_ONFAULT	4		/* Lock all pages that are
 					   faulted in.  */
 #endif
+
+/* Memory protection key support.  */
+#ifdef __USE_GNU
+
+/* FLags for pkey_alloc.  */
+# define PKEY_DISABLE_ACCESS 0x1
+# define PKEY_DISABLE_WRITE 0x2
+
+__BEGIN_DECLS
+
+/* 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 5b81a6cd7d..7397d728f2 100644
--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
+++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
@@ -1872,6 +1872,11 @@  GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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 51ead9e867..cffdf251d6 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -2037,6 +2037,11 @@  GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
index 78b4ee8d40..3292510a55 100644
--- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
@@ -1901,6 +1901,11 @@  GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
index d9c97779e4..636bbdd1a7 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
@@ -109,6 +109,11 @@  GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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 4acbf7eeed..6952863f86 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
@@ -1986,6 +1986,11 @@  GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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 93f02f08ce..ac5b56abab 100644
--- a/sysdeps/unix/sysv/linux/microblaze/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
@@ -2107,3 +2107,8 @@  GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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 795e85de70..bb0958e842 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
@@ -1961,6 +1961,11 @@  GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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 dc714057b7..9104eb4d6d 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
@@ -1959,6 +1959,11 @@  GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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 ce7bc9b175..58a5d5e141 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
@@ -1957,6 +1957,11 @@  GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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 strtof128 F
 GLIBC_2.27 strtof128_l F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
index 3fdd85eace..2efac14a7d 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
@@ -1952,6 +1952,11 @@  GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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 strtof128 F
 GLIBC_2.27 strtof128_l F
diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
index 3e0bcb2a5c..9ef29e4e98 100644
--- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
+++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
@@ -2148,3 +2148,8 @@  GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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_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 375c69d9d1..60c024096f 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
@@ -1990,6 +1990,11 @@  GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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 a88172a906..327933c973 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
@@ -1995,6 +1995,11 @@  GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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 fa026a332c..b04c31bc10 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
@@ -2202,3 +2202,8 @@  GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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/powerpc/powerpc64/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
index 838f395d78..e0645e9e25 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
@@ -109,6 +109,11 @@  GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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 41b79c496a..ef434c61a7 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
@@ -1990,6 +1990,11 @@  GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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 strtof128 F
 GLIBC_2.27 strtof128_l F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
index 68251a0e69..4114a4ce57 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
@@ -1891,6 +1891,11 @@  GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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 strtof128 F
 GLIBC_2.27 strtof128_l F
diff --git a/sysdeps/unix/sysv/linux/sh/libc.abilist b/sysdeps/unix/sysv/linux/sh/libc.abilist
index bc1aae275e..f4478b0cc5 100644
--- a/sysdeps/unix/sysv/linux/sh/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/libc.abilist
@@ -1876,6 +1876,11 @@  GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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 93e6d092ac..136a57fc0e 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
@@ -1983,6 +1983,11 @@  GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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 strtof128 F
 GLIBC_2.27 strtof128_l F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
index b11d6764d4..9ad0790829 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
@@ -1920,6 +1920,11 @@  GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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 strtof128 F
 GLIBC_2.27 strtof128_l F
diff --git a/sysdeps/unix/sysv/linux/syscalls.list b/sysdeps/unix/sysv/linux/syscalls.list
index 40c4fbb9ea..6f657eea2e 100644
--- a/sysdeps/unix/sysv/linux/syscalls.list
+++ b/sysdeps/unix/sysv/linux/syscalls.list
@@ -110,3 +110,6 @@  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
+pkey_mprotect	EXTRA	pkey_mprotect	i:aiii  pkey_mprotect
diff --git a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
index e9eb4ff7bd..d4f2094027 100644
--- a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
@@ -2114,3 +2114,8 @@  GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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 8f08e909cd..4916dbabb5 100644
--- a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
@@ -2114,3 +2114,8 @@  GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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 e9eb4ff7bd..d4f2094027 100644
--- a/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
+++ b/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
@@ -2114,3 +2114,8 @@  GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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..3c1f3cfa39
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/tst-pkey.c
@@ -0,0 +1,320 @@ 
+/* 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 *
+initial_thread_func (void *closure)
+{
+  pthread_barrier_wait (&barrier);
+  struct thread_result *result = get_thread_func (NULL);
+
+  /* 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);
+
+  xpthread_barrier_init (&barrier, NULL, 2);
+  pthread_t initial_thread = xpthread_create
+    (NULL, &initial_thread_func, NULL);
+
+  keys[0] = pkey_alloc (0, 0);
+  if (keys[0] < 0)
+    {
+      if (errno == ENOSYS)
+        {
+          puts ("warning: kernel does not support memory protection keys");
+          return EXIT_UNSUPPORTED;
+        }
+      if (errno == ENOSPC)
+        {
+          puts ("warning: CPU does not support memory protection keys");
+          return EXIT_UNSUPPORTED;
+        }
+      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 (initial_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));
+            }
+      }
+
+
+  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);
+
+  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_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
index 0a4f7797ac..1ea74f9e8c 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -1878,6 +1878,11 @@  GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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/x86_64/arch-pkey.h b/sysdeps/unix/sysv/linux/x86_64/arch-pkey.h
new file mode 100644
index 0000000000..8e9bfdae96
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/x86_64/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_64/pkey_get.c b/sysdeps/unix/sysv/linux/x86_64/pkey_get.c
new file mode 100644
index 0000000000..3a9bfbe676
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/x86_64/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_64/pkey_set.c b/sysdeps/unix/sysv/linux/x86_64/pkey_set.c
new file mode 100644
index 0000000000..91dffd22c3
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/x86_64/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/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
index 23f6a91429..1d3d598618 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2121,3 +2121,8 @@  GLIBC_2.27 GLIBC_2.27 A
 GLIBC_2.27 glob F
 GLIBC_2.27 glob64 F
 GLIBC_2.27 memfd_create 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