AArch64: Add support for memory protection keys

This patch adds support for memory protection keys on AArch64 systems with
enabled Stage 1 permission overlays feature introduced in Armv8.9 / 9.4
(FEAT_S1POE) [1].

 1. Internal functions "pkey_read" and "pkey_write" to access data
    associated with memory protection keys.
 2. Implementation of API functions "pkey_get" and "pkey_set" for
    the AArch64 target.
 3. AArch64-specific PKEY flags for READ and EXECUTE (see below).
 4. New target-specific test that checks behaviour of pkeys on
    AArch64 targets.
 5. This patch also extends existing generic test for pkeys.
 6. HWCAP constant for Permission Overlay Extension feature.

To support more accurate mapping of underlying permissions to the
PKEY flags, we introduce additional AArch64-specific flags. The full
list of flags is:

 - PKEY_UNRESTRICTED: 0x0 (for completeness)
 - PKEY_DISABLE_ACCESS: 0x1 (existing flag)
 - PKEY_DISABLE_WRITE: 0x2 (existing flag)
 - PKEY_DISABLE_EXECUTE: 0x4 (new flag, AArch64 specific)
 - PKEY_DISABLE_READ: 0x8 (new flag, AArch64 specific)

The problem here is that PKEY_DISABLE_ACCESS has unusual semantics as
it overlaps with existing PKEY_DISABLE_WRITE and new PKEY_DISABLE_READ.
For this reason mapping between permission bits RWX and "restrictions"
bits awxr (a for disable access, etc) becomes complicated:

 - PKEY_DISABLE_ACCESS disables both R and W
 - PKEY_DISABLE_{WRITE,READ} disables W and R respectively
 - PKEY_DISABLE_EXECUTE disables X

Combinations like the one below are accepted although they are redundant:

 - PKEY_DISABLE_ACCESS | PKEY_DISABLE_READ | PKEY_DISABLE_WRITE

Reverse mapping tries to retain backward compatibility and ORs
PKEY_DISABLE_ACCESS whenever both flags PKEY_DISABLE_READ and
PKEY_DISABLE_WRITE would be present.

This will break code that compares pkey_get output with == instead
of using bitwise operations. The latter is more correct since PKEY_*
constants are essentially bit flags.

It should be noted that PKEY_DISABLE_ACCESS does not prevent execution.

[1] https://developer.arm.com/documentation/ddi0487/ka/ section D8.4.1.4

Co-authored-by: Szabolcs Nagy <szabolcs.nagy@arm.com>

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
This commit is contained in:
Yury Khrustalev 2024-11-20 11:16:36 +00:00 committed by Wilco Dijkstra
parent e162ab2bf1
commit f4d00dd60d
8 changed files with 477 additions and 11 deletions

View File

@ -1,5 +1,8 @@
ifeq ($(subdir),misc) ifeq ($(subdir),misc)
sysdep_headers += sys/elf.h sysdep_headers += sys/elf.h
tests += \
tst-aarch64-pkey \
# tests
endif endif
ifeq ($(subdir),stdlib) ifeq ($(subdir),stdlib)

View File

@ -0,0 +1,53 @@
/* Helper functions for manipulating memory protection keys.
Copyright (C) 2024 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#ifndef _ARCH_PKEY_H
#define _ARCH_PKEY_H
#include <sys/cdefs.h>
#define S1POE_PERM_NO_ACCESS 0b0000UL
#define S1POE_PERM_R 0b0001UL
#define S1POE_PERM_X 0b0010UL
#define S1POE_PERM_RX 0b0011UL
#define S1POE_PERM_W 0b0100UL
#define S1POE_PERM_RW 0b0101UL
#define S1POE_PERM_WX 0b0110UL
#define S1POE_PERM_RWX 0b0111UL
#define S1POE_PERM_MASK 0b1111UL
#define S1POE_BITS_PER_POI 4UL
/* Return the value of the POR_EL0 register. */
static __always_inline unsigned long
pkey_read (void)
{
unsigned long r;
__asm__ volatile ("mrs %0, s3_3_c10_c2_4" : "=r" (r));
return r;
}
/* Overwrite the POR_EL0 register with VALUE. */
static __always_inline void
pkey_write (unsigned long value)
{
__asm__ volatile ("msr s3_3_c10_c2_4, %0; isb" : : "r" (value));
}
#endif /* _ARCH_PKEY_H */

