Skip to content

Commit 890274c

Browse files
committed
powerpc/64s: Implement KUAP for Radix MMU
Kernel Userspace Access Prevention utilises a feature of the Radix MMU which disallows read and write access to userspace addresses. By utilising this, the kernel is prevented from accessing user data from outside of trusted paths that perform proper safety checks, such as copy_{to/from}_user() and friends. Userspace access is disabled from early boot and is only enabled when performing an operation like copy_{to/from}_user(). The register that controls this (AMR) does not prevent userspace from accessing itself, so there is no need to save and restore when entering and exiting userspace. When entering the kernel from the kernel we save AMR and if it is not blocking user access (because eg. we faulted doing a user access) we reblock user access for the duration of the exception (ie. the page fault) and then restore the AMR when returning back to the kernel. This feature can be tested by using the lkdtm driver (CONFIG_LKDTM=y) and performing the following: # (echo ACCESS_USERSPACE) > [debugfs]/provoke-crash/DIRECT If enabled, this should send SIGSEGV to the thread. We also add paranoid checking of AMR in switch and syscall return under CONFIG_PPC_KUAP_DEBUG. Co-authored-by: Michael Ellerman <[email protected]> Signed-off-by: Russell Currey <[email protected]> Signed-off-by: Michael Ellerman <[email protected]>
1 parent ef29672 commit 890274c

File tree

10 files changed

+176
-3
lines changed

10 files changed

+176
-3
lines changed
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
#ifndef _ASM_POWERPC_BOOK3S_64_KUP_RADIX_H
3+
#define _ASM_POWERPC_BOOK3S_64_KUP_RADIX_H
4+
5+
#include <linux/const.h>
6+
7+
#define AMR_KUAP_BLOCK_READ UL(0x4000000000000000)
8+
#define AMR_KUAP_BLOCK_WRITE UL(0x8000000000000000)
9+
#define AMR_KUAP_BLOCKED (AMR_KUAP_BLOCK_READ | AMR_KUAP_BLOCK_WRITE)
10+
#define AMR_KUAP_SHIFT 62
11+
12+
#ifdef __ASSEMBLY__
13+
14+
.macro kuap_restore_amr gpr
15+
#ifdef CONFIG_PPC_KUAP
16+
BEGIN_MMU_FTR_SECTION_NESTED(67)
17+
ld \gpr, STACK_REGS_KUAP(r1)
18+
mtspr SPRN_AMR, \gpr
19+
END_MMU_FTR_SECTION_NESTED_IFSET(MMU_FTR_RADIX_KUAP, 67)
20+
#endif
21+
.endm
22+
23+
.macro kuap_check_amr gpr1, gpr2
24+
#ifdef CONFIG_PPC_KUAP_DEBUG
25+
BEGIN_MMU_FTR_SECTION_NESTED(67)
26+
mfspr \gpr1, SPRN_AMR
27+
li \gpr2, (AMR_KUAP_BLOCKED >> AMR_KUAP_SHIFT)
28+
sldi \gpr2, \gpr2, AMR_KUAP_SHIFT
29+
999: tdne \gpr1, \gpr2
30+
EMIT_BUG_ENTRY 999b, __FILE__, __LINE__, (BUGFLAG_WARNING | BUGFLAG_ONCE)
31+
END_MMU_FTR_SECTION_NESTED_IFSET(MMU_FTR_RADIX_KUAP, 67)
32+
#endif
33+
.endm
34+
35+
.macro kuap_save_amr_and_lock gpr1, gpr2, use_cr, msr_pr_cr
36+
#ifdef CONFIG_PPC_KUAP
37+
BEGIN_MMU_FTR_SECTION_NESTED(67)
38+
.ifnb \msr_pr_cr
39+
bne \msr_pr_cr, 99f
40+
.endif
41+
mfspr \gpr1, SPRN_AMR
42+
std \gpr1, STACK_REGS_KUAP(r1)
43+
li \gpr2, (AMR_KUAP_BLOCKED >> AMR_KUAP_SHIFT)
44+
sldi \gpr2, \gpr2, AMR_KUAP_SHIFT
45+
cmpd \use_cr, \gpr1, \gpr2
46+
beq \use_cr, 99f
47+
// We don't isync here because we very recently entered via rfid
48+
mtspr SPRN_AMR, \gpr2
49+
isync
50+
99:
51+
END_MMU_FTR_SECTION_NESTED_IFSET(MMU_FTR_RADIX_KUAP, 67)
52+
#endif
53+
.endm
54+
55+
#else /* !__ASSEMBLY__ */
56+
57+
#ifdef CONFIG_PPC_KUAP
58+
59+
#include <asm/reg.h>
60+
61+
/*
62+
* We support individually allowing read or write, but we don't support nesting
63+
* because that would require an expensive read/modify write of the AMR.
64+
*/
65+
66+
static inline void set_kuap(unsigned long value)
67+
{
68+
if (!mmu_has_feature(MMU_FTR_RADIX_KUAP))
69+
return;
70+
71+
/*
72+
* ISA v3.0B says we need a CSI (Context Synchronising Instruction) both
73+
* before and after the move to AMR. See table 6 on page 1134.
74+
*/
75+
isync();
76+
mtspr(SPRN_AMR, value);
77+
isync();
78+
}
79+
80+
static inline void allow_user_access(void __user *to, const void __user *from,
81+
unsigned long size)
82+
{
83+
// This is written so we can resolve to a single case at build time
84+
if (__builtin_constant_p(to) && to == NULL)
85+
set_kuap(AMR_KUAP_BLOCK_WRITE);
86+
else if (__builtin_constant_p(from) && from == NULL)
87+
set_kuap(AMR_KUAP_BLOCK_READ);
88+
else
89+
set_kuap(0);
90+
}
91+
92+
static inline void prevent_user_access(void __user *to, const void __user *from,
93+
unsigned long size)
94+
{
95+
set_kuap(AMR_KUAP_BLOCKED);
96+
}
97+
98+
#endif /* CONFIG_PPC_KUAP */
99+
100+
#endif /* __ASSEMBLY__ */
101+
102+
#endif /* _ASM_POWERPC_BOOK3S_64_KUP_RADIX_H */

