![Mark Rutland](/assets/img/avatar_default.png)
In arm64's kasan_init(), we use pfn_to_nid() to find the NUMA node a span of memory is in, hoping to allocate shadow from the same NUMA node. However, at this point, the page array has not been initialized, and thus this is bogus. Since commit:f165b378bb
("mm: uninitialized struct page poisoning sanity") ... accessing fields of the page array results in a boot time Oops(), highlighting this problem: [ 0.000000] Unable to handle kernel paging request at virtual address dfff200000000000 [ 0.000000] Mem abort info: [ 0.000000] ESR = 0x96000004 [ 0.000000] Exception class = DABT (current EL), IL = 32 bits [ 0.000000] SET = 0, FnV = 0 [ 0.000000] EA = 0, S1PTW = 0 [ 0.000000] Data abort info: [ 0.000000] ISV = 0, ISS = 0x00000004 [ 0.000000] CM = 0, WnR = 0 [ 0.000000] [dfff200000000000] address between user and kernel address ranges [ 0.000000] Internal error: Oops: 96000004 [#1] PREEMPT SMP [ 0.000000] Modules linked in: [ 0.000000] CPU: 0 PID: 0 Comm: swapper Not tainted 4.16.0-07317-gf165b378bbdf #42 [ 0.000000] Hardware name: ARM Juno development board (r1) (DT) [ 0.000000] pstate: 80000085 (Nzcv daIf -PAN -UAO) [ 0.000000] pc : __asan_load8+0x8c/0xa8 [ 0.000000] lr : __dump_page+0x3c/0x3b8 [ 0.000000] sp : ffff2000099b7ca0 [ 0.000000] x29: ffff2000099b7ca0 x28: ffff20000a1762c0 [ 0.000000] x27: ffff7e0000000000 x26: ffff2000099dd000 [ 0.000000] x25: ffff200009a3f960 x24: ffff200008f9c38c [ 0.000000] x23: ffff20000a9d3000 x22: ffff200009735430 [ 0.000000] x21: fffffffffffffffe x20: ffff7e0001e50420 [ 0.000000] x19: ffff7e0001e50400 x18: 0000000000001840 [ 0.000000] x17: ffffffffffff8270 x16: 0000000000001840 [ 0.000000] x15: 0000000000001920 x14: 0000000000000004 [ 0.000000] x13: 0000000000000000 x12: 0000000000000800 [ 0.000000] x11: 1ffff0012d0f89ff x10: ffff10012d0f89ff [ 0.000000] x9 : 0000000000000000 x8 : ffff8009687c5000 [ 0.000000] x7 : 0000000000000000 x6 : ffff10000f282000 [ 0.000000] x5 : 0000000000000040 x4 : fffffffffffffffe [ 0.000000] x3 : 0000000000000000 x2 : dfff200000000000 [ 0.000000] x1 : 0000000000000005 x0 : 0000000000000000 [ 0.000000] Process swapper (pid: 0, stack limit = 0x (ptrval)) [ 0.000000] Call trace: [ 0.000000] __asan_load8+0x8c/0xa8 [ 0.000000] __dump_page+0x3c/0x3b8 [ 0.000000] dump_page+0xc/0x18 [ 0.000000] kasan_init+0x2e8/0x5a8 [ 0.000000] setup_arch+0x294/0x71c [ 0.000000] start_kernel+0xdc/0x500 [ 0.000000] Code: aa0403e0 9400063c 17ffffee d343fc00 (38e26800) [ 0.000000] ---[ end trace 67064f0e9c0cc338 ]--- [ 0.000000] Kernel panic - not syncing: Attempted to kill the idle task! [ 0.000000] ---[ end Kernel panic - not syncing: Attempted to kill the idle task! ]--- Let's fix this by using early_pfn_to_nid(), as other architectures do in their kasan init code. Note that early_pfn_to_nid acquires the nid from the memblock array, which we iterate over in kasan_init(), so this should be fine. Signed-off-by: Mark Rutland <mark.rutland@arm.com> Fixes:39d114ddc6
("arm64: add KASAN support") Cc: Will Deacon <will.deacon@arm.com> Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
245 lines
7.4 KiB
C
245 lines
7.4 KiB
C
/*
|
|
* This file contains kasan initialization code for ARM64.
|
|
*
|
|
* Copyright (c) 2015 Samsung Electronics Co., Ltd.
|
|
* Author: Andrey Ryabinin <ryabinin.a.a@gmail.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
*/
|
|
|
|
#define pr_fmt(fmt) "kasan: " fmt
|
|
#include <linux/bootmem.h>
|
|
#include <linux/kasan.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/sched/task.h>
|
|
#include <linux/memblock.h>
|
|
#include <linux/start_kernel.h>
|
|
#include <linux/mm.h>
|
|
|
|
#include <asm/mmu_context.h>
|
|
#include <asm/kernel-pgtable.h>
|
|
#include <asm/page.h>
|
|
#include <asm/pgalloc.h>
|
|
#include <asm/pgtable.h>
|
|
#include <asm/sections.h>
|
|
#include <asm/tlbflush.h>
|
|
|
|
static pgd_t tmp_pg_dir[PTRS_PER_PGD] __initdata __aligned(PGD_SIZE);
|
|
|
|
/*
|
|
* The p*d_populate functions call virt_to_phys implicitly so they can't be used
|
|
* directly on kernel symbols (bm_p*d). All the early functions are called too
|
|
* early to use lm_alias so __p*d_populate functions must be used to populate
|
|
* with the physical address from __pa_symbol.
|
|
*/
|
|
|
|
static phys_addr_t __init kasan_alloc_zeroed_page(int node)
|
|
{
|
|
void *p = memblock_virt_alloc_try_nid(PAGE_SIZE, PAGE_SIZE,
|
|
__pa(MAX_DMA_ADDRESS),
|
|
MEMBLOCK_ALLOC_ACCESSIBLE, node);
|
|
return __pa(p);
|
|
}
|
|
|
|
static pte_t *__init kasan_pte_offset(pmd_t *pmdp, unsigned long addr, int node,
|
|
bool early)
|
|
{
|
|
if (pmd_none(READ_ONCE(*pmdp))) {
|
|
phys_addr_t pte_phys = early ? __pa_symbol(kasan_zero_pte)
|
|
: kasan_alloc_zeroed_page(node);
|
|
__pmd_populate(pmdp, pte_phys, PMD_TYPE_TABLE);
|
|
}
|
|
|
|
return early ? pte_offset_kimg(pmdp, addr)
|
|
: pte_offset_kernel(pmdp, addr);
|
|
}
|
|
|
|
static pmd_t *__init kasan_pmd_offset(pud_t *pudp, unsigned long addr, int node,
|
|
bool early)
|
|
{
|
|
if (pud_none(READ_ONCE(*pudp))) {
|
|
phys_addr_t pmd_phys = early ? __pa_symbol(kasan_zero_pmd)
|
|
: kasan_alloc_zeroed_page(node);
|
|
__pud_populate(pudp, pmd_phys, PMD_TYPE_TABLE);
|
|
}
|
|
|
|
return early ? pmd_offset_kimg(pudp, addr) : pmd_offset(pudp, addr);
|
|
}
|
|
|
|
static pud_t *__init kasan_pud_offset(pgd_t *pgdp, unsigned long addr, int node,
|
|
bool early)
|
|
{
|
|
if (pgd_none(READ_ONCE(*pgdp))) {
|
|
phys_addr_t pud_phys = early ? __pa_symbol(kasan_zero_pud)
|
|
: kasan_alloc_zeroed_page(node);
|
|
__pgd_populate(pgdp, pud_phys, PMD_TYPE_TABLE);
|
|
}
|
|
|
|
return early ? pud_offset_kimg(pgdp, addr) : pud_offset(pgdp, addr);
|
|
}
|
|
|
|
static void __init kasan_pte_populate(pmd_t *pmdp, unsigned long addr,
|
|
unsigned long end, int node, bool early)
|
|
{
|
|
unsigned long next;
|
|
pte_t *ptep = kasan_pte_offset(pmdp, addr, node, early);
|
|
|
|
do {
|
|
phys_addr_t page_phys = early ? __pa_symbol(kasan_zero_page)
|
|
: kasan_alloc_zeroed_page(node);
|
|
next = addr + PAGE_SIZE;
|
|
set_pte(ptep, pfn_pte(__phys_to_pfn(page_phys), PAGE_KERNEL));
|
|
} while (ptep++, addr = next, addr != end && pte_none(READ_ONCE(*ptep)));
|
|
}
|
|
|
|
static void __init kasan_pmd_populate(pud_t *pudp, unsigned long addr,
|
|
unsigned long end, int node, bool early)
|
|
{
|
|
unsigned long next;
|
|
pmd_t *pmdp = kasan_pmd_offset(pudp, addr, node, early);
|
|
|
|
do {
|
|
next = pmd_addr_end(addr, end);
|
|
kasan_pte_populate(pmdp, addr, next, node, early);
|
|
} while (pmdp++, addr = next, addr != end && pmd_none(READ_ONCE(*pmdp)));
|
|
}
|
|
|
|
static void __init kasan_pud_populate(pgd_t *pgdp, unsigned long addr,
|
|
unsigned long end, int node, bool early)
|
|
{
|
|
unsigned long next;
|
|
pud_t *pudp = kasan_pud_offset(pgdp, addr, node, early);
|
|
|
|
do {
|
|
next = pud_addr_end(addr, end);
|
|
kasan_pmd_populate(pudp, addr, next, node, early);
|
|
} while (pudp++, addr = next, addr != end && pud_none(READ_ONCE(*pudp)));
|
|
}
|
|
|
|
static void __init kasan_pgd_populate(unsigned long addr, unsigned long end,
|
|
int node, bool early)
|
|
{
|
|
unsigned long next;
|
|
pgd_t *pgdp;
|
|
|
|
pgdp = pgd_offset_k(addr);
|
|
do {
|
|
next = pgd_addr_end(addr, end);
|
|
kasan_pud_populate(pgdp, addr, next, node, early);
|
|
} while (pgdp++, addr = next, addr != end);
|
|
}
|
|
|
|
/* The early shadow maps everything to a single page of zeroes */
|
|
asmlinkage void __init kasan_early_init(void)
|
|
{
|
|
BUILD_BUG_ON(KASAN_SHADOW_OFFSET !=
|
|
KASAN_SHADOW_END - (1UL << (64 - KASAN_SHADOW_SCALE_SHIFT)));
|
|
BUILD_BUG_ON(!IS_ALIGNED(KASAN_SHADOW_START, PGDIR_SIZE));
|
|
BUILD_BUG_ON(!IS_ALIGNED(KASAN_SHADOW_END, PGDIR_SIZE));
|
|
kasan_pgd_populate(KASAN_SHADOW_START, KASAN_SHADOW_END, NUMA_NO_NODE,
|
|
true);
|
|
}
|
|
|
|
/* Set up full kasan mappings, ensuring that the mapped pages are zeroed */
|
|
static void __init kasan_map_populate(unsigned long start, unsigned long end,
|
|
int node)
|
|
{
|
|
kasan_pgd_populate(start & PAGE_MASK, PAGE_ALIGN(end), node, false);
|
|
}
|
|
|
|
/*
|
|
* Copy the current shadow region into a new pgdir.
|
|
*/
|
|
void __init kasan_copy_shadow(pgd_t *pgdir)
|
|
{
|
|
pgd_t *pgdp, *pgdp_new, *pgdp_end;
|
|
|
|
pgdp = pgd_offset_k(KASAN_SHADOW_START);
|
|
pgdp_end = pgd_offset_k(KASAN_SHADOW_END);
|
|
pgdp_new = pgd_offset_raw(pgdir, KASAN_SHADOW_START);
|
|
do {
|
|
set_pgd(pgdp_new, READ_ONCE(*pgdp));
|
|
} while (pgdp++, pgdp_new++, pgdp != pgdp_end);
|
|
}
|
|
|
|
static void __init clear_pgds(unsigned long start,
|
|
unsigned long end)
|
|
{
|
|
/*
|
|
* Remove references to kasan page tables from
|
|
* swapper_pg_dir. pgd_clear() can't be used
|
|
* here because it's nop on 2,3-level pagetable setups
|
|
*/
|
|
for (; start < end; start += PGDIR_SIZE)
|
|
set_pgd(pgd_offset_k(start), __pgd(0));
|
|
}
|
|
|
|
void __init kasan_init(void)
|
|
{
|
|
u64 kimg_shadow_start, kimg_shadow_end;
|
|
u64 mod_shadow_start, mod_shadow_end;
|
|
struct memblock_region *reg;
|
|
int i;
|
|
|
|
kimg_shadow_start = (u64)kasan_mem_to_shadow(_text) & PAGE_MASK;
|
|
kimg_shadow_end = PAGE_ALIGN((u64)kasan_mem_to_shadow(_end));
|
|
|
|
mod_shadow_start = (u64)kasan_mem_to_shadow((void *)MODULES_VADDR);
|
|
mod_shadow_end = (u64)kasan_mem_to_shadow((void *)MODULES_END);
|
|
|
|
/*
|
|
* We are going to perform proper setup of shadow memory.
|
|
* At first we should unmap early shadow (clear_pgds() call bellow).
|
|
* However, instrumented code couldn't execute without shadow memory.
|
|
* tmp_pg_dir used to keep early shadow mapped until full shadow
|
|
* setup will be finished.
|
|
*/
|
|
memcpy(tmp_pg_dir, swapper_pg_dir, sizeof(tmp_pg_dir));
|
|
dsb(ishst);
|
|
cpu_replace_ttbr1(lm_alias(tmp_pg_dir));
|
|
|
|
clear_pgds(KASAN_SHADOW_START, KASAN_SHADOW_END);
|
|
|
|
kasan_map_populate(kimg_shadow_start, kimg_shadow_end,
|
|
early_pfn_to_nid(virt_to_pfn(lm_alias(_text))));
|
|
|
|
kasan_populate_zero_shadow((void *)KASAN_SHADOW_START,
|
|
(void *)mod_shadow_start);
|
|
kasan_populate_zero_shadow((void *)kimg_shadow_end,
|
|
kasan_mem_to_shadow((void *)PAGE_OFFSET));
|
|
|
|
if (kimg_shadow_start > mod_shadow_end)
|
|
kasan_populate_zero_shadow((void *)mod_shadow_end,
|
|
(void *)kimg_shadow_start);
|
|
|
|
for_each_memblock(memory, reg) {
|
|
void *start = (void *)__phys_to_virt(reg->base);
|
|
void *end = (void *)__phys_to_virt(reg->base + reg->size);
|
|
|
|
if (start >= end)
|
|
break;
|
|
|
|
kasan_map_populate((unsigned long)kasan_mem_to_shadow(start),
|
|
(unsigned long)kasan_mem_to_shadow(end),
|
|
early_pfn_to_nid(virt_to_pfn(start)));
|
|
}
|
|
|
|
/*
|
|
* KAsan may reuse the contents of kasan_zero_pte directly, so we
|
|
* should make sure that it maps the zero page read-only.
|
|
*/
|
|
for (i = 0; i < PTRS_PER_PTE; i++)
|
|
set_pte(&kasan_zero_pte[i],
|
|
pfn_pte(sym_to_pfn(kasan_zero_page), PAGE_KERNEL_RO));
|
|
|
|
memset(kasan_zero_page, 0, PAGE_SIZE);
|
|
cpu_replace_ttbr1(lm_alias(swapper_pg_dir));
|
|
|
|
/* At this point kasan is fully initialized. Enable error messages */
|
|
init_task.kasan_depth = 0;
|
|
pr_info("KernelAddressSanitizer initialized\n");
|
|
}
|