diff --git a/sysdeps/unix/sysv/linux/aarch64/Makefile b/sysdeps/unix/sysv/linux/aarch64/Makefile
index 40b9a2e5de..1fdad67fae 100644
--- a/sysdeps/unix/sysv/linux/aarch64/Makefile
+++ b/sysdeps/unix/sysv/linux/aarch64/Makefile
@@ -1,5 +1,8 @@
 ifeq ($(subdir),misc)
 sysdep_headers += sys/elf.h
+tests += \
+  tst-aarch64-pkey \
+  # tests
 endif
 
 ifeq ($(subdir),stdlib)
diff --git a/sysdeps/unix/sysv/linux/aarch64/arch-pkey.h b/sysdeps/unix/sysv/linux/aarch64/arch-pkey.h
new file mode 100644
index 0000000000..a507bf3608
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/aarch64/arch-pkey.h
@@ -0,0 +1,51 @@
+/* Helper functions for manipulating memory protection keys.
+   Copyright (C) 2024 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#ifndef _ARCH_PKEY_H
+#define _ARCH_PKEY_H
+
+#define S1POE_PERM_NO_ACCESS    0b0000UL
+#define S1POE_PERM_R            0b0001UL
+#define S1POE_PERM_X            0b0010UL
+#define S1POE_PERM_RX           0b0011UL
+#define S1POE_PERM_W            0b0100UL
+#define S1POE_PERM_RW           0b0101UL
+#define S1POE_PERM_WX           0b0110UL
+#define S1POE_PERM_RWX          0b0111UL
+
+#define S1POE_PERM_MASK         0b1111UL
+
+#define S1POE_BITS_PER_POI  4UL
+
+/* Return the value of the POR_EL0 register.  */
+static inline unsigned long
+pkey_read (void)
+{
+  unsigned long r;
+  __asm__ volatile ("mrs %0, s3_3_c10_c2_4" : "=r" (r));
+  return r;
+}
+
+/* Overwrite the POR_EL0 register with VALUE.  */
+static inline void
+pkey_write (unsigned long value)
+{
+  __asm__ volatile ("msr s3_3_c10_c2_4, %0" : : "r" (value));
+}
+
+#endif /* _ARCH_PKEY_H */
diff --git a/sysdeps/unix/sysv/linux/aarch64/bits/hwcap.h b/sysdeps/unix/sysv/linux/aarch64/bits/hwcap.h
index 8dceaa1a52..2fa158fcc0 100644
--- a/sysdeps/unix/sysv/linux/aarch64/bits/hwcap.h
+++ b/sysdeps/unix/sysv/linux/aarch64/bits/hwcap.h
@@ -118,3 +118,4 @@
 #define HWCAP2_SME_SF8FMA	(1UL << 60)
 #define HWCAP2_SME_SF8DP4	(1UL << 61)
 #define HWCAP2_SME_SF8DP2	(1UL << 62)
+#define HWCAP2_POE		(1UL << 63)
diff --git a/sysdeps/unix/sysv/linux/aarch64/bits/mman.h b/sysdeps/unix/sysv/linux/aarch64/bits/mman.h
index c5b6c69d43..288dff064b 100644
--- a/sysdeps/unix/sysv/linux/aarch64/bits/mman.h
+++ b/sysdeps/unix/sysv/linux/aarch64/bits/mman.h
@@ -26,6 +26,14 @@
 #define PROT_BTI	0x10
 #define PROT_MTE	0x20
 
+#ifdef __USE_GNU
+# define PKEY_UNRESTRICTED 0x0
+# define PKEY_DISABLE_ACCESS 0x1
+# define PKEY_DISABLE_WRITE 0x2
+# define PKEY_DISABLE_EXECUTE 0x4
+# define PKEY_DISABLE_READ 0x8
+#endif
+
 #include <bits/mman-map-flags-generic.h>
 
 /* Include generic Linux declarations.  */