arch/powerpc/include/asm/exception-64s.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,7 @@ END_FTR_SECTION_NESTED(ftr,ftr,943)
497497
RESTORE_CTR(r1, area); \
498498
b bad_stack; \
499499
3: EXCEPTION_PROLOG_COMMON_1(); \
500+
kuap_save_amr_and_lock r9, r10, cr1, cr0; \
500501
beq 4f; /* if from kernel mode */ \
501502
ACCOUNT_CPU_USER_ENTRY(r13, r9, r10); \
502503
SAVE_PPR(area, r9); \
@@ -691,6 +692,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_CTRL)
691692
*/
692693
#define EXCEPTION_COMMON_NORET_STACK(area, trap, label, hdlr, additions) \
693694
EXCEPTION_PROLOG_COMMON_1(); \
695+
kuap_save_amr_and_lock r9, r10, cr1; \
694696
EXCEPTION_PROLOG_COMMON_2(area); \
695697
EXCEPTION_PROLOG_COMMON_3(trap); \
696698
/* Volatile regs are potentially clobbered here */ \

arch/powerpc/include/asm/feature-fixups.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ label##5: \
100100
#define END_MMU_FTR_SECTION(msk, val) \
101101
END_MMU_FTR_SECTION_NESTED(msk, val, 97)
102102

103+
#define END_MMU_FTR_SECTION_NESTED_IFSET(msk, label) \
104+
END_MMU_FTR_SECTION_NESTED((msk), (msk), label)
105+
103106
#define END_MMU_FTR_SECTION_IFSET(msk) END_MMU_FTR_SECTION((msk), (msk))
104107
#define END_MMU_FTR_SECTION_IFCLR(msk) END_MMU_FTR_SECTION((msk), 0)
105108

arch/powerpc/include/asm/kup.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
#ifndef _ASM_POWERPC_KUP_H_
33
#define _ASM_POWERPC_KUP_H_
44

5+
#ifdef CONFIG_PPC64
6+
#include <asm/book3s/64/kup-radix.h>
7+
#endif
8+
59
#ifndef __ASSEMBLY__
610

711
#include <asm/pgtable.h>

arch/powerpc/include/asm/mmu.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,11 @@
107107
*/
108108
#define MMU_FTR_1T_SEGMENT ASM_CONST(0x40000000)
109109

