Skip to content

Commit f5b5ea5

Browse files
jgoulywilldeacon
authored andcommitted
selftests: mm: make protection_keys test work on arm64
The encoding of the pkey register differs on arm64, than on x86/ppc. On those platforms, a bit in the register is used to disable permissions, for arm64, a bit enabled in the register indicates that the permission is allowed. This drops two asserts of the form: assert(read_pkey_reg() <= orig_pkey_reg); Because on arm64 this doesn't hold, due to the encoding. The pkey must be reset to both access allow and write allow in the signal handler. pkey_access_allow() works currently for PowerPC as the PKEY_DISABLE_ACCESS and PKEY_DISABLE_WRITE have overlapping bits set. Access to the uc_mcontext is abstracted, as arm64 has a different structure. Signed-off-by: Joey Gouly <[email protected]> Cc: Catalin Marinas <[email protected]> Cc: Will Deacon <[email protected]> Cc: Andrew Morton <[email protected]> Cc: Shuah Khan <[email protected]> Cc: Dave Hansen <[email protected]> Cc: Aneesh Kumar K.V <[email protected]> Acked-by: Dave Hansen <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Will Deacon <[email protected]>
1 parent 41bbcf7 commit f5b5ea5

File tree

7 files changed

+247
-12
lines changed

7 files changed

+247
-12
lines changed

tools/testing/selftests/arm64/signal/testcases/testcases.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@
2626
#define HDR_SZ \
2727
sizeof(struct _aarch64_ctx)
2828

29+
#define GET_UC_RESV_HEAD(uc) \
30+
(struct _aarch64_ctx *)(&(uc->uc_mcontext.__reserved))
31+
2932
#define GET_SF_RESV_HEAD(sf) \
3033
(struct _aarch64_ctx *)(&(sf).uc.uc_mcontext.__reserved)
3134

tools/testing/selftests/mm/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ TEST_GEN_FILES += $(BINARIES_64)
104104
endif
105105
else
106106

107-
ifneq (,$(findstring $(ARCH),powerpc))
107+
ifneq (,$(filter $(ARCH),arm64 powerpc))
108108
TEST_GEN_FILES += protection_keys
109109
endif
110110

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
/*
3+
* Copyright (C) 2023 Arm Ltd.
4+
*/
5+
6+
#ifndef _PKEYS_ARM64_H
7+
#define _PKEYS_ARM64_H
8+
9+
#include "vm_util.h"
10+
/* for signal frame parsing */
11+
#include "../arm64/signal/testcases/testcases.h"
12+
13+
#ifndef SYS_mprotect_key
14+
# define SYS_mprotect_key 288
15+
#endif
16+
#ifndef SYS_pkey_alloc
17+
# define SYS_pkey_alloc 289
18+
# define SYS_pkey_free 290
19+
#endif
20+
#define MCONTEXT_IP(mc) mc.pc
21+
#define MCONTEXT_TRAPNO(mc) -1
22+
23+
#define PKEY_MASK 0xf
24+
25+
#define POE_NONE 0x0
26+
#define POE_X 0x2
27+
#define POE_RX 0x3
28+
#define POE_RWX 0x7
29+
30+
#define NR_PKEYS 8
31+
#define NR_RESERVED_PKEYS 1 /* pkey-0 */
32+
33+
#define PKEY_ALLOW_ALL 0x77777777
34+
35+
#define PKEY_BITS_PER_PKEY 4
36+
#define PAGE_SIZE sysconf(_SC_PAGESIZE)
37+
#undef HPAGE_SIZE
38+
#define HPAGE_SIZE default_huge_page_size()
39+
40+
/* 4-byte instructions * 16384 = 64K page */
41+
#define __page_o_noops() asm(".rept 16384 ; nop; .endr")
42+
43+
static inline u64 __read_pkey_reg(void)
44+
{
45+
u64 pkey_reg = 0;
46+
47+
// POR_EL0
48+
asm volatile("mrs %0, S3_3_c10_c2_4" : "=r" (pkey_reg));
49+
50+
return pkey_reg;
51+
}
52+
53+
static inline void __write_pkey_reg(u64 pkey_reg)
54+
{
55+
u64 por = pkey_reg;
56+
57+
dprintf4("%s() changing %016llx to %016llx\n",
58+
__func__, __read_pkey_reg(), pkey_reg);
59+
60+
// POR_EL0
61+
asm volatile("msr S3_3_c10_c2_4, %0\nisb" :: "r" (por) :);
62+
63+
dprintf4("%s() pkey register after changing %016llx to %016llx\n",
64+
__func__, __read_pkey_reg(), pkey_reg);
65+
}
66+
67+
static inline int cpu_has_pkeys(void)
68+
{
69+
/* No simple way to determine this */
70+
return 1;
71+
}
72+
73+
static inline u32 pkey_bit_position(int pkey)
74+
{
75+
return pkey * PKEY_BITS_PER_PKEY;
76+
}
77+
78+
static inline int get_arch_reserved_keys(void)
79+
{
80+
return NR_RESERVED_PKEYS;
81+
}
82+
83+
void expect_fault_on_read_execonly_key(void *p1, int pkey)
84+
{
85+
}
86+
87+
void *malloc_pkey_with_mprotect_subpage(long size, int prot, u16 pkey)
88+
{
89+
return PTR_ERR_ENOTSUP;
90+
}
91+
92+
#define set_pkey_bits set_pkey_bits
93+
static inline u64 set_pkey_bits(u64 reg, int pkey, u64 flags)
94+
{
95+
u32 shift = pkey_bit_position(pkey);
96+
u64 new_val = POE_RWX;
97+
98+
/* mask out bits from pkey in old value */
99+
reg &= ~((u64)PKEY_MASK << shift);
100+
101+
if (flags & PKEY_DISABLE_ACCESS)
102+
new_val = POE_X;
103+
else if (flags & PKEY_DISABLE_WRITE)
104+
new_val = POE_RX;
105+
106+
/* OR in new bits for pkey */
107+
reg |= new_val << shift;
108+
109+
return reg;
110+
}
111+
112+
#define get_pkey_bits get_pkey_bits
113+
static inline u64 get_pkey_bits(u64 reg, int pkey)
114+
{
115+
u32 shift = pkey_bit_position(pkey);
116+
/*
117+
* shift down the relevant bits to the lowest four, then
118+
* mask off all the other higher bits
119+
*/
120+
u32 perm = (reg >> shift) & PKEY_MASK;
121+
122+
if (perm == POE_X)
123+
return PKEY_DISABLE_ACCESS;
124+
if (perm == POE_RX)
125+
return PKEY_DISABLE_WRITE;
126+
return 0;
127+
}
128+
129+
static void aarch64_write_signal_pkey(ucontext_t *uctxt, u64 pkey)
130+
{
131+
struct _aarch64_ctx *ctx = GET_UC_RESV_HEAD(uctxt);
132+
struct poe_context *poe_ctx =
133+
(struct poe_context *) get_header(ctx, POE_MAGIC,
134+
sizeof(uctxt->uc_mcontext), NULL);
135+
if (poe_ctx)
136+
poe_ctx->por_el0 = pkey;
137+
}
138+
139+
#endif /* _PKEYS_ARM64_H */

