[PATCH 7/7] powerpc/book3s32: Implement Kernel Userspace Access Protection

christophe leroy christophe.leroy at c-s.fr
Sun Mar 10 00:04:49 AEDT 2019


Hi Russel,

As you can see in patchwork, snowpatch report 3477 new sparse warning(s) 
with this series.

Half of them are because of a cast of __user pointer to u32. I'll fix it 
by adding __force to the cast.

The other half comes from the use of min() macro. The induced warning is 
"warning: expression using sizeof(void)". This is because of a bug in 
sparse.

The above bug is fixed in sparse 0.6.0. Could you update snowpatch to 
use that version ?

Thanks
Christophe

Le 05/03/2019 à 22:18, Christophe Leroy a écrit :
> This patch implements Kernel Userspace Access Protection for
> book3s/32.
> 
> Due to limitations of the processor page protection capabilities,
> the protection is only against writing. read protection cannot be
> achieved using page protection.
> 
> The previous patch modifies the page protection so that RW user
> pages are RW for Key 0 and RO for Key 1, and it sets Key 0 for
> both user and kernel.
> 
> This patch changes userspace segment registers are set to Ku 0
> and Ks 1. When kernel needs to write to RW pages, the associated
> segment register is then changed to Ks 0 in order to allow write
> access to the kernel.
> 
> In order to avoid having the read all segment registers when
> locking/unlocking the access, some data is kept in the thread_struct
> and saved on stack on exceptions. The field identifies both the
> first unlocked segment and the first segment following the last
> unlocked one. When no segment is unlocked, it contains value 0.
> 
> Signed-off-by: Christophe Leroy <christophe.leroy at c-s.fr>
> ---
>   arch/powerpc/include/asm/book3s/32/kup.h | 96 ++++++++++++++++++++++++++++++++
>   arch/powerpc/include/asm/kup.h           |  3 +
>   arch/powerpc/include/asm/processor.h     |  3 +
>   arch/powerpc/kernel/asm-offsets.c        |  3 +
>   arch/powerpc/kernel/head_32.S            |  7 +++
>   arch/powerpc/mm/ppc_mmu_32.c             | 10 ++++
>   arch/powerpc/platforms/Kconfig.cputype   |  1 +
>   7 files changed, 123 insertions(+)
>   create mode 100644 arch/powerpc/include/asm/book3s/32/kup.h
> 
> diff --git a/arch/powerpc/include/asm/book3s/32/kup.h b/arch/powerpc/include/asm/book3s/32/kup.h
> new file mode 100644
> index 000000000000..3f02db633849
> --- /dev/null
> +++ b/arch/powerpc/include/asm/book3s/32/kup.h
> @@ -0,0 +1,96 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef _ASM_POWERPC_BOOK3S_32_KUP_H
> +#define _ASM_POWERPC_BOOK3S_32_KUP_H
> +
> +#ifdef CONFIG_PPC_KUAP
> +
> +#include <asm/book3s/32/mmu-hash.h>
> +
> +#ifdef __ASSEMBLY__
> +
> +.macro kuap_update_sr	gpr1, gpr2, gpr3	/* NEVER use r0 as gpr2 due to addis */
> +101:	mtsrin	\gpr1, \gpr2
> +	addi	\gpr1, \gpr1, 0x111		/* next VSID */
> +	rlwinm	\gpr1, \gpr1, 0, 0xf0ffffff	/* clear VSID overflow */
> +	addis	\gpr2, \gpr2, 0x1000		/* address of next segment */
> +	cmplw	\gpr2, \gpr3
> +	blt-	101b
> +.endm
> +
> +.macro kuap_save_and_lock	sp, thread, gpr1, gpr2, gpr3
> +	lwz	\gpr2, KUAP(\thread)
> +	rlwinm.	\gpr3, \gpr2, 28, 0xf0000000
> +	stw	\gpr2, STACK_REGS_KUAP(\sp)
> +	beq+	102f
> +	li	\gpr1, 0
> +	stw	\gpr1, KUAP(\thread)
> +	mfsrin	\gpr1, \gpr2
> +	oris	\gpr1, \gpr1, SR_KS at h	/* set Ks */
> +	kuap_update_sr	\gpr1, \gpr2, \gpr3
> +102:
> +.endm
> +
> +.macro kuap_restore	sp, current, gpr1, gpr2, gpr3
> +	lwz	\gpr2, STACK_REGS_KUAP(\sp)
> +	rlwinm.	\gpr3, \gpr2, 28, 0xf0000000
> +	stw	\gpr2, THREAD + KUAP(\current)
> +	beq+	102f
> +	mfsrin	\gpr1, \gpr2
> +	rlwinm	\gpr1, \gpr1, 0, ~SR_KS	/* Clear Ks */
> +	kuap_update_sr	\gpr1, \gpr2, \gpr3
> +102:
> +.endm
> +
> +.macro kuap_check	current, gpr
> +#ifdef CONFIG_PPC_KUAP_DEBUG
> +	lwz	\gpr2, KUAP(thread)
> +999:	twnei	\gpr, 0
> +	EMIT_BUG_ENTRY 999b, __FILE__, __LINE__, (BUGFLAG_WARNING | BUGFLAG_ONCE)
> +#endif
> +.endm
> +
> +#else /* !__ASSEMBLY__ */
> +
> +#include <linux/sched.h>
> +
> +static inline void kuap_update_sr(u32 sr, u32 addr, u32 end)
> +{
> +	barrier();	/* make sure thread.kuap is updated before playing with SRs */
> +	while (addr < end) {
> +		mtsrin(sr, addr);
> +		sr += 0x111;		/* next VSID */
> +		sr &= 0xf0ffffff;	/* clear VSID overflow */
> +		addr += 0x10000000;	/* address of next segment */
> +	}
> +	isync();	/* Context sync required after mtsrin() */
> +}
> +
> +static inline void allow_user_access(void __user *to, const void __user *from, u32 size)
> +{
> +	u32 addr = (u32)to;
> +	u32 end = min(addr + size, TASK_SIZE);
> +
> +	if (!addr || addr >= TASK_SIZE || !size)
> +		return;
> +
> +	current->thread.kuap = (addr & 0xf0000000) | ((((end - 1) >> 28) + 1) & 0xf);
> +	kuap_update_sr(mfsrin(addr) & ~SR_KS, addr, end);	/* Clear Ks */
> +}
> +
> +static inline void prevent_user_access(void __user *to, const void __user *from, u32 size)
> +{
> +	u32 addr = (u32)to;
> +	u32 end = min(addr + size, TASK_SIZE);
> +
> +	if (!addr || addr >= TASK_SIZE || !size)
> +		return;
> +
> +	current->thread.kuap = 0;
> +	kuap_update_sr(mfsrin(addr) | SR_KS, addr, end);	/* set Ks */
> +}
> +
> +#endif /* !__ASSEMBLY__ */
> +
> +#endif /* CONFIG_PPC_KUAP */
> +
> +#endif /* _ASM_POWERPC_BOOK3S_32_KUP_H */
> diff --git a/arch/powerpc/include/asm/kup.h b/arch/powerpc/include/asm/kup.h
> index 2ab9e904c22c..2f42e8c19506 100644
> --- a/arch/powerpc/include/asm/kup.h
> +++ b/arch/powerpc/include/asm/kup.h
> @@ -8,6 +8,9 @@
>   #ifdef CONFIG_PPC_8xx
>   #include <asm/nohash/32/kup-8xx.h>
>   #endif
> +#ifdef CONFIG_PPC_BOOK3S_32
> +#include <asm/book3s/32/kup.h>
> +#endif
>   
>   #ifdef __ASSEMBLY__
>   #ifndef CONFIG_PPC_KUAP
> diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h
> index 3351bcf42f2d..540949b397d4 100644
> --- a/arch/powerpc/include/asm/processor.h
> +++ b/arch/powerpc/include/asm/processor.h
> @@ -164,6 +164,9 @@ struct thread_struct {
>   	unsigned long	rtas_sp;	/* stack pointer for when in RTAS */
>   #endif
>   #endif
> +#if defined(CONFIG_PPC_BOOK3S_32) && defined(CONFIG_PPC_KUAP)
> +	unsigned long	kuap;		/* opened segments for user access */
> +#endif
>   	/* Debug Registers */
>   	struct debug_reg debug;
>   	struct thread_fp_state	fp_state;
> diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c
> index 66202e02fee2..60b82198de7c 100644
> --- a/arch/powerpc/kernel/asm-offsets.c
> +++ b/arch/powerpc/kernel/asm-offsets.c
> @@ -147,6 +147,9 @@ int main(void)
>   #if defined(CONFIG_KVM) && defined(CONFIG_BOOKE)
>   	OFFSET(THREAD_KVM_VCPU, thread_struct, kvm_vcpu);
>   #endif
> +#if defined(CONFIG_PPC_BOOK3S_32) && defined(CONFIG_PPC_KUAP)
> +	OFFSET(KUAP, thread_struct, kuap);
> +#endif
>   
>   #ifdef CONFIG_PPC_TRANSACTIONAL_MEM
>   	OFFSET(PACATMSCRATCH, paca_struct, tm_scratch);
> diff --git a/arch/powerpc/kernel/head_32.S b/arch/powerpc/kernel/head_32.S
> index 46b633514422..d5dc62997267 100644
> --- a/arch/powerpc/kernel/head_32.S
> +++ b/arch/powerpc/kernel/head_32.S
> @@ -900,6 +900,9 @@ load_up_mmu:
>   	li	r0, NUM_USER_SEGMENTS /* load up user segment register values */
>   	mtctr	r0		/* for context 0 */
>   	li	r3, 0		/* Kp = 0, Ks = 0, VSID = 0 */
> +#ifdef CONFIG_PPC_KUAP
> +	oris	r3, r3, SR_KS at h	/* Set Ks */
> +#endif
>   	li	r4,0
>   3:	mtsrin	r3,r4
>   	addi	r3,r3,0x111	/* increment VSID */
> @@ -907,6 +910,7 @@ load_up_mmu:
>   	bdnz	3b
>   	li	r0, 16 - NUM_USER_SEGMENTS /* load up kernel segment registers */
>   	mtctr	r0			/* for context 0 */
> +	rlwinm	r3, r3, 0, ~SR_KS	/* Ks = 0 */
>   	oris	r3, r3, SR_KP at h		/* Kp = 1 */
>   3:	mtsrin	r3, r4
>   	addi	r3, r3, 0x111	/* increment VSID */
> @@ -1015,6 +1019,9 @@ _ENTRY(switch_mmu_context)
>   	blt-	4f
>   	mulli	r3,r3,897	/* multiply context by skew factor */
>   	rlwinm	r3,r3,4,8,27	/* VSID = (context & 0xfffff) << 4 */
> +#ifdef CONFIG_PPC_KUAP
> +	oris	r3, r3, SR_KS at h	/* Set Ks */
> +#endif
>   	li	r0,NUM_USER_SEGMENTS
>   	mtctr	r0
>   
> diff --git a/arch/powerpc/mm/ppc_mmu_32.c b/arch/powerpc/mm/ppc_mmu_32.c
> index 2d5b0d50fb31..417cf33aa5ba 100644
> --- a/arch/powerpc/mm/ppc_mmu_32.c
> +++ b/arch/powerpc/mm/ppc_mmu_32.c
> @@ -392,3 +392,13 @@ void setup_initial_memory_limit(phys_addr_t first_memblock_base,
>   	else /* Anything else has 256M mapped */
>   		memblock_set_current_limit(min_t(u64, first_memblock_size, 0x10000000));
>   }
> +
> +#ifdef CONFIG_PPC_KUAP
> +void __init setup_kuap(bool disabled)
> +{
> +	pr_info("Activating Kernel Userspace Access Protection\n");
> +
> +	if (disabled)
> +		pr_warn("KUAP cannot be disabled yet on 6xx when compiled in\n");
> +}
> +#endif
> diff --git a/arch/powerpc/platforms/Kconfig.cputype b/arch/powerpc/platforms/Kconfig.cputype
> index ab586963893a..e3a5cbcc7700 100644
> --- a/arch/powerpc/platforms/Kconfig.cputype
> +++ b/arch/powerpc/platforms/Kconfig.cputype
> @@ -25,6 +25,7 @@ config PPC_BOOK3S_32
>   	bool "512x/52xx/6xx/7xx/74xx/82xx/83xx/86xx"
>   	select PPC_FPU
>   	select PPC_HAVE_PMU_SUPPORT
> +	select PPC_HAVE_KUAP
>   
>   config PPC_85xx
>   	bool "Freescale 85xx"
> 

---
L'absence de virus dans ce courrier électronique a été vérifiée par le logiciel antivirus Avast.
https://www.avast.com/antivirus



More information about the Linuxppc-dev mailing list