selftests/mm: Use generic pkey register manipulation

pkey_sighandler_tests.c currently hardcodes x86 PKRU encodings. The
first step towards running those tests on arm64 is to abstract away
the pkey register values.

Since those tests want to deny access to all keys except a few,
we have each arch define PKEY_REG_ALLOW_NONE, the pkey register value
denying access to all keys. We then use the existing set_pkey_bits()
helper to grant access to specific keys.

Because pkeys may also remove the execute permission on arm64, we
need to be a little careful: all code is mapped with pkey 0, and we
need it to remain executable. pkey_reg_restrictive_default() is
introduced for that purpose: the value it returns prevents RW access
to all pkeys, but retains X permission for pkey 0.

test_pkru_preserved_after_sigusr1() only checks that the pkey
register value remains unchanged after a signal is delivered, so the
particular value is irrelevant. We enable pkey 0 and a few more
arbitrary keys in the smallest range available on all architectures
(8 keys on arm64).

Signed-off-by: Kevin Brodsky <kevin.brodsky@arm.com>
Acked-by: Dave Hansen <dave.hansen@linux.intel.com>
Link: https://lore.kernel.org/r/20241029144539.111155-5-kevin.brodsky@arm.com
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
This commit is contained in:
Kevin Brodsky 2024-10-29 14:45:38 +00:00 committed by Catalin Marinas
parent 8edbbfcc1e
commit 6e182dc9f2
3 changed files with 47 additions and 9 deletions

View File

@ -31,6 +31,7 @@
#define NR_RESERVED_PKEYS 1 /* pkey-0 */
#define PKEY_ALLOW_ALL 0x77777777
#define PKEY_REG_ALLOW_NONE 0x0
#define PKEY_BITS_PER_PKEY 4
#define PAGE_SIZE sysconf(_SC_PAGESIZE)

View File