110+
/*
111+
* Supports KUAP (key 0 controlling userspace addresses) on radix
112+
*/
113+
#define MMU_FTR_RADIX_KUAP ASM_CONST(0x80000000)
114+
110115
/* MMU feature bit sets for various CPUs */
111116
#define MMU_FTRS_DEFAULT_HPTE_ARCH_V2 \
112117
MMU_FTR_HPTE_TABLE | MMU_FTR_PPCAS_ARCH_V2
@@ -164,7 +169,10 @@ enum {
164169
#endif
165170
#ifdef CONFIG_PPC_RADIX_MMU
166171
MMU_FTR_TYPE_RADIX |
167-
#endif
172+
#ifdef CONFIG_PPC_KUAP
173+
MMU_FTR_RADIX_KUAP |
174+
#endif /* CONFIG_PPC_KUAP */
175+
#endif /* CONFIG_PPC_RADIX_MMU */
168176
0,
169177
};
170178

arch/powerpc/kernel/entry_64.S

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
#include <asm/exception-64e.h>
4747
#endif
4848
#include <asm/feature-fixups.h>
49+
#include <asm/kup.h>
4950

5051
/*
5152
* System calls.
@@ -120,6 +121,9 @@ END_BTB_FLUSH_SECTION
120121
addi r9,r1,STACK_FRAME_OVERHEAD
121122
ld r11,exception_marker@toc(r2)
122123
std r11,-16(r9) /* "regshere" marker */
124+
125+
kuap_check_amr r10, r11
126+
123127
#if defined(CONFIG_VIRT_CPU_ACCOUNTING_NATIVE) && defined(CONFIG_PPC_SPLPAR)
124128
BEGIN_FW_FTR_SECTION
125129
beq 33f
@@ -275,6 +279,8 @@ END_FTR_SECTION_IFCLR(CPU_FTR_STCX_CHECKS_ADDRESS)
275279
andi. r6,r8,MSR_PR
276280
ld r4,_LINK(r1)
277281

282+
kuap_check_amr r10, r11
283+
278284
#ifdef CONFIG_PPC_BOOK3S
279285
/*
280286
* Clear MSR_RI, MSR_EE is already and remains disabled. We could do
@@ -296,6 +302,10 @@ END_FTR_SECTION_IFSET(CPU_FTR_HAS_PPR)
296302
std r8, PACATMSCRATCH(r13)
297303
#endif
298304

305+
/*
306+
* We don't need to restore AMR on the way back to userspace for KUAP.
307+
* The value of AMR only matters while we're in the kernel.
308+
*/
299309
ld r13,GPR13(r1) /* only restore r13 if returning to usermode */
300310
ld r2,GPR2(r1)
301311
ld r1,GPR1(r1)
@@ -306,8 +316,10 @@ END_FTR_SECTION_IFSET(CPU_FTR_HAS_PPR)
306316
RFI_TO_USER
307317
b . /* prevent speculative execution */
308318

309-
/* exit to kernel */
310-
1: ld r2,GPR2(r1)
319+
1: /* exit to kernel */
320+
kuap_restore_amr r2
321+
322+
ld r2,GPR2(r1)
311323
ld r1,GPR1(r1)
312324
mtlr r4
313325
mtcr r5
@@ -594,6 +606,8 @@ _GLOBAL(_switch)
594606
std r23,_CCR(r1)
595607
std r1,KSP(r3) /* Set old stack pointer */
596608

609+
kuap_check_amr r9, r10
610+
597611
FLUSH_COUNT_CACHE
598612

599613
/*
@@ -942,6 +956,8 @@ fast_exception_return:
942956
ld r4,_XER(r1)
943957
mtspr SPRN_XER,r4
944958

959+
kuap_check_amr r5, r6
960+
945961
REST_8GPRS(5, r1)
946962

947963
andi. r0,r3,MSR_RI
@@ -974,6 +990,10 @@ END_FTR_SECTION_IFSET(CPU_FTR_HAS_PPR)
974990
ACCOUNT_CPU_USER_EXIT(r13, r2, r4)
975991
REST_GPR(13, r1)
976992

993+
/*
994+
* We don't need to restore AMR on the way back to userspace for KUAP.
995+
* The value of AMR only matters while we're in the kernel.
996+
*/
977997
mtspr SPRN_SRR1,r3
978998

979999
ld r2,_CCR(r1)
@@ -1006,6 +1026,9 @@ END_FTR_SECTION_IFSET(CPU_FTR_HAS_PPR)
10061026
ld r0,GPR0(r1)
10071027
ld r2,GPR2(r1)
10081028
ld r3,GPR3(r1)
1029+
1030+
kuap_restore_amr r4
1031+
10091032
ld r4,GPR4(r1)
10101033
ld r1,GPR1(r1)
10111034
RFI_TO_KERNEL

