Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

x86_64: add KASan support

This patch adds arch specific code for kernel address sanitizer.

16TB of virtual addressed used for shadow memory. It's located in range
[ffffec0000000000 - fffffc0000000000] between vmemmap and %esp fixup
stacks.

At early stage we map whole shadow region with zero page. Latter, after
pages mapped to direct mapping address range we unmap zero pages from
corresponding shadow (see kasan_map_shadow()) and allocate and map a real
shadow memory reusing vmemmap_populate() function.

Also replace __pa with __pa_nodebug before shadow initialized. __pa with
CONFIG_DEBUG_VIRTUAL=y make external function call (__phys_addr)
__phys_addr is instrumented, so __asan_load could be called before shadow
area initialized.

Signed-off-by: Andrey Ryabinin <a.ryabinin@samsung.com>
Cc: Dmitry Vyukov <dvyukov@google.com>
Cc: Konstantin Serebryany <kcc@google.com>
Cc: Dmitry Chernenkov <dmitryc@google.com>
Signed-off-by: Andrey Konovalov <adech.fo@gmail.com>
Cc: Yuri Gribov <tetra2005@gmail.com>
Cc: Konstantin Khlebnikov <koct9i@gmail.com>
Cc: Sasha Levin <sasha.levin@oracle.com>
Cc: Christoph Lameter <cl@linux.com>
Cc: Joonsoo Kim <iamjoonsoo.kim@lge.com>
Cc: Dave Hansen <dave.hansen@intel.com>
Cc: Andi Kleen <andi@firstfloor.org>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Christoph Lameter <cl@linux.com>
Cc: Pekka Enberg <penberg@kernel.org>
Cc: David Rientjes <rientjes@google.com>
Cc: Jim Davis <jim.epost@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Andrey Ryabinin and committed by
Linus Torvalds
ef7f0d6a 786a8959

