powerpc/32s: Implement Kernel Userspace Execution Prevention.
authorChristophe Leroy <christophe.leroy@c-s.fr>
Mon, 11 Mar 2019 08:30:35 +0000 (08:30 +0000)
committerMichael Ellerman <mpe@ellerman.id.au>
Sun, 21 Apr 2019 13:11:46 +0000 (23:11 +1000)
To implement Kernel Userspace Execution Prevention, this patch
sets NX bit on all user segments on kernel entry and clears NX bit
on all user segments on kernel exit.

Note that powerpc 601 doesn't have the NX bit, so KUEP will not
work on it. A warning is displayed at startup.

Signed-off-by: Christophe Leroy <christophe.leroy@c-s.fr>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
arch/powerpc/include/asm/book3s/32/kup.h [new file with mode: 0644]
arch/powerpc/include/asm/book3s/32/mmu-hash.h
arch/powerpc/include/asm/kup.h
arch/powerpc/kernel/entry_32.S
arch/powerpc/kernel/head_32.S
arch/powerpc/mm/ppc_mmu_32.c
arch/powerpc/platforms/Kconfig.cputype

diff --git a/arch/powerpc/include/asm/book3s/32/kup.h b/arch/powerpc/include/asm/book3s/32/kup.h
new file mode 100644 (file)
index 0000000..5f97c74
--- /dev/null
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_POWERPC_BOOK3S_32_KUP_H
+#define _ASM_POWERPC_BOOK3S_32_KUP_H
+
+#include <asm/book3s/32/mmu-hash.h>
+
+#ifdef __ASSEMBLY__
+
+.macro kuep_update_sr  gpr1, gpr2              /* 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 */
+       bdnz    101b
+       isync
+.endm
+
+.macro kuep_lock       gpr1, gpr2
+#ifdef CONFIG_PPC_KUEP
+       li      \gpr1, NUM_USER_SEGMENTS
+       li      \gpr2, 0
+       mtctr   \gpr1
+       mfsrin  \gpr1, \gpr2
+       oris    \gpr1, \gpr1, SR_NX@h           /* set Nx */
+       kuep_update_sr \gpr1, \gpr2
+#endif
+.endm
+
+.macro kuep_unlock     gpr1, gpr2
+#ifdef CONFIG_PPC_KUEP
+       li      \gpr1, NUM_USER_SEGMENTS
+       li      \gpr2, 0
+       mtctr   \gpr1
+       mfsrin  \gpr1, \gpr2
+       rlwinm  \gpr1, \gpr1, 0, ~SR_NX         /* Clear Nx */
+       kuep_update_sr \gpr1, \gpr2
+#endif
+.endm
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* _ASM_POWERPC_BOOK3S_32_KUP_H */
index 5cb588395fdcfdc7044dcecd0b3bcf9ecd89d73c..8c5727a322b1364c7e8e755a42c9af7ca35bb59e 100644 (file)
@@ -63,6 +63,9 @@ typedef pte_t *pgtable_t;
 #define PP_RWRW 2      /* Supervisor read/write, User read/write */
 #define PP_RXRX 3      /* Supervisor read,       User read */
 
+/* Values for Segment Registers */
+#define SR_NX  0x10000000      /* No Execute */
+
 #ifndef __ASSEMBLY__
 
 /*
index 043c800ec5fbb7df8a9804e90598767641decadb..5b5e39643a27ca434f272323395eb690e4734aee 100644 (file)
@@ -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
index 1182bf603d3c53de52579bacdcdea83380229a89..2f3d159c11d79499077442aa8b62ac43e98710f1 100644 (file)
@@ -162,6 +162,9 @@ transfer_to_handler:
        andis.  r12,r12,DBCR0_IDM@h
 #endif
        ACCOUNT_CPU_USER_ENTRY(r2, r11, r12)
+#ifdef CONFIG_PPC_BOOK3S_32
+       kuep_lock r11, r12
+#endif
 #if defined(CONFIG_40x) || defined(CONFIG_BOOKE)
        beq+    3f
        /* From user and task is ptraced - load up global dbcr0 */