View File

@ -118,3 +118,4 @@
#define HWCAP2_SME_SF8FMA (1UL << 60) #define HWCAP2_SME_SF8FMA (1UL << 60)
#define HWCAP2_SME_SF8DP4 (1UL << 61) #define HWCAP2_SME_SF8DP4 (1UL << 61)
#define HWCAP2_SME_SF8DP2 (1UL << 62) #define HWCAP2_SME_SF8DP2 (1UL << 62)
#define HWCAP2_POE (1UL << 63)

View File

@ -26,6 +26,14 @@
#define PROT_BTI 0x10 #define PROT_BTI 0x10
#define PROT_MTE 0x20 #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 <bits/mman-map-flags-generic.h>
/* Include generic Linux declarations. */ /* Include generic Linux declarations. */

View File

@ -0,0 +1,73 @@
/* Reading the per-thread memory protection key, AArch64 version.
Copyright (C) 2024 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <arch-pkey.h>
#include <errno.h>
#include <sys/mman.h>
int
pkey_get (int key)
{
if (key < 0 || key > 15)
{
__set_errno (EINVAL);
return -1;
}
unsigned long int por_el0 = pkey_read ();
unsigned long int perm = (por_el0 >> (S1POE_BITS_PER_POI * key))
& S1POE_PERM_MASK;
/* The following mapping between POR permission bits (4 bits)
and PKEY flags is supported:
-WXR POR to PKEY_ mapping
0000 => DISABLE_ACCESS | DISABLE_READ | DISABLE_WRITE | DISABLE_EXECUTE
0001 => DISABLE_WRITE | DISABLE_EXECUTE (read-only)
0010 => DISABLE_ACCESS | DISABLE_READ | DISABLE_WRITE (execute-only)
0011 => DISABLE_WRITE (read-execute)
0100 => DISABLE_READ | DISABLE_EXECUTE (write-only)
0101 => DISABLE_EXECUTE (read-write)
0110 => DISABLE_READ (execute-write)
0111 => UNRESTRICTED (no restrictions, read-write-execute)
else => undefined behavior
Note that pkey_set and pkey_alloc would only set these specific
values. The PKEY_DISABLE_ACCESS flag is redundant as it implies
PKEY_DISABLE_READ | PKEY_DISABLE_WRITE but is kept for backward
compatibility. */
if (perm == S1POE_PERM_NO_ACCESS)
return PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE | PKEY_DISABLE_EXECUTE
| PKEY_DISABLE_READ;
if (perm == S1POE_PERM_R)
return PKEY_DISABLE_WRITE | PKEY_DISABLE_EXECUTE;
if (perm == S1POE_PERM_X)
return PKEY_DISABLE_ACCESS | PKEY_DISABLE_READ | PKEY_DISABLE_WRITE;
if (perm == S1POE_PERM_RX)
return PKEY_DISABLE_WRITE;
if (perm == S1POE_PERM_W)
return PKEY_DISABLE_READ | PKEY_DISABLE_EXECUTE;
if (perm == S1POE_PERM_RW)
return PKEY_DISABLE_EXECUTE;
if (perm == S1POE_PERM_WX)
return PKEY_DISABLE_READ;
if (perm == S1POE_PERM_RWX)
return PKEY_UNRESTRICTED;
return PKEY_DISABLE_ACCESS;
}

View File

