powerpc/32: Add KASAN support
authorChristophe Leroy <christophe.leroy@c-s.fr>
Fri, 26 Apr 2019 16:23:34 +0000 (16:23 +0000)
committerMichael Ellerman <mpe@ellerman.id.au>
Thu, 2 May 2019 15:20:26 +0000 (01:20 +1000)
This patch adds KASAN support for PPC32. The following patch
will add an early activation of hash table for book3s. Until
then, a warning will be raised if trying to use KASAN on an
hash 6xx.

To support KASAN, this patch initialises that MMU mapings for
accessing to the KASAN shadow area defined in a previous patch.

An early mapping is set as soon as the kernel code has been
relocated at its definitive place.

Then the definitive mapping is set once paging is initialised.

For modules, the shadow area is allocated at module_alloc().

Signed-off-by: Christophe Leroy <christophe.leroy@c-s.fr>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
arch/powerpc/Kconfig
arch/powerpc/include/asm/kasan.h
arch/powerpc/kernel/head_32.S
arch/powerpc/kernel/head_40x.S
arch/powerpc/kernel/head_44x.S
arch/powerpc/kernel/head_8xx.S
arch/powerpc/kernel/head_fsl_booke.S
arch/powerpc/kernel/setup-common.c
arch/powerpc/mm/Makefile
arch/powerpc/mm/init_32.c
arch/powerpc/mm/kasan/kasan_init_32.c [new file with mode: 0644]

index 515bd10d32f6d693a0679904724b910f92eb4d67..2711aac24621bc1d14cbf12e1b1b98affae9b09d 100644 (file)
@@ -173,6 +173,7 @@ config PPC
        select GENERIC_TIME_VSYSCALL
        select HAVE_ARCH_AUDITSYSCALL
        select HAVE_ARCH_JUMP_LABEL
+       select HAVE_ARCH_KASAN                  if PPC32
        select HAVE_ARCH_KGDB
        select HAVE_ARCH_MMAP_RND_BITS
        select HAVE_ARCH_MMAP_RND_COMPAT_BITS   if COMPAT
index 05274dea310910c333c86771ca9fee4b077f6c6e..296e51c2f066b09dbebf6cedb1d88ef617cbd29d 100644 (file)
 
 #define KASAN_SHADOW_SIZE      (KASAN_SHADOW_END - KASAN_SHADOW_START)
 
+#ifdef CONFIG_KASAN
+void kasan_early_init(void);
+void kasan_mmu_init(void);
+void kasan_init(void);
+#else
+static inline void kasan_init(void) { }
+static inline void kasan_mmu_init(void) { }
+#endif
+
 #endif /* __ASSEMBLY */
 #endif
index 40aec3f00a0523e6ca68c0437ca356ee2bde7cbc..6e85171e513c821fb69fa5a0ee1d350444e7d1f8 100644 (file)
@@ -969,6 +969,9 @@ start_here:
  * Do early platform-specific initialization,
  * and set up the MMU.
  */
+#ifdef CONFIG_KASAN
+       bl      kasan_early_init
+#endif
        li      r3,0
        mr      r4,r31
        bl      machine_init
index a9c934f2319b5418dbff6b913a4e4e1635ca5a92..efa219d2136e74a77eb0882395c305d523aadbc2 100644 (file)
@@ -848,6 +848,9 @@ start_here:
 /*
  * Decide what sort of machine this is and initialize the MMU.
  */
+#ifdef CONFIG_KASAN
+       bl      kasan_early_init
+#endif
        li      r3,0
        mr      r4,r31
        bl      machine_init
index 37117ab11584c7a06a9c2ccc86b2fd3a70f278c4..34a5df827b38e58b10d5511b3bb958dd34a8809f 100644 (file)
@@ -203,6 +203,9 @@ _ENTRY(_start);
 /*
  * Decide what sort of machine this is and initialize the MMU.
  */
+#ifdef CONFIG_KASAN
+       bl      kasan_early_init
+#endif
        li      r3,0
        mr      r4,r31
        bl      machine_init
index 03c73b4c6435974278f0f3a07ec20981704403ce..d25adb6ef23507ab89a6a0b2cae1144f14615143 100644 (file)
@@ -853,6 +853,9 @@ start_here:
 /*
  * Decide what sort of machine this is and initialize the MMU.
  */