+290 -4
+2
Documentation/x86/x86_64/mm.txt
··· 12 12 ffffe90000000000 - ffffe9ffffffffff (=40 bits) hole 13 13 ffffea0000000000 - ffffeaffffffffff (=40 bits) virtual memory map (1TB) 14 14 ... unused hole ... 15 + ffffec0000000000 - fffffc0000000000 (=44 bits) kasan shadow memory (16TB) 16 + ... unused hole ... 15 17 ffffff0000000000 - ffffff7fffffffff (=39 bits) %esp fixup stacks 16 18 ... unused hole ... 17 19 ffffffff80000000 - ffffffffa0000000 (=512 MB) kernel text mapping, from phys 0
+1
arch/x86/Kconfig
··· 85 85 select HAVE_CMPXCHG_LOCAL 86 86 select HAVE_CMPXCHG_DOUBLE 87 87 select HAVE_ARCH_KMEMCHECK 88 + select HAVE_ARCH_KASAN if X86_64 && SPARSEMEM_VMEMMAP 88 89 select HAVE_USER_RETURN_NOTIFIER 89 90 select ARCH_BINFMT_ELF_RANDOMIZE_PIE 90 91 select HAVE_ARCH_JUMP_LABEL
+2
arch/x86/boot/Makefile
··· 14 14 # Set it to -DSVGA_MODE=NORMAL_VGA if you just want the EGA/VGA mode. 15 15 # The number is the same as you would ordinarily press at bootup. 16 16 17 + KASAN_SANITIZE := n 18 + 17 19 SVGA_MODE := -DSVGA_MODE=NORMAL_VGA 18 20 19 21 targets := vmlinux.bin setup.bin setup.elf bzImage
+2
arch/x86/boot/compressed/Makefile
··· 16 16 # (see scripts/Makefile.lib size_append) 17 17 # compressed vmlinux.bin.all + u32 size of vmlinux.bin.all 18 18 19 + KASAN_SANITIZE := n 20 + 19 21 targets := vmlinux vmlinux.bin vmlinux.bin.gz vmlinux.bin.bz2 vmlinux.bin.lzma \ 20 22 vmlinux.bin.xz vmlinux.bin.lzo vmlinux.bin.lz4 21 23
+31
arch/x86/include/asm/kasan.h
··· 1 + #ifndef _ASM_X86_KASAN_H 2 + #define _ASM_X86_KASAN_H 3 + 4 + /* 5 + * Compiler uses shadow offset assuming that addresses start 6 + * from 0. Kernel addresses don't start from 0, so shadow 7 + * for kernel really starts from compiler's shadow offset + 8 + * 'kernel address space start' >> KASAN_SHADOW_SCALE_SHIFT 9 + */ 10 + #define KASAN_SHADOW_START (KASAN_SHADOW_OFFSET + \ 11 + (0xffff800000000000ULL >> 3)) 12 + /* 47 bits for kernel address -> (47 - 3) bits for shadow */ 13 + #define KASAN_SHADOW_END (KASAN_SHADOW_START + (1ULL << (47 - 3))) 14 + 15 + #ifndef __ASSEMBLY__ 16 + 17 + extern pte_t kasan_zero_pte[]; 18 + extern pte_t kasan_zero_pmd[]; 19 + extern pte_t kasan_zero_pud[]; 20 + 21 + #ifdef CONFIG_KASAN 22 + void __init kasan_map_early_shadow(pgd_t *pgd); 23 + void __init kasan_init(void); 24 + #else 25 + static inline void kasan_map_early_shadow(pgd_t *pgd) { } 26 + static inline void kasan_init(void) { } 27 + #endif 28 + 29 + #endif 30 + 31 + #endif
+2
arch/x86/kernel/Makefile
··· 16 16 CFLAGS_REMOVE_early_printk.o = -pg 17 17 endif 18 18 19 + KASAN_SANITIZE_head$(BITS).o := n 20 + 19 21 CFLAGS_irq.o := -I$(src)/../include/asm/trace 20 22 21 23 obj-y := process_$(BITS).o signal.o entry_$(BITS).o
+4 -1
arch/x86/kernel/dumpstack.c
··· 265 265 printk("SMP "); 266 266 #endif 267 267 #ifdef CONFIG_DEBUG_PAGEALLOC 268 - printk("DEBUG_PAGEALLOC"); 268 + printk("DEBUG_PAGEALLOC "); 269 + #endif 270 + #ifdef CONFIG_KASAN 271 + printk("KASAN"); 269 272 #endif 270 273 printk("\n"); 271 274 if (notify_die(DIE_OOPS, str, regs, err,
+7 -2
arch/x86/kernel/head64.c
··· 27 27 #include <asm/bios_ebda.h> 28 28 #include <asm/bootparam_utils.h> 29 29 #include <asm/microcode.h> 30 + #include <asm/kasan.h> 30 31 31 32 /* 32 33 * Manage page tables very early on. ··· 47 46 48 47 next_early_pgt = 0; 49 48 50 - write_cr3(__pa(early_level4_pgt)); 49 + write_cr3(__pa_nodebug(early_level4_pgt)); 51 50 } 52 51 53 52 /* Create a new PMD entry */ ··· 60 59 pmdval_t pmd, *pmd_p; 61 60 62 61 /* Invalid address or early pgt is done ? */ 63 - if (physaddr >= MAXMEM || read_cr3() != __pa(early_level4_pgt)) 62 + if (physaddr >= MAXMEM || read_cr3() != __pa_nodebug(early_level4_pgt)) 64 63 return -1; 65 64 66 65 again: ··· 159 158 /* Kill off the identity-map trampoline */ 160 159 reset_early_page_tables(); 161 160 161 + kasan_map_early_shadow(early_level4_pgt); 162 + 162 163 /* clear bss before set_intr_gate with early_idt_handler */ 163 164 clear_bss(); 164 165 ··· 181 178 clear_page(init_level4_pgt); 182 179 /* set init_level4_pgt kernel high mapping*/ 183 180 init_level4_pgt[511] = early_level4_pgt[511]; 181 + 182 + kasan_map_early_shadow(init_level4_pgt); 184 183 185 184 x86_64_start_reservations(real_mode_data); 186 185 }
+30
arch/x86/kernel/head_64.S
··· 514 514 /* This must match the first entry in level2_kernel_pgt */ 515 515 .quad 0x0000000000000000 516 516 517 + #ifdef CONFIG_KASAN 518 + #define FILL(VAL, COUNT) \ 519 + .rept (COUNT) ; \ 520 + .quad (VAL) ; \ 521 + .endr 522 + 523 + NEXT_PAGE(kasan_zero_pte) 524 + FILL(kasan_zero_page - __START_KERNEL_map + _KERNPG_TABLE, 512) 525 + NEXT_PAGE(kasan_zero_pmd) 526 + FILL(kasan_zero_pte - __START_KERNEL_map + _KERNPG_TABLE, 512) 527 + NEXT_PAGE(kasan_zero_pud) 528 + FILL(kasan_zero_pmd - __START_KERNEL_map + _KERNPG_TABLE, 512) 529 + 530 + #undef FILL 531 + #endif 532 + 533 + 517 534 #include "../../x86/xen/xen-head.S" 518 535 519 536 __PAGE_ALIGNED_BSS 520 537 NEXT_PAGE(empty_zero_page) 521 538 .skip PAGE_SIZE 539 + 540 + #ifdef CONFIG_KASAN 541 + /* 542 + * This page used as early shadow. We don't use empty_zero_page 543 + * at early stages, stack instrumentation could write some garbage 544 + * to this page. 545 + * Latter we reuse it as zero shadow for large ranges of memory 546 + * that allowed to access, but not instrumented by kasan 547 + * (vmalloc/vmemmap ...). 548 + */ 549 + NEXT_PAGE(kasan_zero_page) 550 + .skip PAGE_SIZE 551 + #endif
+3
arch/x86/kernel/setup.c
··· 89 89 #include <asm/cacheflush.h> 90 90 #include <asm/processor.h> 91 91 #include <asm/bugs.h> 92 + #include <asm/kasan.h> 92 93 93 94 #include <asm/vsyscall.h> 94 95 #include <asm/cpu.h> ··· 1174 1173 #endif 1175 1174 1176 1175 x86_init.paging.pagetable_init(); 1176 + 1177 + kasan_init(); 1177 1178 1178 1179 if (boot_cpu_data.cpuid_level >= 0) { 1179 1180 /* A CPU has %cr4 if and only if it has CPUID */
+3
arch/x86/mm/Makefile
··· 20 20 21 21 obj-$(CONFIG_KMEMCHECK) += kmemcheck/ 22 22 23 + KASAN_SANITIZE_kasan_init_$(BITS).o := n 24 + obj-$(CONFIG_KASAN) += kasan_init_$(BITS).o 25 + 23 26 obj-$(CONFIG_MMIOTRACE) += mmiotrace.o 24 27 mmiotrace-y := kmmio.o pf_in.o mmio-mod.o 25 28 obj-$(CONFIG_MMIOTRACE_TEST) += testmmiotrace.o
+199
arch/x86/mm/kasan_init_64.c
··· 1 + #include <linux/bootmem.h> 2 + #include <linux/kasan.h> 3 + #include <linux/kdebug.h> 4 + #include <linux/mm.h> 5 + #include <linux/sched.h> 6 + #include <linux/vmalloc.h> 7 + 8 + #include <asm/tlbflush.h> 9 + #include <asm/sections.h> 10 + 11 + extern pgd_t early_level4_pgt[PTRS_PER_PGD]; 12 + extern struct range pfn_mapped[E820_X_MAX]; 13 + 14 + extern unsigned char kasan_zero_page[PAGE_SIZE]; 15 + 16 + static int __init map_range(struct range *range) 17 + { 18 + unsigned long start; 19 + unsigned long end; 20 + 21 + start = (unsigned long)kasan_mem_to_shadow(pfn_to_kaddr(range->start)); 22 + end = (unsigned long)kasan_mem_to_shadow(pfn_to_kaddr(range->end)); 23 + 24 + /* 25 + * end + 1 here is intentional. We check several shadow bytes in advance 26 + * to slightly speed up fastpath. In some rare cases we could cross 27 + * boundary of mapped shadow, so we just map some more here. 28 + */ 29 + return vmemmap_populate(start, end + 1, NUMA_NO_NODE); 30 + } 31 + 32 + static void __init clear_pgds(unsigned long start, 33 + unsigned long end) 34 + { 35 + for (; start < end; start += PGDIR_SIZE) 36 + pgd_clear(pgd_offset_k(start)); 37 + } 38 + 39 + void __init kasan_map_early_shadow(pgd_t *pgd) 40 + { 41 + int i; 42 + unsigned long start = KASAN_SHADOW_START; 43 + unsigned long end = KASAN_SHADOW_END; 44 + 45 + for (i = pgd_index(start); start < end; i++) { 46 + pgd[i] = __pgd(__pa_nodebug(kasan_zero_pud) 47 + | _KERNPG_TABLE); 48 + start += PGDIR_SIZE; 49 + } 50 + } 51 + 52 + static int __init zero_pte_populate(pmd_t *pmd, unsigned long addr, 53 + unsigned long end) 54 + { 55 + pte_t *pte = pte_offset_kernel(pmd, addr); 56 + 57 + while (addr + PAGE_SIZE <= end) { 58 + WARN_ON(!pte_none(*pte)); 59 + set_pte(pte, __pte(__pa_nodebug(kasan_zero_page) 60 + | __PAGE_KERNEL_RO)); 61 + addr += PAGE_SIZE; 62 + pte = pte_offset_kernel(pmd, addr); 63 + } 64 + return 0; 65 + } 66 + 67 + static int __init zero_pmd_populate(pud_t *pud, unsigned long addr, 68 + unsigned long end) 69 + { 70 + int ret = 0; 71 + pmd_t *pmd = pmd_offset(pud, addr); 72 + 73 + while (IS_ALIGNED(addr, PMD_SIZE) && addr + PMD_SIZE <= end) { 74 + WARN_ON(!pmd_none(*pmd)); 75 + set_pmd(pmd, __pmd(__pa_nodebug(kasan_zero_pte) 76 + | __PAGE_KERNEL_RO)); 77 + addr += PMD_SIZE; 78 + pmd = pmd_offset(pud, addr); 79 + } 80 + if (addr < end) { 81 + if (pmd_none(*pmd)) { 82 + void *p = vmemmap_alloc_block(PAGE_SIZE, NUMA_NO_NODE); 83 + if (!p) 84 + return -ENOMEM; 85 + set_pmd(pmd, __pmd(__pa_nodebug(p) | _KERNPG_TABLE)); 86 + } 87 + ret = zero_pte_populate(pmd, addr, end); 88 + } 89 + return ret; 90 + } 91 + 92 + 93 + static int __init zero_pud_populate(pgd_t *pgd, unsigned long addr, 94 + unsigned long end) 95 + { 96 + int ret = 0; 97 + pud_t *pud = pud_offset(pgd, addr); 98 + 99 + while (IS_ALIGNED(addr, PUD_SIZE) && addr + PUD_SIZE <= end) { 100 + WARN_ON(!pud_none(*pud)); 101 + set_pud(pud, __pud(__pa_nodebug(kasan_zero_pmd) 102 + | __PAGE_KERNEL_RO)); 103 + addr += PUD_SIZE; 104 + pud = pud_offset(pgd, addr); 105 + } 106 + 107 + if (addr < end) { 108 + if (pud_none(*pud)) { 109 + void *p = vmemmap_alloc_block(PAGE_SIZE, NUMA_NO_NODE); 110 + if (!p) 111 + return -ENOMEM; 112 + set_pud(pud, __pud(__pa_nodebug(p) | _KERNPG_TABLE)); 113 + } 114 + ret = zero_pmd_populate(pud, addr, end); 115 + } 116 + return ret; 117 + } 118 + 119 + static int __init zero_pgd_populate(unsigned long addr, unsigned long end) 120 + { 121 + int ret = 0; 122 + pgd_t *pgd = pgd_offset_k(addr); 123 + 124 + while (IS_ALIGNED(addr, PGDIR_SIZE) && addr + PGDIR_SIZE <= end) { 125 + WARN_ON(!pgd_none(*pgd)); 126 + set_pgd(pgd, __pgd(__pa_nodebug(kasan_zero_pud) 127 + | __PAGE_KERNEL_RO)); 128 + addr += PGDIR_SIZE; 129 + pgd = pgd_offset_k(addr); 130 + } 131 + 132 + if (addr < end) { 133 + if (pgd_none(*pgd)) { 134 + void *p = vmemmap_alloc_block(PAGE_SIZE, NUMA_NO_NODE); 135 + if (!p) 136 + return -ENOMEM; 137 + set_pgd(pgd, __pgd(__pa_nodebug(p) | _KERNPG_TABLE)); 138 + } 139 + ret = zero_pud_populate(pgd, addr, end); 140 + } 141 + return ret; 142 + } 143 + 144 + 145 + static void __init populate_zero_shadow(const void *start, const void *end) 146 + { 147 + if (zero_pgd_populate((unsigned long)start, (unsigned long)end)) 148 + panic("kasan: unable to map zero shadow!"); 149 + } 150 + 151 + 152 + #ifdef CONFIG_KASAN_INLINE 153 + static int kasan_die_handler(struct notifier_block *self, 154 + unsigned long val, 155 + void *data) 156 + { 157 + if (val == DIE_GPF) { 158 + pr_emerg("CONFIG_KASAN_INLINE enabled"); 159 + pr_emerg("GPF could be caused by NULL-ptr deref or user memory access"); 160 + } 161 + return NOTIFY_OK; 162 + } 163 + 164 + static struct notifier_block kasan_die_notifier = { 165 + .notifier_call = kasan_die_handler, 166 + }; 167 + #endif 168 + 169 + void __init kasan_init(void) 170 + { 171 + int i; 172 + 173 + #ifdef CONFIG_KASAN_INLINE 174 + register_die_notifier(&kasan_die_notifier); 175 + #endif 176 + 177 + memcpy(early_level4_pgt, init_level4_pgt, sizeof(early_level4_pgt)); 178 + load_cr3(early_level4_pgt); 179 + 180 + clear_pgds(KASAN_SHADOW_START, KASAN_SHADOW_END); 181 + 182 + populate_zero_shadow((void *)KASAN_SHADOW_START, 183 + kasan_mem_to_shadow((void *)PAGE_OFFSET)); 184 + 185 + for (i = 0; i < E820_X_MAX; i++) { 186 + if (pfn_mapped[i].end == 0) 187 + break; 188 + 189 + if (map_range(&pfn_mapped[i])) 190 + panic("kasan: unable to allocate shadow!"); 191 + } 192 + 193 + populate_zero_shadow(kasan_mem_to_shadow((void *)PAGE_OFFSET + MAXMEM), 194 + (void *)KASAN_SHADOW_END); 195 + 196 + memset(kasan_zero_page, 0, PAGE_SIZE); 197 + 198 + load_cr3(init_level4_pgt); 199 + }
+1 -1
arch/x86/realmode/Makefile
··· 6 6 # for more details. 7 7 # 8 8 # 9 - 9 + KASAN_SANITIZE := n 10 10 subdir- := rm 11 11 12 12 obj-y += init.o
+1
arch/x86/realmode/rm/Makefile
··· 6 6 # for more details. 7 7 # 8 8 # 9 + KASAN_SANITIZE := n 9 10 10 11 always := realmode.bin realmode.relocs 11 12
+1
arch/x86/vdso/Makefile
··· 3 3 # 4 4 5 5 KBUILD_CFLAGS += $(DISABLE_LTO) 6 + KASAN_SANITIZE := n 6 7 7 8 VDSO64-$(CONFIG_X86_64) := y 8 9 VDSOX32-$(CONFIG_X86_X32_ABI) := y
+1
lib/Kconfig.kasan
··· 15 15 16 16 config KASAN_SHADOW_OFFSET 17 17 hex 18 + default 0xdffffc0000000000 if X86_64 18 19 19 20 choice 20 21 prompt "Instrumentation type"