tools/testing/selftests/mm/pkey-helpers.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,12 +91,17 @@ void record_pkey_malloc(void *ptr, long size, int prot);
9191
#include "pkey-x86.h"
9292
#elif defined(__powerpc64__) /* arch */
9393
#include "pkey-powerpc.h"
94+
#elif defined(__aarch64__) /* arch */
95+
#include "pkey-arm64.h"
9496
#else /* arch */
9597
#error Architecture not supported
9698
#endif /* arch */
9799

100+
#ifndef PKEY_MASK
98101
#define PKEY_MASK (PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE)
102+
#endif
99103

104+
#ifndef set_pkey_bits
100105
static inline u64 set_pkey_bits(u64 reg, int pkey, u64 flags)
101106
{
102107
u32 shift = pkey_bit_position(pkey);
@@ -106,7 +111,9 @@ static inline u64 set_pkey_bits(u64 reg, int pkey, u64 flags)
106111
reg |= (flags & PKEY_MASK) << shift;
107112
return reg;
108113
}
114+
#endif
109115

116+
#ifndef get_pkey_bits
110117
static inline u64 get_pkey_bits(u64 reg, int pkey)
111118
{
112119
u32 shift = pkey_bit_position(pkey);
@@ -116,6 +123,7 @@ static inline u64 get_pkey_bits(u64 reg, int pkey)
116123
*/
117124
return ((reg >> shift) & PKEY_MASK);
118125
}
126+
#endif
119127

120128
extern u64 shadow_pkey_reg;
121129

tools/testing/selftests/mm/pkey-powerpc.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
# define SYS_pkey_free 385
99
#endif
1010
#define REG_IP_IDX PT_NIP
11+
#define MCONTEXT_IP(mc) mc.gp_regs[REG_IP_IDX]
12+
#define MCONTEXT_TRAPNO(mc) mc.gp_regs[REG_TRAPNO]
1113
#define REG_TRAPNO PT_TRAP
1214
#define MCONTEXT_FPREGS
1315
#define gregs gp_regs

tools/testing/selftests/mm/pkey-x86.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515

1616
#endif
1717