@ -0,0 +1,113 @@
/* Changing the per-thread memory protection key, AArch64 version.
Copyright (C) 2024 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <arch-pkey.h>
#include <errno.h>
#include <sys/mman.h>
#define MAX_PKEY_RIGHTS (PKEY_DISABLE_ACCESS | \
PKEY_DISABLE_WRITE | PKEY_DISABLE_EXECUTE | PKEY_DISABLE_READ)
int
pkey_set (int key, unsigned int restrictions)
{
if (key < 0 || key > 15 || restrictions > MAX_PKEY_RIGHTS)
{
__set_errno (EINVAL);
return -1;
}
unsigned long mask = S1POE_PERM_MASK << (S1POE_BITS_PER_POI * key);
unsigned long por_el0 = pkey_read ();
unsigned long perm;
/* POR ot PKEY mapping: -WXR
PKEY_UNRESTRICTED => 0111 (read-write-execute)
PKEY_DISABLE_ACCESS => removes R and W access
PKEY_DISABLE_READ => removes R access
PKEY_DISABLE_WRITE => removes W access
PKEY_DISABLE_EXECUTE => removes X access
Either of PKEY_DISABLE_ACCESS or PKEY_DISABLE_READ removes R access.
Either of PKEY_DISABLE_ACCESS or PKEY_DISABLE_WRITE removes W access.
Using PKEY_DISABLE_ACCESS along with only one of PKEY_DISABLE_READ or
PKEY_DISABLE_WRITE is considered to be in error.
Furthermore, for avoidance of doubt:
PKEY flags Permissions
rxwa -WXR
1111 => 0000 S1POE_PERM_NO_ACCESS
1110 => 0000 S1POE_PERM_NO_ACCESS
1101 => EINVAL
1100 => 0100 S1POE_PERM_W
1011 => 0010 S1POE_PERM_X
1010 => 0010 S1POE_PERM_X
1001 => EINVAL
1000 => 0110 S1POE_PERM_WX
0111 => EINVAL
0110 => 0001 S1POE_PERM_R
0101 => 0000 S1POE_PERM_NO_ACCESS
0100 => 0101 S1POE_PERM_RW
0011 => EINVAL
0010 => 0011 S1POE_PERM_RX
0001 => 0010 S1POE_PERM_X
0000 => 0111 S1POE_PERM_RWX */
switch (restrictions)
{
case PKEY_DISABLE_ACCESS | PKEY_DISABLE_READ | PKEY_DISABLE_WRITE
| PKEY_DISABLE_EXECUTE:
case PKEY_DISABLE_ACCESS | PKEY_DISABLE_EXECUTE:
case PKEY_DISABLE_ACCESS | PKEY_DISABLE_READ | PKEY_DISABLE_EXECUTE:
case PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE | PKEY_DISABLE_EXECUTE:
case PKEY_DISABLE_READ | PKEY_DISABLE_WRITE | PKEY_DISABLE_EXECUTE:
perm = S1POE_PERM_NO_ACCESS;
break;
case PKEY_DISABLE_READ | PKEY_DISABLE_EXECUTE:
perm = S1POE_PERM_W;
break;
case PKEY_DISABLE_ACCESS | PKEY_DISABLE_READ:
case PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE:
case PKEY_DISABLE_ACCESS | PKEY_DISABLE_READ | PKEY_DISABLE_WRITE:
case PKEY_DISABLE_READ | PKEY_DISABLE_WRITE:
case PKEY_DISABLE_ACCESS:
perm = S1POE_PERM_X;
break;
case PKEY_DISABLE_READ:
perm = S1POE_PERM_WX;
break;
case PKEY_DISABLE_WRITE | PKEY_DISABLE_EXECUTE:
perm = S1POE_PERM_R;
break;
case PKEY_DISABLE_EXECUTE:
perm = S1POE_PERM_RW;
break;
case PKEY_DISABLE_WRITE:
perm = S1POE_PERM_RX;
break;
case PKEY_UNRESTRICTED:
perm = S1POE_PERM_RWX;
break;
default:
__set_errno (EINVAL);
return -1;
}
por_el0 = (por_el0 & ~mask) | (perm << (S1POE_BITS_PER_POI * key));
pkey_write (por_el0);
return 0;
}

View File