arch/powerpc/kernel/exceptions-64s.S

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <asm/cpuidle.h>
2020
#include <asm/head-64.h>
2121
#include <asm/feature-fixups.h>
22+
#include <asm/kup.h>
2223

2324
/*
2425
* There are a few constraints to be concerned with.
@@ -309,6 +310,7 @@ TRAMP_REAL_BEGIN(machine_check_common_early)
309310
mfspr r11,SPRN_DSISR /* Save DSISR */
310311
std r11,_DSISR(r1)
311312
std r9,_CCR(r1) /* Save CR in stackframe */
313+
kuap_save_amr_and_lock r9, r10, cr1
312314
/* Save r9 through r13 from EXMC save area to stack frame. */
313315
EXCEPTION_PROLOG_COMMON_2(PACA_EXMC)
314316
mfmsr r11 /* get MSR value */
@@ -1109,6 +1111,7 @@ TRAMP_REAL_BEGIN(hmi_exception_early)
11091111
mfspr r11,SPRN_HSRR0 /* Save HSRR0 */
11101112
mfspr r12,SPRN_HSRR1 /* Save HSRR1 */
11111113
EXCEPTION_PROLOG_COMMON_1()
1114+
/* We don't touch AMR here, we never go to virtual mode */
11121115
EXCEPTION_PROLOG_COMMON_2(PACA_EXGEN)
11131116
EXCEPTION_PROLOG_COMMON_3(0xe60)
11141117
addi r3,r1,STACK_FRAME_OVERHEAD

arch/powerpc/mm/pgtable-radix.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include <asm/powernv.h>
3030
#include <asm/sections.h>
3131
#include <asm/trace.h>
32+
#include <asm/uaccess.h>
3233

3334
#include <trace/events/thp.h>
3435

@@ -549,6 +550,24 @@ void setup_kuep(bool disabled)
549550
}
550551
#endif
551552

553+
#ifdef CONFIG_PPC_KUAP
554+
void setup_kuap(bool disabled)
555+
{
556+
if (disabled || !early_radix_enabled())
557+
return;
558+
559+
if (smp_processor_id() == boot_cpuid) {
560+
pr_info("Activating Kernel Userspace Access Prevention\n");
561+
cur_cpu_spec->mmu_features |= MMU_FTR_RADIX_KUAP;
562+
}
563+
564+
/* Make sure userspace can't change the AMR */
565+
mtspr(SPRN_UAMOR, 0);
566+
mtspr(SPRN_AMR, AMR_KUAP_BLOCKED);
567+
isync();
568+
}
569+
#endif
570+
552571
void __init radix__early_init_mmu(void)
553572
{
554573
unsigned long lpcr;

arch/powerpc/mm/pkeys.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include <asm/mman.h>
99
#include <asm/mmu_context.h>
10+
#include <asm/mmu.h>
1011
#include <asm/setup.h>
1112
#include <linux/pkeys.h>
1213
#include <linux/of_device.h>

arch/powerpc/platforms/Kconfig.cputype

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,7 @@ config PPC_RADIX_MMU
327327
depends on PPC_BOOK3S_64
328328
select ARCH_HAS_GIGANTIC_PAGE if (MEMORY_ISOLATION && COMPACTION) || CMA
329329
select PPC_HAVE_KUEP
330+
select PPC_HAVE_KUAP
330331
default y
331332
help
332333
Enable support for the Power ISA 3.0 Radix style MMU. Currently this
@@ -370,6 +371,13 @@ config PPC_KUAP
370371

371372
If you're unsure, say Y.
372373

374+
config PPC_KUAP_DEBUG
375+
bool "Extra debugging for Kernel Userspace Access Protection"
376+
depends on PPC_HAVE_KUAP && PPC_RADIX_MMU
377+
help
378+
Add extra debugging for Kernel Userspace Access Protection (KUAP)
379+
If you're unsure, say N.
380+
373381
config ARCH_ENABLE_HUGEPAGE_MIGRATION
374382
def_bool y
375383
depends on PPC_BOOK3S_64 && HUGETLB_PAGE && MIGRATION

0 commit comments

Comments
 (0)