@@ -427,6 +430,9 @@ BEGIN_FTR_SECTION
 END_FTR_SECTION_IFSET(CPU_FTR_NEED_PAIRED_STWCX)
        stwcx.  r0,0,r1                 /* to clear the reservation */
        ACCOUNT_CPU_USER_EXIT(r2, r5, r7)
+#ifdef CONFIG_PPC_BOOK3S_32
+       kuep_unlock r5, r7
+#endif
        kuap_check r2, r4
        lwz     r4,_LINK(r1)
        lwz     r5,_CCR(r1)
@@ -821,6 +827,9 @@ restore_user:
        bnel-   load_dbcr0
 #endif
        ACCOUNT_CPU_USER_EXIT(r2, r10, r11)
+#ifdef CONFIG_PPC_BOOK3S_32
+       kuep_unlock     r10, r11
+#endif
 
        b       restore
 
index e25b615e9f9e642d34e9387aac7db652131a466f..19b46cb9f623e0dd90c467eaec530579d22dcc5c 100644 (file)
@@ -896,14 +896,24 @@ load_up_mmu:
        tophys(r6,r6)
        lwz     r6,_SDR1@l(r6)
        mtspr   SPRN_SDR1,r6
-       li      r0,16           /* load up segment register values */
+       li      r0, NUM_USER_SEGMENTS   /* load up segment register values */
        mtctr   r0              /* for context 0 */
        lis     r3,0x2000       /* Ku = 1, VSID = 0 */
+#ifdef CONFIG_PPC_KUEP
+       oris    r3, r3, SR_NX@h /* Set Nx */
+#endif
        li      r4,0
 3:     mtsrin  r3,r4
        addi    r3,r3,0x111     /* increment VSID */
        addis   r4,r4,0x1000    /* address of next segment */
        bdnz    3b
+       li      r0, 16 - NUM_USER_SEGMENTS /* load up kernel segment registers */
+       mtctr   r0                      /* for context 0 */
+       rlwinm  r3, r3, 0, ~SR_NX       /* Nx = 0 */
+3:     mtsrin  r3, r4
+       addi    r3, r3, 0x111   /* increment VSID */
+       addis   r4, r4, 0x1000  /* address of next segment */
+       bdnz    3b
 
 /* Load the BAT registers with the values set up by MMU_init.
    MMU_init takes care of whether we're on a 601 or not. */
@@ -1007,6 +1017,9 @@ _ENTRY(switch_mmu_context)
        mulli   r3,r3,897       /* multiply context by skew factor */
        rlwinm  r3,r3,4,8,27    /* VSID = (context & 0xfffff) << 4 */
        addis   r3,r3,0x6000    /* Set Ks, Ku bits */
+#ifdef CONFIG_PPC_KUEP
+       oris    r3, r3, SR_NX@h /* Set Nx */
+#endif
        li      r0,NUM_USER_SEGMENTS
        mtctr   r0
 
index f29d2f118b444aa6b060bcfa6fab6fb0bf321949..baa75673b1f52ed1001d043c0a53d8314511e7a9 100644 (file)
@@ -394,3 +394,16 @@ 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_KUEP
+void __init setup_kuep(bool disabled)
+{
+       pr_info("Activating Kernel Userspace Execution Prevention\n");
+
+       if (cpu_has_feature(CPU_FTR_601))
+               pr_warn("KUEP is not working on powerpc 601 (No NX bit in Seg Regs)\n");
+
+       if (disabled)
+               pr_warn("KUEP cannot be disabled yet on 6xx when compiled in\n");
+}
+#endif
index ab586963893a2003a48d12d624a90857d0119efe..6bc0a4c08c1c4bb06713f4563f80f065ef549c2c 100644 (file)
@@ -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_KUEP
 
 config PPC_85xx
        bool "Freescale 85xx"