@ -0,0 +1,186 @@
/* 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>
#include <array_length.h>
static sig_atomic_t sigusr1_handler_ran;
static int pkey;
/* On AArch64 access is revoked during signal handling for
pkey > 0 because POR is reset to the default value 0x7. */
static void
sigusr1_handler (int signum)
{
TEST_COMPARE (signum, SIGUSR1);
TEST_COMPARE (pkey_get (pkey) & PKEY_DISABLE_ACCESS, PKEY_DISABLE_ACCESS);
TEST_COMPARE (pkey_get (pkey) & PKEY_DISABLE_READ, PKEY_DISABLE_READ);
TEST_COMPARE (pkey_get (pkey) & PKEY_DISABLE_WRITE, PKEY_DISABLE_WRITE);
TEST_COMPARE (pkey_get (pkey) & PKEY_DISABLE_EXECUTE, PKEY_DISABLE_EXECUTE);
sigusr1_handler_ran += 1;
}
static int
do_test (void)
{
pkey = pkey_alloc (0, PKEY_UNRESTRICTED);
if (pkey < 0)
{
if (errno == ENOSYS || errno == EINVAL)
FAIL_UNSUPPORTED
("kernel or CPU does not support memory protection keys");
FAIL_EXIT1 ("pkey_alloc: %m");
}
long int pagesize = xsysconf (_SC_PAGESIZE);
int *page = xmmap (NULL, pagesize, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE,
-1);
/* On AArch64 pkey == 0 is reserved and should never be allocated. */
TEST_VERIFY (pkey > 0);
TEST_COMPARE (pkey_get(pkey), PKEY_UNRESTRICTED);
/* Check that access is revoked during signal handling
with initial rights being set to no restrictions. */
TEST_COMPARE (pkey_mprotect ((void *) page, pagesize, PROT_READ
| PROT_WRITE, pkey), 0);
xsignal (SIGUSR1, &sigusr1_handler);
xraise (SIGUSR1);
xsignal (SIGUSR1, SIG_DFL);
TEST_COMPARE (sigusr1_handler_ran, 1);
/* Check that access is revoked during signal handling
with initial rights being set to PKEY_DISABLE_WRITE. */
TEST_COMPARE (pkey_set (pkey, PKEY_DISABLE_WRITE), 0);
xsignal (SIGUSR1, &sigusr1_handler);
xraise (SIGUSR1);
xsignal (SIGUSR1, SIG_DFL);
TEST_COMPARE (sigusr1_handler_ran, 2);
/* Check that all combinations of PKEY flags used in pkey_set
result in consistent values obtained via pkey_get.
Note that whenever flags PKEY_DISABLE_READ and PKEY_DISABLE_WRITE
are set, the PKEY_DISABLE_ACCESS is also set. */
struct
{
unsigned int set;
unsigned int expected;
} rrs[] =
{
{
PKEY_UNRESTRICTED,
PKEY_UNRESTRICTED
},
{
PKEY_DISABLE_ACCESS,
PKEY_DISABLE_ACCESS | PKEY_DISABLE_READ | PKEY_DISABLE_WRITE
},
{
PKEY_DISABLE_WRITE,
PKEY_DISABLE_WRITE
},
{
PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE,
PKEY_DISABLE_ACCESS | PKEY_DISABLE_READ | PKEY_DISABLE_WRITE
},
{
PKEY_DISABLE_EXECUTE,
PKEY_DISABLE_EXECUTE
},
{
PKEY_DISABLE_ACCESS | PKEY_DISABLE_EXECUTE,
PKEY_DISABLE_ACCESS | PKEY_DISABLE_READ | PKEY_DISABLE_WRITE
| PKEY_DISABLE_EXECUTE
},
{
PKEY_DISABLE_WRITE | PKEY_DISABLE_EXECUTE,
PKEY_DISABLE_WRITE | PKEY_DISABLE_EXECUTE
},
{
PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE | PKEY_DISABLE_EXECUTE,
PKEY_DISABLE_ACCESS | PKEY_DISABLE_READ | PKEY_DISABLE_WRITE
| PKEY_DISABLE_EXECUTE
},
{
PKEY_DISABLE_READ,
PKEY_DISABLE_READ
},
{
PKEY_DISABLE_ACCESS | PKEY_DISABLE_READ,
PKEY_DISABLE_ACCESS | PKEY_DISABLE_READ | PKEY_DISABLE_WRITE
},
{
PKEY_DISABLE_WRITE | PKEY_DISABLE_READ,
PKEY_DISABLE_ACCESS | PKEY_DISABLE_READ | PKEY_DISABLE_WRITE
},
{
PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE | PKEY_DISABLE_READ,
PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE | PKEY_DISABLE_READ
},
{
PKEY_DISABLE_EXECUTE | PKEY_DISABLE_READ,
PKEY_DISABLE_EXECUTE | PKEY_DISABLE_READ
},
{
PKEY_DISABLE_ACCESS | PKEY_DISABLE_EXECUTE | PKEY_DISABLE_READ,
PKEY_DISABLE_ACCESS | PKEY_DISABLE_READ | PKEY_DISABLE_WRITE
| PKEY_DISABLE_EXECUTE
},
{
PKEY_DISABLE_WRITE | PKEY_DISABLE_EXECUTE | PKEY_DISABLE_READ,
PKEY_DISABLE_ACCESS | PKEY_DISABLE_READ | PKEY_DISABLE_WRITE
| PKEY_DISABLE_EXECUTE
},
{
PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE | PKEY_DISABLE_EXECUTE
| PKEY_DISABLE_READ,
PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE | PKEY_DISABLE_EXECUTE
| PKEY_DISABLE_READ
},
};
for (int k = 0; k < (array_length (rrs) / 2); k++) {
TEST_COMPARE (k, rrs[k].set);
TEST_COMPARE (pkey_set (pkey, rrs[k].set), 0);
TEST_COMPARE (pkey_get (pkey), rrs[k].expected);
}
/* Check that restrictions above maximum allowed value are rejected. */
TEST_COMPARE (pkey_set (pkey, 16), -1);
TEST_COMPARE (errno, EINVAL);
TEST_COMPARE (pkey_free (pkey), 0);
xmunmap ((void *) page, pagesize);
return 0;
}
#include <support/test-driver.c>