diff --git a/sysdeps/unix/sysv/linux/aarch64/pkey_get.c b/sysdeps/unix/sysv/linux/aarch64/pkey_get.c
new file mode 100644
index 0000000000..5290ac2e3c
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/aarch64/pkey_get.c
@@ -0,0 +1,71 @@
+/* Reading the per-thread memory protection key, AArch64 version.
+   Copyright (C) 2024 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <arch-pkey.h>
+#include <errno.h>
+#include <sys/mman.h>
+
+int
+pkey_get (int key)
+{
+  if (key < 0 || key > 15)
+    {
+      __set_errno (EINVAL);
+      return -1;
+    }
+  unsigned long por_el0 = pkey_read ();
+  unsigned long perm = (por_el0 >> (S1POE_BITS_PER_POI * key)) & S1POE_PERM_MASK;
+
+  /* The following mapping between POR permission bits (4 bits)
+     and PKEY flags is supported:
+
+      -WXR    POR to PKEY_ mapping
+      0000 => DISABLE_ACCESS | DISABLE_READ | DISABLE_WRITE | DISABLE_EXECUTE
+      0001 => DISABLE_WRITE | DISABLE_EXECUTE (read-only)
+      0010 => DISABLE_ACCESS | DISABLE_READ | DISABLE_WRITE (execute-only)
+      0011 => DISABLE_WRITE (read-execute)
+      0100 => DISABLE_READ | DISABLE_EXECUTE (write-only)
+      0101 => DISABLE_EXECUTE (read-write)
+      0110 => DISABLE_READ (execute-write)
+      0111 => UNRESTRICTED (no restrictions, read-write-execute)
+      else => undefined behavior
+
+     Note that pkey_set and pkey_alloc would only set these specific
+     values. The PKEY_DISABLE_ACCESS flag is redundat as it implies
+     PKEY_DISABLE_READ | PKEY_DISABLE_WRITE but is kept for backward
+     compatibility.  */
+
+  if (perm == S1POE_PERM_NO_ACCESS)
+    return PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE | PKEY_DISABLE_EXECUTE | PKEY_DISABLE_READ;
+  if (perm == S1POE_PERM_R)
+    return PKEY_DISABLE_WRITE | PKEY_DISABLE_EXECUTE;
+  if (perm == S1POE_PERM_X)
+    return PKEY_DISABLE_ACCESS | PKEY_DISABLE_READ | PKEY_DISABLE_WRITE;
+  if (perm == S1POE_PERM_RX)
+    return PKEY_DISABLE_WRITE;
+  if (perm == S1POE_PERM_W)
+    return PKEY_DISABLE_READ | PKEY_DISABLE_EXECUTE;
+  if (perm == S1POE_PERM_RW)
+    return PKEY_DISABLE_EXECUTE;
+  if (perm == S1POE_PERM_WX)
+    return PKEY_DISABLE_READ;
+  if (perm == S1POE_PERM_RWX)
+    return PKEY_UNRESTRICTED;
+
+  return PKEY_DISABLE_ACCESS; // undefined behavior
+}
diff --git a/sysdeps/unix/sysv/linux/aarch64/pkey_set.c b/sysdeps/unix/sysv/linux/aarch64/pkey_set.c
new file mode 100644
index 0000000000..a4057fbe13
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/aarch64/pkey_set.c
@@ -0,0 +1,108 @@
+/* Changing the per-thread memory protection key, AArch64 version.
+   Copyright (C) 2024 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <arch-pkey.h>
+#include <errno.h>
+#include <sys/mman.h>
+
+#define MAX_PKEY_RIGHTS (PKEY_DISABLE_ACCESS | \
+  PKEY_DISABLE_WRITE | PKEY_DISABLE_EXECUTE | PKEY_DISABLE_READ)
+
+int
+pkey_set (int key, unsigned int rights)
+{
+  if (key < 0 || key > 15 || rights > MAX_PKEY_RIGHTS)
+    {
+      __set_errno (EINVAL);
+      return -1;
+    }
+  unsigned long mask = S1POE_PERM_MASK << (S1POE_BITS_PER_POI * key);
+  unsigned long por_el0 = pkey_read ();
+  unsigned long perm;
+
+  /* POR ot PKEY mapping:     -WXR
+     PKEY_UNRESTRICTED    =>  0111 (read-write-execute)
+     PKEY_DISABLE_ACCESS  =>  removes R and W access
+     PKEY_DISABLE_READ    =>  removes R access
+     PKEY_DISABLE_WRITE   =>  removes W access
+     PKEY_DISABLE_EXECUTE =>  removes X access
+
+     Either of PKEY_DISABLE_ACCESS or PKEY_DISABLE_READ removes R access.
+     Either of PKEY_DISABLE_ACCESS or PKEY_DISABLE_WRITE removes W access.
+     Using PKEY_DISABLE_ACCESS along with only one of PKEY_DISABLE_READ or
+     PKEY_DISABLE_WRITE is considered to be in error.
+
+    Furthermore, for avoidance of doubt:
+
+    PKEY flags  Permissions
+    rxwa        -WXR
+    1111  =>    0000 S1POE_PERM_NO_ACCESS
+    1110  =>    0000 S1POE_PERM_NO_ACCESS
+    1101  =>    EINVAL
+    1100  =>    0100  S1POE_PERM_W
+    1011  =>    0010  S1POE_PERM_X
+    1010  =>    0010  S1POE_PERM_X
+    1001  =>    EINVAL
+    1000  =>    0110  S1POE_PERM_WX
+    0111  =>    EINVAL
+    0110  =>    0001  S1POE_PERM_R
+    0101  =>    0000  S1POE_PERM_NO_ACCESS
+    0100  =>    0101  S1POE_PERM_RW
+    0011  =>    EINVAL
+    0010  =>    0011  S1POE_PERM_RX
+    0001  =>    0010  S1POE_PERM_X
+    0000  =>    0111  S1POE_PERM_RWX  */
+  switch (rights)
+    {
+      case PKEY_DISABLE_ACCESS | PKEY_DISABLE_READ | PKEY_DISABLE_WRITE | PKEY_DISABLE_EXECUTE:
+      case PKEY_DISABLE_ACCESS | PKEY_DISABLE_EXECUTE:
+      case PKEY_DISABLE_READ | PKEY_DISABLE_WRITE | PKEY_DISABLE_EXECUTE:
+        perm = S1POE_PERM_NO_ACCESS;
+        break;
+      case PKEY_DISABLE_READ | PKEY_DISABLE_EXECUTE:
+        perm = S1POE_PERM_W;
+        break;
+      case PKEY_DISABLE_ACCESS | PKEY_DISABLE_READ | PKEY_DISABLE_WRITE:
+      case PKEY_DISABLE_READ | PKEY_DISABLE_WRITE:
+      case PKEY_DISABLE_ACCESS:
+        perm = S1POE_PERM_X;
+        break;
+      case PKEY_DISABLE_READ:
+        perm = S1POE_PERM_WX;
+        break;
+      case PKEY_DISABLE_WRITE | PKEY_DISABLE_EXECUTE:
+        perm = S1POE_PERM_R;
+        break;
+      case PKEY_DISABLE_EXECUTE:
+        perm = S1POE_PERM_RW;
+        break;
+      case PKEY_DISABLE_WRITE:
+        perm = S1POE_PERM_RX;
+        break;
+      case PKEY_UNRESTRICTED:
+        perm = S1POE_PERM_RWX;
+        break;
+      default:
+        __set_errno (EINVAL);
+        return -1;
+    }
+
+  por_el0 = (por_el0 & ~mask) | (perm << (S1POE_BITS_PER_POI * key));
+  pkey_write (por_el0);
+  return 0;
+}
diff --git a/sysdeps/unix/sysv/linux/aarch64/tst-aarch64-pkey.c b/sysdeps/unix/sysv/linux/aarch64/tst-aarch64-pkey.c
new file mode 100644
index 0000000000..1623ccffd0
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/aarch64/tst-aarch64-pkey.c
@@ -0,0 +1,127 @@
+/* AArch64 tests for memory protection keys.
+   Copyright (C) 2024 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <support/test-driver.h>
+#include <support/xsignal.h>
+#include <support/xunistd.h>
+#include <sys/mman.h>
+
+static volatile sig_atomic_t sigusr1_handler_ran;
+static volatile int pkey;
+
+/* On AArch64 access is revoked during signal handling for
+  pkey > 0 because POR is reset to the default value 0x7.  */
+static void
+sigusr1_handler (int signum)
+{
+  TEST_COMPARE (signum, SIGUSR1);
+  TEST_COMPARE (pkey_get (pkey) & PKEY_DISABLE_ACCESS, PKEY_DISABLE_ACCESS);
+  TEST_COMPARE (pkey_get (pkey) & PKEY_DISABLE_READ, PKEY_DISABLE_READ);
+  TEST_COMPARE (pkey_get (pkey) & PKEY_DISABLE_WRITE, PKEY_DISABLE_WRITE);
+  TEST_COMPARE (pkey_get (pkey) & PKEY_DISABLE_EXECUTE, PKEY_DISABLE_EXECUTE);
+  sigusr1_handler_ran += 1;
+}
+
+static int
+do_test (void)
+{
+
+  pkey = pkey_alloc (0, PKEY_UNRESTRICTED);
+  if (pkey < 0)
+    {
+      if (errno == ENOSYS || errno == EINVAL)
+        FAIL_UNSUPPORTED
+          ("kernel or CPU does not support memory protection keys");
+      FAIL_EXIT1 ("pkey_alloc: %m");
+    }
+
+  long pagesize = xsysconf (_SC_PAGESIZE);
+
+  int *page = xmmap (NULL, pagesize, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1);
+  pkey = pkey_alloc (0, PKEY_UNRESTRICTED);
+
+  /* On AArch64 pkey == 0 is reserved and should never be accocated.  */
+  TEST_VERIFY (pkey > 0);
+  TEST_COMPARE (pkey_get(pkey), PKEY_UNRESTRICTED);
+
+  /* Check that access is revoked during signal handling
+     with initial rights being set to no restrictions.  */
+  TEST_COMPARE (pkey_mprotect ((void *) page, pagesize, PROT_READ | PROT_WRITE, pkey), 0);
+  xsignal (SIGUSR1, &sigusr1_handler);
+  xraise (SIGUSR1);
+  xsignal (SIGUSR1, SIG_DFL);
+  TEST_COMPARE (sigusr1_handler_ran, 1);
+
+  /* Check that access is revoked during signal handling
+     with initial rights being set to PKEY_DISABLE_WRITE.  */
+  TEST_COMPARE (pkey_set (pkey, PKEY_DISABLE_WRITE), 0);
+  xsignal (SIGUSR1, &sigusr1_handler);
+  xraise (SIGUSR1);
+  xsignal (SIGUSR1, SIG_DFL);
+  TEST_COMPARE (sigusr1_handler_ran, 2);
+
+  /* Check that all combinations of PKEY flags used in pkey_set
+     result in consistent values obtained via pkey_get.
+     Note that whenever flags PKEY_DISABLE_READ and PKEY_DISABLE_WRITE
+     are set, the PKEY_DISABLE_ACCESS is also set.  */
+  TEST_COMPARE (pkey_set(pkey, PKEY_UNRESTRICTED), 0);
+  TEST_COMPARE (pkey_get(pkey), PKEY_UNRESTRICTED);
+  TEST_COMPARE (pkey_set(pkey, PKEY_DISABLE_ACCESS), 0);
+  TEST_COMPARE (pkey_get(pkey), PKEY_DISABLE_ACCESS | PKEY_DISABLE_READ | PKEY_DISABLE_WRITE);
+  TEST_COMPARE (pkey_set(pkey, PKEY_DISABLE_READ | PKEY_DISABLE_WRITE), 0);
+  TEST_COMPARE (pkey_get(pkey), PKEY_DISABLE_ACCESS | PKEY_DISABLE_READ | PKEY_DISABLE_WRITE);
+  TEST_COMPARE (pkey_set(pkey, PKEY_DISABLE_READ), 0);
+  TEST_COMPARE (pkey_get(pkey), PKEY_DISABLE_READ);
+  TEST_COMPARE (pkey_set(pkey, PKEY_DISABLE_WRITE), 0);
+  TEST_COMPARE (pkey_get(pkey), PKEY_DISABLE_WRITE);
+  TEST_COMPARE (pkey_set(pkey, PKEY_DISABLE_EXECUTE), 0);
+  TEST_COMPARE (pkey_get(pkey), PKEY_DISABLE_EXECUTE);
+  TEST_COMPARE (pkey_set(pkey, PKEY_DISABLE_WRITE | PKEY_DISABLE_EXECUTE), 0);
+  TEST_COMPARE (pkey_get(pkey), PKEY_DISABLE_WRITE | PKEY_DISABLE_EXECUTE);
+  TEST_COMPARE (pkey_set(pkey, PKEY_DISABLE_READ | PKEY_DISABLE_EXECUTE), 0);
+  TEST_COMPARE (pkey_get(pkey), PKEY_DISABLE_READ | PKEY_DISABLE_EXECUTE);
+
+  /* Check EINVAL cases for pkey_set: use of
+      - PKEY_DISABLE_ACCESS with PKEY_DISABLE_READ only
+      - PKEY_DISABLE_ACCESS with PKEY_DISABLE_WRITE only
+     is likely an error.  */
+  TEST_COMPARE (pkey_set(pkey, PKEY_DISABLE_ACCESS | PKEY_DISABLE_READ), -1);
+  TEST_COMPARE (errno, EINVAL);
+  TEST_COMPARE (pkey_set(pkey, PKEY_DISABLE_ACCESS | PKEY_DISABLE_READ | PKEY_DISABLE_EXECUTE), -1);
+  TEST_COMPARE (errno, EINVAL);
+  TEST_COMPARE (pkey_set(pkey, PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE), -1);
+  TEST_COMPARE (errno, EINVAL);
+  TEST_COMPARE (pkey_set(pkey, PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE | PKEY_DISABLE_EXECUTE), -1);
+  TEST_COMPARE (errno, EINVAL);
+
+  TEST_COMPARE (pkey_free(pkey), 0);
+
+  xmunmap ((void *) page, pagesize);
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/sysdeps/unix/sysv/linux/tst-pkey.c b/sysdeps/unix/sysv/linux/tst-pkey.c
index d9083daab9..72f8ccfa00 100644
--- a/sysdeps/unix/sysv/linux/tst-pkey.c
+++ b/sysdeps/unix/sysv/linux/tst-pkey.c
@@ -120,7 +120,7 @@ sigusr1_handler (int signum)
   TEST_COMPARE (signum, SIGUSR1);
   for (int i = 0; i < key_count; ++i)
     TEST_VERIFY (pkey_get (keys[i]) == PKEY_DISABLE_ACCESS
-                 || pkey_get (keys[i]) == i);
+                 || (pkey_get (keys[i]) & i) == i);
   sigusr1_handler_ran = 1;
 }
 
