Le 26/04/2019 à 18:23, Christophe Leroy a écrit :
> 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 <[email protected]>
> ---
> arch/powerpc/Kconfig | 1 +
> arch/powerpc/include/asm/kasan.h | 9 ++
> arch/powerpc/kernel/head_32.S | 3 +
> arch/powerpc/kernel/head_40x.S | 3 +
> arch/powerpc/kernel/head_44x.S | 3 +
> arch/powerpc/kernel/head_8xx.S | 3 +
> arch/powerpc/kernel/head_fsl_booke.S | 3 +
> arch/powerpc/kernel/setup-common.c | 3 +
> arch/powerpc/mm/Makefile | 1 +
> arch/powerpc/mm/init_32.c | 3 +
> arch/powerpc/mm/kasan/Makefile | 5 ++
Looks like the above Makefile is missing in powerpc/next ???
Christophe
> arch/powerpc/mm/kasan/kasan_init_32.c | 156 ++++++++++++++++++++++++++++++++++
> 12 files changed, 193 insertions(+)
> create mode 100644 arch/powerpc/mm/kasan/Makefile
> create mode 100644 arch/powerpc/mm/kasan/kasan_init_32.c
>
> diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
> index a7c80f2b08b5..1a2fb50126b2 100644
> --- a/arch/powerpc/Kconfig
> +++ b/arch/powerpc/Kconfig
> @@ -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
> diff --git a/arch/powerpc/include/asm/kasan.h b/arch/powerpc/include/asm/kasan.h
> index 05274dea3109..296e51c2f066 100644
> --- a/arch/powerpc/include/asm/kasan.h
> +++ b/arch/powerpc/include/asm/kasan.h
> @@ -27,5 +27,14 @@
>
> #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
> diff --git a/arch/powerpc/kernel/head_32.S b/arch/powerpc/kernel/head_32.S
> index 40aec3f00a05..6e85171e513c 100644
> --- a/arch/powerpc/kernel/head_32.S
> +++ b/arch/powerpc/kernel/head_32.S
> @@ -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
> diff --git a/arch/powerpc/kernel/head_40x.S b/arch/powerpc/kernel/head_40x.S
> index a9c934f2319b..efa219d2136e 100644
> --- a/arch/powerpc/kernel/head_40x.S
> +++ b/arch/powerpc/kernel/head_40x.S
> @@ -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
> diff --git a/arch/powerpc/kernel/head_44x.S b/arch/powerpc/kernel/head_44x.S
> index 37117ab11584..34a5df827b38 100644
> --- a/arch/powerpc/kernel/head_44x.S
> +++ b/arch/powerpc/kernel/head_44x.S
> @@ -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
> diff --git a/arch/powerpc/kernel/head_8xx.S b/arch/powerpc/kernel/head_8xx.S
> index 03c73b4c6435..d25adb6ef235 100644
> --- a/arch/powerpc/kernel/head_8xx.S
> +++ b/arch/powerpc/kernel/head_8xx.S
> @@ -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
> diff --git a/arch/powerpc/kernel/head_fsl_booke.S b/arch/powerpc/kernel/head_fsl_booke.S
> index 32332e24e421..567e0ed45ca8 100644
> --- a/arch/powerpc/kernel/head_fsl_booke.S
> +++ b/arch/powerpc/kernel/head_fsl_booke.S
> @@ -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
> diff --git a/arch/powerpc/kernel/setup-common.c b/arch/powerpc/kernel/setup-common.c
> index 1729bf409562..15afb01b4374 100644
> --- a/arch/powerpc/kernel/setup-common.c
> +++ b/arch/powerpc/kernel/setup-common.c
> @@ -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 */
> diff --git a/arch/powerpc/mm/Makefile b/arch/powerpc/mm/Makefile
> index dd945ca869b2..01afb10a7b33 100644
> --- a/arch/powerpc/mm/Makefile
> +++ b/arch/powerpc/mm/Makefile
> @@ -53,6 +53,7 @@ obj-$(CONFIG_PPC_COPRO_BASE) += copro_fault.o
> obj-$(CONFIG_SPAPR_TCE_IOMMU) += mmu_context_iommu.o
> obj-$(CONFIG_PPC_PTDUMP) += ptdump/
> obj-$(CONFIG_PPC_MEM_KEYS) += pkeys.o
> +obj-$(CONFIG_KASAN) += kasan/
>
> # Disable kcov instrumentation on sensitive code
> # This is necessary for booting with kcov enabled on book3e machines
> diff --git a/arch/powerpc/mm/init_32.c b/arch/powerpc/mm/init_32.c
> index 80cc97cd8878..5b61673e7eed 100644
> --- a/arch/powerpc/mm/init_32.c
> +++ b/arch/powerpc/mm/init_32.c
> @@ -46,6 +46,7 @@
> #include <asm/sections.h>
> #include <asm/hugetlb.h>
> #include <asm/kup.h>
> +#include <asm/kasan.h>
>
> #include "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/Makefile b/arch/powerpc/mm/kasan/Makefile
> new file mode 100644
> index 000000000000..6577897673dd
> --- /dev/null
> +++ b/arch/powerpc/mm/kasan/Makefile
> @@ -0,0 +1,5 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +KASAN_SANITIZE := n
> +
> +obj-$(CONFIG_PPC32) += kasan_init_32.o
> diff --git a/arch/powerpc/mm/kasan/kasan_init_32.c b/arch/powerpc/mm/kasan/kasan_init_32.c
> new file mode 100644
> index 000000000000..42617fcad828
> --- /dev/null
> +++ b/arch/powerpc/mm/kasan/kasan_init_32.c
> @@ -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");
> +}
>