[PATCH 4/4] powerpc/64s: Implement KUAP for Radix MMU
Christophe Leroy
christophe.leroy at c-s.fr
Wed Nov 28 20:39:25 AEDT 2018
On 11/22/2018 02:04 PM, Russell Currey wrote:
> 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:
>
> - exiting the kernel and entering userspace
> - performing an operation like copy_{to/from}_user()
> - context switching to a process that has access enabled
>
> and similarly, access is disabled again when exiting userspace and entering
> the kernel.
>
> This feature has a slight performance impact which I roughly measured to be
> 3% slower in the worst case (performing 1GB of 1 byte read()/write()
> syscalls), and is gated behind the CONFIG_PPC_KUAP option for
> performance-critical builds.
>
> 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.
>
> Signed-off-by: Russell Currey <ruscur at russell.cc>
I squashed the paca thing into this one in RFC v2
Christophe
> ---
> arch/powerpc/include/asm/book3s/64/radix.h | 43 ++++++++++++++++++++++
> arch/powerpc/include/asm/exception-64e.h | 3 ++
> arch/powerpc/include/asm/exception-64s.h | 19 +++++++++-
> arch/powerpc/include/asm/mmu.h | 9 ++++-
> arch/powerpc/include/asm/reg.h | 1 +
> arch/powerpc/kernel/entry_64.S | 16 +++++++-
> arch/powerpc/mm/pgtable-radix.c | 12 ++++++
> arch/powerpc/mm/pkeys.c | 7 +++-
> arch/powerpc/platforms/Kconfig.cputype | 1 +
> 9 files changed, 105 insertions(+), 6 deletions(-)
>
> diff --git a/arch/powerpc/include/asm/book3s/64/radix.h b/arch/powerpc/include/asm/book3s/64/radix.h
> index 7d1a3d1543fc..9af93d05e6fa 100644
> --- a/arch/powerpc/include/asm/book3s/64/radix.h
> +++ b/arch/powerpc/include/asm/book3s/64/radix.h
> @@ -284,5 +284,48 @@ static inline unsigned long radix__get_tree_size(void)
> int radix__create_section_mapping(unsigned long start, unsigned long end, int nid);
> int radix__remove_section_mapping(unsigned long start, unsigned long end);
> #endif /* CONFIG_MEMORY_HOTPLUG */
> +
> +#ifdef CONFIG_PPC_KUAP
> +#include <asm/reg.h>
> +/*
> + * We do have the ability to individually lock/unlock reads and writes rather
> + * than both at once, however it's a significant performance hit due to needing
> + * to do a read-modify-write, which adds a mfspr, which is slow. As a result,
> + * locking/unlocking both at once is preferred.
> + */
> +static inline void __unlock_user_rd_access(void)
> +{
> + if (!mmu_has_feature(MMU_FTR_RADIX_KUAP))
> + return;
> +
> + mtspr(SPRN_AMR, 0);
> + isync();
> +}
> +
> +static inline void __lock_user_rd_access(void)
> +{
> + if (!mmu_has_feature(MMU_FTR_RADIX_KUAP))
> + return;
> +
> + mtspr(SPRN_AMR, AMR_LOCKED);
> +}
> +
> +static inline void __unlock_user_wr_access(void)
> +{
> + if (!mmu_has_feature(MMU_FTR_RADIX_KUAP))
> + return;
> +
> + mtspr(SPRN_AMR, 0);
> + isync();
> +}
> +
> +static inline void __lock_user_wr_access(void)
> +{
> + if (!mmu_has_feature(MMU_FTR_RADIX_KUAP))
> + return;
> +
> + mtspr(SPRN_AMR, AMR_LOCKED);
> +}
> +#endif /* CONFIG_PPC_KUAP */
> #endif /* __ASSEMBLY__ */
> #endif
> diff --git a/arch/powerpc/include/asm/exception-64e.h b/arch/powerpc/include/asm/exception-64e.h
> index 555e22d5e07f..bf25015834ee 100644
> --- a/arch/powerpc/include/asm/exception-64e.h
> +++ b/arch/powerpc/include/asm/exception-64e.h
> @@ -215,5 +215,8 @@ exc_##label##_book3e:
> #define RFI_TO_USER \
> rfi
>
> +#define UNLOCK_USER_ACCESS(reg)
> +#define LOCK_USER_ACCESS(reg)
> +
> #endif /* _ASM_POWERPC_EXCEPTION_64E_H */
>
> diff --git a/arch/powerpc/include/asm/exception-64s.h b/arch/powerpc/include/asm/exception-64s.h
> index 3b4767ed3ec5..d92614c66d87 100644
> --- a/arch/powerpc/include/asm/exception-64s.h
> +++ b/arch/powerpc/include/asm/exception-64s.h
> @@ -264,6 +264,19 @@ BEGIN_FTR_SECTION_NESTED(943) \
> std ra,offset(r13); \
> END_FTR_SECTION_NESTED(ftr,ftr,943)
>
> +#define LOCK_USER_ACCESS(reg) \
> +BEGIN_MMU_FTR_SECTION_NESTED(944) \
> + LOAD_REG_IMMEDIATE(reg,AMR_LOCKED); \
> + mtspr SPRN_AMR,reg; \
> +END_MMU_FTR_SECTION_NESTED(MMU_FTR_RADIX_KUAP,MMU_FTR_RADIX_KUAP,944)
> +
> +#define UNLOCK_USER_ACCESS(reg) \
> +BEGIN_MMU_FTR_SECTION_NESTED(945) \
> + li reg,0; \
> + mtspr SPRN_AMR,reg; \
> + isync; \
> +END_MMU_FTR_SECTION_NESTED(MMU_FTR_RADIX_KUAP,MMU_FTR_RADIX_KUAP,945)
> +
> #define EXCEPTION_PROLOG_0(area) \
> GET_PACA(r13); \
> std r9,area+EX_R9(r13); /* save r9 */ \
> @@ -500,7 +513,11 @@ END_FTR_SECTION_NESTED(ftr,ftr,943)
> beq 4f; /* if from kernel mode */ \
> ACCOUNT_CPU_USER_ENTRY(r13, r9, r10); \
> SAVE_PPR(area, r9); \
> -4: EXCEPTION_PROLOG_COMMON_2(area) \
> +4: lbz r9,PACA_USER_ACCESS_ALLOWED(r13); \
> + cmpwi cr1,r9,0; \
> + beq 5f; \
> + LOCK_USER_ACCESS(r9); \
> +5: EXCEPTION_PROLOG_COMMON_2(area) \
> EXCEPTION_PROLOG_COMMON_3(n) \
> ACCOUNT_STOLEN_TIME
>
> diff --git a/arch/powerpc/include/asm/mmu.h b/arch/powerpc/include/asm/mmu.h
> index 5631a906af55..a1450d56d0db 100644
> --- a/arch/powerpc/include/asm/mmu.h
> +++ b/arch/powerpc/include/asm/mmu.h
> @@ -107,6 +107,10 @@
> */
> #define MMU_FTR_1T_SEGMENT ASM_CONST(0x40000000)
>
> +/* Supports KUAP (key 0 controlling userspace addresses) on radix
> + */
> +#define MMU_FTR_RADIX_KUAP ASM_CONST(0x80000000)
> +
> /* MMU feature bit sets for various CPUs */
> #define MMU_FTRS_DEFAULT_HPTE_ARCH_V2 \
> MMU_FTR_HPTE_TABLE | MMU_FTR_PPCAS_ARCH_V2
> @@ -143,7 +147,10 @@ enum {
> MMU_FTR_KERNEL_RO | MMU_FTR_68_BIT_VA |
> #ifdef CONFIG_PPC_RADIX_MMU
> MMU_FTR_TYPE_RADIX |
> -#endif
> +#ifdef CONFIG_PPC_KUAP
> + MMU_FTR_RADIX_KUAP |
> +#endif /* CONFIG_PPC_KUAP */
> +#endif /* CONFIG_PPC_RADIX_MMU */
> 0,
> };
>
> diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h
> index de52c3166ba4..d9598e6790d8 100644
> --- a/arch/powerpc/include/asm/reg.h
> +++ b/arch/powerpc/include/asm/reg.h
> @@ -246,6 +246,7 @@
> #define SPRN_DSCR 0x11
> #define SPRN_CFAR 0x1c /* Come From Address Register */
> #define SPRN_AMR 0x1d /* Authority Mask Register */
> +#define AMR_LOCKED 0xC000000000000000UL /* Read & Write disabled */
> #define SPRN_UAMOR 0x9d /* User Authority Mask Override Register */
> #define SPRN_AMOR 0x15d /* Authority Mask Override Register */
> #define SPRN_ACOP 0x1F /* Available Coprocessor Register */
> diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S
> index 7b1693adff2a..d5879f32bd34 100644
> --- a/arch/powerpc/kernel/entry_64.S
> +++ b/arch/powerpc/kernel/entry_64.S
> @@ -297,7 +297,12 @@ END_FTR_SECTION_IFSET(CPU_FTR_HAS_PPR)
> b . /* prevent speculative execution */
>
> /* exit to kernel */
> -1: ld r2,GPR2(r1)
> +1: /* if the AMR was unlocked before, unlock it again */
> + lbz r2,PACA_USER_ACCESS_ALLOWED(r13)
> + cmpwi cr1,0
> + bne 2f
> + UNLOCK_USER_ACCESS(r2)
> +2: ld r2,GPR2(r1)
> ld r1,GPR1(r1)
> mtlr r4
> mtcr r5
> @@ -983,7 +988,14 @@ END_FTR_SECTION_IFSET(CPU_FTR_HAS_PPR)
> RFI_TO_USER
> b . /* prevent speculative execution */
>
> -1: mtspr SPRN_SRR1,r3
> +1: /* exit to kernel */
> + /* if the AMR was unlocked before, unlock it again */
> + lbz r2,PACA_USER_ACCESS_ALLOWED(r13)
> + cmpwi cr1,0
> + bne 2f
> + UNLOCK_USER_ACCESS(r2)
> +
> +2: mtspr SPRN_SRR1,r3
>
> ld r2,_CCR(r1)
> mtcrf 0xFF,r2
> diff --git a/arch/powerpc/mm/pgtable-radix.c b/arch/powerpc/mm/pgtable-radix.c
> index f08a459b4255..b064b542e09f 100644
> --- a/arch/powerpc/mm/pgtable-radix.c
> +++ b/arch/powerpc/mm/pgtable-radix.c
> @@ -29,6 +29,7 @@
> #include <asm/powernv.h>
> #include <asm/sections.h>
> #include <asm/trace.h>
> +#include <asm/uaccess.h>
>
> #include <trace/events/thp.h>
>
> @@ -550,6 +551,17 @@ void setup_kuep(bool disabled)
> mtspr(SPRN_IAMR, (1ul << 62));
> }
>
> +void setup_kuap(bool disabled)
> +{
> + if (disabled)
> + return;
> +
> + pr_warn("Activating Kernel Userspace Access Prevention\n");
> +
> + cur_cpu_spec->mmu_features |= MMU_FTR_RADIX_KUAP;
> + mtspr(SPRN_AMR, AMR_LOCKED);
> +}
> +
> void __init radix__early_init_mmu(void)
> {
> unsigned long lpcr;
> diff --git a/arch/powerpc/mm/pkeys.c b/arch/powerpc/mm/pkeys.c
> index b271b283c785..bb3cf915016f 100644
> --- a/arch/powerpc/mm/pkeys.c
> +++ b/arch/powerpc/mm/pkeys.c
> @@ -7,6 +7,7 @@
>
> #include <asm/mman.h>
> #include <asm/setup.h>
> +#include <asm/uaccess.h>
> #include <linux/pkeys.h>
> #include <linux/of_device.h>
>
> @@ -266,7 +267,8 @@ int __arch_set_user_pkey_access(struct task_struct *tsk, int pkey,
>
> void thread_pkey_regs_save(struct thread_struct *thread)
> {
> - if (static_branch_likely(&pkey_disabled))
> + if (static_branch_likely(&pkey_disabled) &&
> + !mmu_has_feature(MMU_FTR_RADIX_KUAP))
> return;
>
> /*
> @@ -280,7 +282,8 @@ void thread_pkey_regs_save(struct thread_struct *thread)
> void thread_pkey_regs_restore(struct thread_struct *new_thread,
> struct thread_struct *old_thread)
> {
> - if (static_branch_likely(&pkey_disabled))
> + if (static_branch_likely(&pkey_disabled) &&
> + !mmu_has_feature(MMU_FTR_RADIX_KUAP))
> return;
>
> if (old_thread->amr != new_thread->amr)
> diff --git a/arch/powerpc/platforms/Kconfig.cputype b/arch/powerpc/platforms/Kconfig.cputype
> index e6831d0ec159..5fbfa041194d 100644
> --- a/arch/powerpc/platforms/Kconfig.cputype
> +++ b/arch/powerpc/platforms/Kconfig.cputype
> @@ -335,6 +335,7 @@ config PPC_RADIX_MMU
> depends on PPC_BOOK3S_64
> select ARCH_HAS_GIGANTIC_PAGE if (MEMORY_ISOLATION && COMPACTION) || CMA
> select PPC_HAVE_KUEP
> + select PPC_HAVE_KUAP
> default y
> help
> Enable support for the Power ISA 3.0 Radix style MMU. Currently this
>
More information about the Linuxppc-dev
mailing list