+#ifdef CONFIG_KASAN
+       bl      kasan_early_init
+#endif
        li      r3,0
        mr      r4,r31
        bl      machine_init
index 32332e24e4216402d21787f883c498fa2bd8e57f..567e0ed45ca8171a6abb4f09815339bd870b48f2 100644 (file)
@@ -268,6 +268,9 @@ set_ivor:
 /*
  * Decide what sort of machine this is and initialize the MMU.
  */
+#ifdef CONFIG_KASAN
+       bl      kasan_early_init
+#endif
        mr      r3,r30
        mr      r4,r31
        bl      machine_init
index 19d68a9b5f372a233efe5591016b444c3a5a58cc..91d2c6970bdb9d84d2a7bc835b61fe1b175239f9 100644 (file)
@@ -67,6 +67,7 @@
 #include <asm/livepatch.h>
 #include <asm/mmu_context.h>
 #include <asm/cpu_has_feature.h>
+#include <asm/kasan.h>
 
 #include "setup.h"
 
@@ -871,6 +872,8 @@ static void smp_setup_pacas(void)
  */
 void __init setup_arch(char **cmdline_p)
 {
+       kasan_init();
+
        *cmdline_p = boot_command_line;
 
        /* Set a half-reasonable default so udelay does something sensible */
index 62735f335bce394ac7d7bb591e152807a5b9b15b..d8c0ce9b255767903fba87d24e8f740039c499f0 100644 (file)
@@ -26,3 +26,4 @@ obj-$(CONFIG_NOT_COHERENT_CACHE) += dma-noncoherent.o
 obj-$(CONFIG_HIGHMEM)          += highmem.o
 obj-$(CONFIG_PPC_COPRO_BASE)   += copro_fault.o
 obj-$(CONFIG_PPC_PTDUMP)       += ptdump/
+obj-$(CONFIG_KASAN)            += kasan/
index 3eb4cb09749c68f3da26e8edeab46e1b6265942e..c3121b6c8cbdf937ca9d2209a21d2bdd9b30503d 100644 (file)
@@ -46,6 +46,7 @@
 #include <asm/sections.h>
 #include <asm/hugetlb.h>
 #include <asm/kup.h>
+#include <asm/kasan.h>
 
 #include <mm/mmu_decl.h>
 
@@ -179,6 +180,8 @@ void __init MMU_init(void)
        btext_unmap();
 #endif
 
+       kasan_mmu_init();
+
        setup_kup();
 
        /* Shortly after that, the entire linear mapping will be available */
diff --git a/arch/powerpc/mm/kasan/kasan_init_32.c b/arch/powerpc/mm/kasan/kasan_init_32.c
new file mode 100644 (file)
index 0000000..42617fc
--- /dev/null
@@ -0,0 +1,156 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#define DISABLE_BRANCH_PROFILING
+
+#include <linux/kasan.h>
+#include <linux/printk.h>
+#include <linux/memblock.h>
+#include <linux/sched/task.h>
+#include <linux/vmalloc.h>
+#include <asm/pgalloc.h>
+#include <asm/code-patching.h>
+#include <mm/mmu_decl.h>
+
+static void kasan_populate_pte(pte_t *ptep, pgprot_t prot)
+{
+       unsigned long va = (unsigned long)kasan_early_shadow_page;
+       phys_addr_t pa = __pa(kasan_early_shadow_page);
+       int i;
+
+       for (i = 0; i < PTRS_PER_PTE; i++, ptep++)
+               __set_pte_at(&init_mm, va, ptep, pfn_pte(PHYS_PFN(pa), prot), 0);
+}
+
+static int kasan_init_shadow_page_tables(unsigned long k_start, unsigned long k_end)
+{
+       pmd_t *pmd;
+       unsigned long k_cur, k_next;
+
+       pmd = pmd_offset(pud_offset(pgd_offset_k(k_start), k_start), k_start);
+
+       for (k_cur = k_start; k_cur != k_end; k_cur = k_next, pmd++) {
+               pte_t *new;
+
+               k_next = pgd_addr_end(k_cur, k_end);
+               if ((void *)pmd_page_vaddr(*pmd) != kasan_early_shadow_pte)
+                       continue;
+
+               new = pte_alloc_one_kernel(&init_mm);
+
+               if (!new)
+                       return -ENOMEM;
+               kasan_populate_pte(new, PAGE_KERNEL_RO);
+               pmd_populate_kernel(&init_mm, pmd, new);
+       }
+       return 0;
+}
+
+static void __ref *kasan_get_one_page(void)
+{
+       if (slab_is_available())
+               return (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO);
+
+       return memblock_alloc(PAGE_SIZE, PAGE_SIZE);
+}
+
+static int __ref kasan_init_region(void *start, size_t size)
+{
+       unsigned long k_start = (unsigned long)kasan_mem_to_shadow(start);
+       unsigned long k_end = (unsigned long)kasan_mem_to_shadow(start + size);
+       unsigned long k_cur;
+       int ret;
+       void *block = NULL;
+
+       ret = kasan_init_shadow_page_tables(k_start, k_end);
+       if (ret)
+               return ret;
+
+       if (!slab_is_available())
+               block = memblock_alloc(k_end - k_start, PAGE_SIZE);
+
+       for (k_cur = k_start; k_cur < k_end; k_cur += PAGE_SIZE) {
+               pmd_t *pmd = pmd_offset(pud_offset(pgd_offset_k(k_cur), k_cur), k_cur);
+               void *va = block ? block + k_cur - k_start : kasan_get_one_page();
+               pte_t pte = pfn_pte(PHYS_PFN(__pa(va)), PAGE_KERNEL);
+
+               if (!va)
+                       return -ENOMEM;
+
+               __set_pte_at(&init_mm, k_cur, pte_offset_kernel(pmd, k_cur), pte, 0);
+       }
+       flush_tlb_kernel_range(k_start, k_end);
+       return 0;
+}
+
+static void __init kasan_remap_early_shadow_ro(void)
+{
+       kasan_populate_pte(kasan_early_shadow_pte, PAGE_KERNEL_RO);
+
+       flush_tlb_kernel_range(KASAN_SHADOW_START, KASAN_SHADOW_END);
+}
+
+void __init kasan_mmu_init(void)
+{
+       int ret;
+       struct memblock_region *reg;
+
+       for_each_memblock(memory, reg) {
+               phys_addr_t base = reg->base;
+               phys_addr_t top = min(base + reg->size, total_lowmem);
+
+               if (base >= top)
+                       continue;
+
+               ret = kasan_init_region(__va(base), top - base);
+               if (ret)
+                       panic("kasan: kasan_init_region() failed");
+       }
+}
+
+void __init kasan_init(void)
+{
+       kasan_remap_early_shadow_ro();
+
+       clear_page(kasan_early_shadow_page);
+
+       /* At this point kasan is fully initialized. Enable error messages */
+       init_task.kasan_depth = 0;
+       pr_info("KASAN init done\n");
+}
+
+#ifdef CONFIG_MODULES
+void *module_alloc(unsigned long size)
+{
+       void *base = vmalloc_exec(size);
+
+       if (!base)
+               return NULL;
+
+       if (!kasan_init_region(base, size))
+               return base;
+
+       vfree(base);
+
+       return NULL;
+}
+#endif
+
+void __init kasan_early_init(void)
+{
+       unsigned long addr = KASAN_SHADOW_START;
+       unsigned long end = KASAN_SHADOW_END;
+       unsigned long next;
+       pmd_t *pmd = pmd_offset(pud_offset(pgd_offset_k(addr), addr), addr);
+
+       BUILD_BUG_ON(KASAN_SHADOW_START & ~PGDIR_MASK);
+
+       kasan_populate_pte(kasan_early_shadow_pte, PAGE_KERNEL);
+
+       do {
+               next = pgd_addr_end(addr, end);
+               pmd_populate_kernel(&init_mm, pmd, kasan_early_shadow_pte);
+       } while (pmd++, addr = next, addr != end);
+
+       if (early_mmu_has_feature(MMU_FTR_HPTE_TABLE))
+               WARN(true, "KASAN not supported on hash 6xx");
+}