18+
#define MCONTEXT_IP(mc) mc.gregs[REG_IP_IDX]
19+
#define MCONTEXT_TRAPNO(mc) mc.gregs[REG_TRAPNO]
1820
#define MCONTEXT_FPREGS
1921

2022
#ifndef PKEY_DISABLE_ACCESS

tools/testing/selftests/mm/protection_keys.c

Lines changed: 92 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ void abort_hooks(void)
147147
* will then fault, which makes sure that the fault code handles
148148
* execute-only memory properly.
149149
*/
150-
#ifdef __powerpc64__
150+
#if defined(__powerpc64__) || defined(__aarch64__)
151151
/* This way, both 4K and 64K alignment are maintained */
152152
__attribute__((__aligned__(65536)))
153153
#else
@@ -212,7 +212,6 @@ void pkey_disable_set(int pkey, int flags)
212212
unsigned long syscall_flags = 0;
213213
int ret;
214214
int pkey_rights;
215-
u64 orig_pkey_reg = read_pkey_reg();
216215

217216
dprintf1("START->%s(%d, 0x%x)\n", __func__,
218217
pkey, flags);
@@ -242,8 +241,6 @@ void pkey_disable_set(int pkey, int flags)
242241

243242
dprintf1("%s(%d) pkey_reg: 0x%016llx\n",
244243
__func__, pkey, read_pkey_reg());
245-
if (flags)
246-
pkey_assert(read_pkey_reg() >= orig_pkey_reg);
247244
dprintf1("END<---%s(%d, 0x%x)\n", __func__,
248245
pkey, flags);
249246
}
@@ -253,7 +250,6 @@ void pkey_disable_clear(int pkey, int flags)
253250
unsigned long syscall_flags = 0;
254251
int ret;
255252
int pkey_rights = hw_pkey_get(pkey, syscall_flags);
256-
u64 orig_pkey_reg = read_pkey_reg();
257253

258254
pkey_assert(flags & (PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE));
259255

@@ -273,8 +269,6 @@ void pkey_disable_clear(int pkey, int flags)
273269

274270
dprintf1("%s(%d) pkey_reg: 0x%016llx\n", __func__,
275271
pkey, read_pkey_reg());
276-
if (flags)
277-
assert(read_pkey_reg() <= orig_pkey_reg);
278272
}
279273

280274
void pkey_write_allow(int pkey)
@@ -330,8 +324,8 @@ void signal_handler(int signum, siginfo_t *si, void *vucontext)
330324
__func__, __LINE__,
331325
__read_pkey_reg(), shadow_pkey_reg);
332326

333-
trapno = uctxt->uc_mcontext.gregs[REG_TRAPNO];
334-
ip = uctxt->uc_mcontext.gregs[REG_IP_IDX];
327+
trapno = MCONTEXT_TRAPNO(uctxt->uc_mcontext);
328+
ip = MCONTEXT_IP(uctxt->uc_mcontext);
335329
#ifdef MCONTEXT_FPREGS
336330
fpregs = (char *) uctxt->uc_mcontext.fpregs;
337331
#endif
@@ -395,6 +389,8 @@ void signal_handler(int signum, siginfo_t *si, void *vucontext)
395389
#elif defined(__powerpc64__) /* arch */
396390
/* restore access and let the faulting instruction continue */
397391
pkey_access_allow(siginfo_pkey);
392+
#elif defined(__aarch64__)
393+
aarch64_write_signal_pkey(uctxt, PKEY_ALLOW_ALL);
398394
#endif /* arch */
399395
pkey_faults++;
400396
dprintf1("<<<<==================================================\n");
@@ -908,7 +904,9 @@ void expected_pkey_fault(int pkey)
908904
* test program continue. We now have to restore it.
909905
*/
910906
if (__read_pkey_reg() != 0)
911-
#else /* arch */
907+
#elif defined(__aarch64__)
908+
if (__read_pkey_reg() != PKEY_ALLOW_ALL)
909+
#else
912910
if (__read_pkey_reg() != shadow_pkey_reg)
913911
#endif /* arch */
914912
pkey_assert(0);
@@ -1498,6 +1496,11 @@ void test_executing_on_unreadable_memory(int *ptr, u16 pkey)
14981496
lots_o_noops_around_write(&scratch);
14991497
do_not_expect_pkey_fault("executing on PROT_EXEC memory");
15001498
expect_fault_on_read_execonly_key(p1, pkey);
1499+
1500+
// Reset back to PROT_EXEC | PROT_READ for architectures that support
1501+
// non-PKEY execute-only permissions.
1502+
ret = mprotect_pkey(p1, PAGE_SIZE, PROT_EXEC | PROT_READ, (u64)pkey);
1503+
pkey_assert(!ret);
15011504
}
15021505