View File

@ -120,7 +120,7 @@ sigusr1_handler (int signum)
TEST_COMPARE (signum, SIGUSR1); TEST_COMPARE (signum, SIGUSR1);
for (int i = 0; i < key_count; ++i) for (int i = 0; i < key_count; ++i)
TEST_VERIFY (pkey_get (keys[i]) == PKEY_DISABLE_ACCESS TEST_VERIFY (pkey_get (keys[i]) == PKEY_DISABLE_ACCESS
|| pkey_get (keys[i]) == i); || (pkey_get (keys[i]) & i) == i);
sigusr1_handler_ran = 1; sigusr1_handler_ran = 1;
} }
@ -185,6 +185,7 @@ do_test (void)
xmunmap (page, pagesize); xmunmap (page, pagesize);
} }
/* Create thread before setting up key in the current thread. */
xpthread_barrier_init (&barrier, NULL, 2); xpthread_barrier_init (&barrier, NULL, 2);
bool delayed_thread_check_access = true; bool delayed_thread_check_access = true;
pthread_t delayed_thread = xpthread_create pthread_t delayed_thread = xpthread_create
@ -212,19 +213,47 @@ do_test (void)
("glibc does not support memory protection keys"); ("glibc does not support memory protection keys");
FAIL_EXIT1 ("pkey_get: %m"); FAIL_EXIT1 ("pkey_get: %m");
} }
/* Check that initial rights that are set via pkey_alloc
can be accessed via pkey_get. */
{
int pkey = -1;
pkey = pkey_alloc (0, PKEY_DISABLE_ACCESS);
TEST_COMPARE (pkey_get (pkey) & PKEY_DISABLE_ACCESS, PKEY_DISABLE_ACCESS);
pkey_free (pkey);
pkey = pkey_alloc (0, PKEY_DISABLE_WRITE);
TEST_COMPARE (pkey_get (pkey) & PKEY_DISABLE_WRITE, PKEY_DISABLE_WRITE);
pkey_free (pkey);
}
/* Check that unallocated pkey is not accepted by the
pkey_mprotect function. */
{
int pkey = -1;
pkey = pkey_alloc (0, PKEY_DISABLE_WRITE);
pkey_free (pkey);
int *page = xmmap (NULL, pagesize, PROT_NONE,
MAP_ANONYMOUS | MAP_PRIVATE, -1);
TEST_COMPARE (pkey_mprotect (page, pagesize, PROT_READ, pkey), -1);
TEST_COMPARE (errno, EINVAL);
xmunmap (page, pagesize);
}
for (int i = 1; i < key_count; ++i) 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); keys[i] = pkey_alloc (0, i);
if (keys[i] < 0) if (keys[i] < 0)
FAIL_EXIT1 ("pkey_alloc (0, %d): %m", i); FAIL_EXIT1 ("pkey_alloc (0, %d): %m", i);
/* pkey_alloc is supposed to change the current thread's access /* pkey_alloc is supposed to change the current thread's access
rights for the new key. */ 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 /* Check that all the keys have the expected access rights for the
current thread. */ current thread. */
for (int i = 0; i < key_count; ++i) 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. */ /* Allocate a test page for each key. */
for (int i = 0; i < key_count; ++i) for (int i = 0; i < key_count; ++i)
@ -241,12 +270,12 @@ do_test (void)
pthread_barrier_wait (&barrier); pthread_barrier_wait (&barrier);
struct thread_result *result = xpthread_join (delayed_thread); struct thread_result *result = xpthread_join (delayed_thread);
for (int i = 0; i < key_count; ++i) for (int i = 0; i < key_count; ++i)
TEST_COMPARE (result->access_rights[i], TEST_COMPARE (result->access_rights[i] &
PKEY_DISABLE_ACCESS); PKEY_DISABLE_ACCESS, PKEY_DISABLE_ACCESS);
struct thread_result *result2 = xpthread_join (result->next_thread); struct thread_result *result2 = xpthread_join (result->next_thread);
for (int i = 0; i < key_count; ++i) for (int i = 0; i < key_count; ++i)
TEST_COMPARE (result->access_rights[i], TEST_COMPARE (result->access_rights[i] &
PKEY_DISABLE_ACCESS); PKEY_DISABLE_ACCESS, PKEY_DISABLE_ACCESS);
free (result); free (result);
free (result2); free (result2);
} }
@ -257,12 +286,12 @@ do_test (void)
pthread_t get_thread = xpthread_create (NULL, get_thread_func, NULL); pthread_t get_thread = xpthread_create (NULL, get_thread_func, NULL);
struct thread_result *result = xpthread_join (get_thread); struct thread_result *result = xpthread_join (get_thread);
for (int i = 0; i < key_count; ++i) 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); free (result);
} }
for (int i = 0; i < key_count; ++i) 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. */ /* Check that in a signal handler, there is no access. */
xsignal (SIGUSR1, &sigusr1_handler); xsignal (SIGUSR1, &sigusr1_handler);
@ -281,7 +310,7 @@ do_test (void)
printf ("info: checking access for key %d, bits 0x%x\n", printf ("info: checking access for key %d, bits 0x%x\n",
i, pkey_get (keys[i])); i, pkey_get (keys[i]));
for (int j = 0; j < key_count; ++j) 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) if (i & PKEY_DISABLE_ACCESS)
{ {
TEST_VERIFY (!check_page_access (i, false)); TEST_VERIFY (!check_page_access (i, false));
@ -355,7 +384,7 @@ do_test (void)
not what happens in practice. */ not what happens in practice. */
{ {
/* The limit is in place to avoid running indefinitely in case /* 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_array = xcalloc (100000, sizeof (*keys_array));
int keys_allocated = 0; int keys_allocated = 0;
while (keys_allocated < 100000) while (keys_allocated < 100000)