@@ -185,6 +185,7 @@ do_test (void)
     xmunmap (page, pagesize);
   }
 
+  /* Create thread before setting up key in the current thread.  */
   xpthread_barrier_init (&barrier, NULL, 2);
   bool delayed_thread_check_access = true;
   pthread_t delayed_thread = xpthread_create
@@ -206,19 +207,47 @@ do_test (void)
       FAIL_EXIT1 ("pkey_alloc: %m");
     }
   TEST_COMPARE (pkey_get (keys[0]), 0);
+
+  /* Check that initial rights that are set via pkey_alloc
+     can be accessed via pkey_get.  */
+  {
+    int pkey = -1;
+    pkey = pkey_alloc (0, PKEY_DISABLE_ACCESS);
+    TEST_COMPARE (pkey_get (pkey) & PKEY_DISABLE_ACCESS, PKEY_DISABLE_ACCESS);
+    pkey_free (pkey);
+    pkey = pkey_alloc (0, PKEY_DISABLE_WRITE);
+    TEST_COMPARE (pkey_get (pkey) & PKEY_DISABLE_WRITE, PKEY_DISABLE_WRITE);
+    pkey_free (pkey);
+  }
+
+  /* Check that unallocated pkey is not accepted by the
+     pkey_mprotect function.  */
+  {
+    int pkey = -1;
+    pkey = pkey_alloc (0, PKEY_DISABLE_WRITE);
+    pkey_free (pkey);
+    int *page = xmmap (NULL, pagesize, PROT_NONE,
+                       MAP_ANONYMOUS | MAP_PRIVATE, -1);
+    TEST_COMPARE (pkey_mprotect (page, pagesize, PROT_READ, pkey), -1);
+    TEST_COMPARE (errno, EINVAL);
+    xmunmap (page, pagesize);
+  }
+
   for (int i = 1; i < key_count; ++i)
     {
+      /* i == 1 corresponds to PKEY_DISABLE_ACCESS
+         i == 2 corresponds to PKEY_DISABLE_WRITE  */
       keys[i] = pkey_alloc (0, i);
       if (keys[i] < 0)
         FAIL_EXIT1 ("pkey_alloc (0, %d): %m", i);
       /* pkey_alloc is supposed to change the current thread's access
          rights for the new key.  */
-      TEST_COMPARE (pkey_get (keys[i]), i);
+      TEST_COMPARE (pkey_get (keys[i]) & i, i);
     }
   /* Check that all the keys have the expected access rights for the
      current thread.  */
   for (int i = 0; i < key_count; ++i)