@ -34,6 +34,8 @@
#define PAGE_SIZE 4096
#define MB (1<<20)
#define PKEY_REG_ALLOW_NONE 0x55555555
static inline void __page_o_noops(void)
{
/* 8-bytes of instruction * 512 bytes = 1 page */

View File

@ -11,6 +11,7 @@
*/
#define _GNU_SOURCE
#define __SANE_USERSPACE_TYPES__
#include <linux/mman.h>
#include <errno.h>
#include <sys/syscall.h>
#include <string.h>
@ -65,6 +66,20 @@ long syscall_raw(long n, long a1, long a2, long a3, long a4, long a5, long a6)
return ret;
}
/*
* Returns the most restrictive pkey register value that can be used by the
* tests.
*/
static inline u64 pkey_reg_restrictive_default(void)
{
/*
* Disallow everything except execution on pkey 0, so that each caller
* doesn't need to enable it explicitly (the selftest code runs with
* its code mapped with pkey 0).
*/
return set_pkey_bits(PKEY_REG_ALLOW_NONE, 0, PKEY_DISABLE_ACCESS);
}
static void sigsegv_handler(int signo, siginfo_t *info, void *ucontext)
{
pthread_mutex_lock(&mutex);
@ -113,7 +128,7 @@ static void raise_sigusr2(void)
static void *thread_segv_with_pkey0_disabled(void *ptr)
{
/* Disable MPK 0 (and all others too) */
__write_pkey_reg(0x55555555);
__write_pkey_reg(pkey_reg_restrictive_default());
/* Segfault (with SEGV_MAPERR) */
*(int *) (0x1) = 1;
@ -123,7 +138,7 @@ static void *thread_segv_with_pkey0_disabled(void *ptr)
static void *thread_segv_pkuerr_stack(void *ptr)
{
/* Disable MPK 0 (and all others too) */
__write_pkey_reg(0x55555555);
__write_pkey_reg(pkey_reg_restrictive_default());
/* After we disable MPK 0, we can't access the stack to return */
return NULL;
@ -133,6 +148,7 @@ static void *thread_segv_maperr_ptr(void *ptr)
{
stack_t *stack = ptr;
int *bad = (int *)1;
u64 pkey_reg;
/*
* Setup alternate signal stack, which should be pkey_mprotect()ed by
@ -142,7 +158,9 @@ static void *thread_segv_maperr_ptr(void *ptr)
syscall_raw(SYS_sigaltstack, (long)stack, 0, 0, 0, 0, 0);
/* Disable MPK 0. Only MPK 1 is enabled. */
__write_pkey_reg(0x55555551);
pkey_reg = pkey_reg_restrictive_default();
pkey_reg = set_pkey_bits(pkey_reg, 1, PKEY_UNRESTRICTED);
__write_pkey_reg(pkey_reg);
/* Segfault */
*bad = 1;
@ -240,6 +258,7 @@ static void test_sigsegv_handler_with_different_pkey_for_stack(void)
int pkey;
int parent_pid = 0;
int child_pid = 0;
u64 pkey_reg;
sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
@ -257,7 +276,10 @@ static void test_sigsegv_handler_with_different_pkey_for_stack(void)
assert(stack != MAP_FAILED);
/* Allow access to MPK 0 and MPK 1 */
__write_pkey_reg(0x55555550);
pkey_reg = pkey_reg_restrictive_default();
pkey_reg = set_pkey_bits(pkey_reg, 0, PKEY_UNRESTRICTED);
pkey_reg = set_pkey_bits(pkey_reg, 1, PKEY_UNRESTRICTED);
__write_pkey_reg(pkey_reg);
/* Protect the new stack with MPK 1 */
pkey = pkey_alloc(0, 0);
@ -307,7 +329,13 @@ static void test_sigsegv_handler_with_different_pkey_for_stack(void)
static void test_pkru_preserved_after_sigusr1(void)
{
struct sigaction sa;
unsigned long pkru = 0x45454544;
u64 pkey_reg;
/* Allow access to MPK 0 and an arbitrary set of keys */
pkey_reg = pkey_reg_restrictive_default();
pkey_reg = set_pkey_bits(pkey_reg, 0, PKEY_UNRESTRICTED);
pkey_reg = set_pkey_bits(pkey_reg, 3, PKEY_UNRESTRICTED);
pkey_reg = set_pkey_bits(pkey_reg, 7, PKEY_UNRESTRICTED);
sa.sa_flags = SA_SIGINFO;
@ -320,7 +348,7 @@ static void test_pkru_preserved_after_sigusr1(void)
memset(&siginfo, 0, sizeof(siginfo));
__write_pkey_reg(pkru);
__write_pkey_reg(pkey_reg);
raise(SIGUSR1);
@ -330,7 +358,7 @@ static void test_pkru_preserved_after_sigusr1(void)
pthread_mutex_unlock(&mutex);
/* Ensure the pkru value is the same after returning from signal. */
ksft_test_result(pkru == __read_pkey_reg() &&
ksft_test_result(pkey_reg == __read_pkey_reg() &&
siginfo.si_signo == SIGUSR1,
"%s\n", __func__);
}
@ -347,6 +375,7 @@ static noinline void *thread_sigusr2_self(void *ptr)
'S', 'I', 'G', 'U', 'S', 'R', '2',
'.', '.', '.', '\n', '\0'};
stack_t *stack = ptr;
u64 pkey_reg;
/*
* Setup alternate signal stack, which should be pkey_mprotect()ed by
@ -356,7 +385,9 @@ static noinline void *thread_sigusr2_self(void *ptr)
syscall(SYS_sigaltstack, (long)stack, 0, 0, 0, 0, 0);
/* Disable MPK 0. Only MPK 2 is enabled. */
__write_pkey_reg(0x55555545);
pkey_reg = pkey_reg_restrictive_default();
pkey_reg = set_pkey_bits(pkey_reg, 2, PKEY_UNRESTRICTED);
__write_pkey_reg(pkey_reg);
raise_sigusr2();
@ -384,6 +415,7 @@ static void test_pkru_sigreturn(void)
int pkey;
int parent_pid = 0;
int child_pid = 0;
u64 pkey_reg;
sa.sa_handler = SIG_DFL;
sa.sa_flags = 0;
@ -418,7 +450,10 @@ static void test_pkru_sigreturn(void)
* the current thread's stack is protected by the default MPK 0. Hence
* both need to be enabled.
*/
__write_pkey_reg(0x55555544);
pkey_reg = pkey_reg_restrictive_default();
pkey_reg = set_pkey_bits(pkey_reg, 0, PKEY_UNRESTRICTED);
pkey_reg = set_pkey_bits(pkey_reg, 2, PKEY_UNRESTRICTED);
__write_pkey_reg(pkey_reg);
/* Protect the stack with MPK 2 */
pkey = pkey_alloc(0, 0);