15031506
void test_implicit_mprotect_exec_only_memory(int *ptr, u16 pkey)
@@ -1671,6 +1674,84 @@ void test_ptrace_modifies_pkru(int *ptr, u16 pkey)
16711674
}
16721675
#endif
16731676

1677+
#if defined(__aarch64__)
1678+
void test_ptrace_modifies_pkru(int *ptr, u16 pkey)
1679+
{
1680+
pid_t child;
1681+
int status, ret;
1682+
struct iovec iov;
1683+
u64 trace_pkey;
1684+
/* Just a random pkey value.. */
1685+
u64 new_pkey = (POE_X << PKEY_BITS_PER_PKEY * 2) |
1686+
(POE_NONE << PKEY_BITS_PER_PKEY) |
1687+
POE_RWX;
1688+
1689+
child = fork();
1690+
pkey_assert(child >= 0);
1691+
dprintf3("[%d] fork() ret: %d\n", getpid(), child);
1692+
if (!child) {
1693+
ptrace(PTRACE_TRACEME, 0, 0, 0);
1694+
1695+
/* Stop and allow the tracer to modify PKRU directly */
1696+
raise(SIGSTOP);
1697+
1698+
/*
1699+
* need __read_pkey_reg() version so we do not do shadow_pkey_reg
1700+
* checking
1701+
*/
1702+
if (__read_pkey_reg() != new_pkey)
1703+
exit(1);
1704+
1705+
raise(SIGSTOP);
1706+
1707+
exit(0);
1708+
}
1709+
1710+
pkey_assert(child == waitpid(child, &status, 0));
1711+
dprintf3("[%d] waitpid(%d) status: %x\n", getpid(), child, status);
1712+
pkey_assert(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP);
1713+
1714+
iov.iov_base = &trace_pkey;
1715+
iov.iov_len = 8;
1716+
ret = ptrace(PTRACE_GETREGSET, child, (void *)NT_ARM_POE, &iov);
1717+
pkey_assert(ret == 0);
1718+
pkey_assert(trace_pkey == read_pkey_reg());
1719+
1720+
trace_pkey = new_pkey;
1721+
1722+
ret = ptrace(PTRACE_SETREGSET, child, (void *)NT_ARM_POE, &iov);
1723+
pkey_assert(ret == 0);
1724+
1725+
/* Test that the modification is visible in ptrace before any execution */
1726+
memset(&trace_pkey, 0, sizeof(trace_pkey));
1727+
ret = ptrace(PTRACE_GETREGSET, child, (void *)NT_ARM_POE, &iov);
1728+
pkey_assert(ret == 0);
1729+
pkey_assert(trace_pkey == new_pkey);
1730+
1731+
/* Execute the tracee */
1732+
ret = ptrace(PTRACE_CONT, child, 0, 0);
1733+
pkey_assert(ret == 0);
1734+
1735+
/* Test that the tracee saw the PKRU value change */
1736+
pkey_assert(child == waitpid(child, &status, 0));
1737+
dprintf3("[%d] waitpid(%d) status: %x\n", getpid(), child, status);
1738+
pkey_assert(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP);
1739+
1740+
/* Test that the modification is visible in ptrace after execution */
1741+
memset(&trace_pkey, 0, sizeof(trace_pkey));
1742+
ret = ptrace(PTRACE_GETREGSET, child, (void *)NT_ARM_POE, &iov);
1743+
pkey_assert(ret == 0);
1744+
pkey_assert(trace_pkey == new_pkey);
1745+
1746+
ret = ptrace(PTRACE_CONT, child, 0, 0);
1747+
pkey_assert(ret == 0);
1748+
pkey_assert(child == waitpid(child, &status, 0));
1749+
dprintf3("[%d] waitpid(%d) status: %x\n", getpid(), child, status);
1750+
pkey_assert(WIFEXITED(status));
1751+
pkey_assert(WEXITSTATUS(status) == 0);
1752+
}
1753+
#endif
1754+
16741755
void test_mprotect_pkey_on_unsupported_cpu(int *ptr, u16 pkey)
16751756
{
16761757
int size = PAGE_SIZE;
@@ -1706,7 +1787,7 @@ void (*pkey_tests[])(int *ptr, u16 pkey) = {
17061787
test_pkey_syscalls_bad_args,
17071788
test_pkey_alloc_exhaust,
17081789
test_pkey_alloc_free_attach_pkey0,
1709-
#if defined(__i386__) || defined(__x86_64__)
1790+
#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__)
17101791
test_ptrace_modifies_pkru,
17111792
#endif
17121793
};

0 commit comments

Comments
 (0)