-    TEST_COMPARE (pkey_get (keys[i]), i);
+    TEST_COMPARE (pkey_get (keys[i]) & i, i);
 
   /* Allocate a test page for each key.  */
   for (int i = 0; i < key_count; ++i)
@@ -235,12 +264,12 @@ do_test (void)
     pthread_barrier_wait (&barrier);
     struct thread_result *result = xpthread_join (delayed_thread);
     for (int i = 0; i < key_count; ++i)
-      TEST_COMPARE (result->access_rights[i],
-                    PKEY_DISABLE_ACCESS);
+      TEST_COMPARE (result->access_rights[i] &
+                    PKEY_DISABLE_ACCESS, PKEY_DISABLE_ACCESS);
     struct thread_result *result2 = xpthread_join (result->next_thread);
     for (int i = 0; i < key_count; ++i)
-      TEST_COMPARE (result->access_rights[i],
-                    PKEY_DISABLE_ACCESS);
+      TEST_COMPARE (result->access_rights[i] &
+                    PKEY_DISABLE_ACCESS, PKEY_DISABLE_ACCESS);
     free (result);
     free (result2);
   }
@@ -251,12 +280,12 @@ do_test (void)
     pthread_t get_thread = xpthread_create (NULL, get_thread_func, NULL);
     struct thread_result *result = xpthread_join (get_thread);
     for (int i = 0; i < key_count; ++i)
-      TEST_COMPARE (result->access_rights[i], i);
+      TEST_COMPARE (result->access_rights[i] & i, i);
     free (result);
   }
 
   for (int i = 0; i < key_count; ++i)
-    TEST_COMPARE (pkey_get (keys[i]), i);
+    TEST_COMPARE (pkey_get (keys[i]) & i, i);
 
   /* Check that in a signal handler, there is no access.  */
   xsignal (SIGUSR1, &sigusr1_handler);
@@ -275,7 +304,7 @@ do_test (void)
         printf ("info: checking access for key %d, bits 0x%x\n",
                 i, pkey_get (keys[i]));
       for (int j = 0; j < key_count; ++j)
-        TEST_COMPARE (pkey_get (keys[j]), j);
+        TEST_COMPARE (pkey_get (keys[j]) & j, j);
       if (i & PKEY_DISABLE_ACCESS)
         {
           TEST_VERIFY (!check_page_access (i, false));
@@ -349,7 +378,7 @@ do_test (void)
      not what happens in practice.  */
   {
     /* The limit is in place to avoid running indefinitely in case
-       there many keys available.  */
+       there are many keys available.  */
     int *keys_array = xcalloc (100000, sizeof (*keys_array));
     int keys_allocated = 0;
     while (keys_allocated < 100000)
