Module Name: src Committed By: maxv Date: Mon Aug 20 15:04:52 UTC 2018
Modified Files: src/sys/arch/amd64/amd64: machdep.c src/sys/arch/amd64/conf: GENERIC Makefile.amd64 files.amd64 kern.ldscript src/sys/arch/amd64/include: pmap.h types.h src/sys/arch/x86/include: pmap.h src/sys/arch/x86/x86: pmap.c src/sys/conf: files src/sys/kern: kern_malloc.c subr_kmem.c src/sys/lib/libkern: libkern.h src/sys/sys: Makefile Added Files: src/sys/arch/amd64/amd64: asan.c src/sys/sys: asan.h Log Message: Add support for kASan on amd64. Written by me, with some parts inspired from Siddharth Muralee's initial work. This feature can detect several kinds of memory bugs, and it's an excellent feature. It can be enabled by uncommenting these three lines in GENERIC: #makeoptions KASAN=1 # Kernel Address Sanitizer #options KASAN #no options SVS The kernel is compiled without SVS, without DMAP and without PCPU area. A shadow area is created at boot time, and it can cover the upper 128TB of the address space. This area is populated gradually as we allocate memory. With this design the memory consumption is kept at its lowest level. The compiler calls the __asan_* functions each time a memory access is done. We verify whether this access is legal by looking at the shadow area. We declare our own special memcpy/memset/etc functions, because the compiler's builtins don't add the __asan_* instrumentation. Initially all the mappings are marked as valid. During dynamic allocations, we add a redzone, which we mark as invalid. Any access on it will trigger a kASan error message. Additionally, the compiler adds a redzone on global variables, and we mark these redzones as invalid too. The illegal-access detection works with a 1-byte granularity. For now, we cover three areas: - global variables - kmem_alloc-ated areas - malloc-ated areas More will come, but that's a good start. To generate a diff of this commit: cvs rdiff -u -r0 -r1.1 src/sys/arch/amd64/amd64/asan.c cvs rdiff -u -r1.314 -r1.315 src/sys/arch/amd64/amd64/machdep.c cvs rdiff -u -r1.503 -r1.504 src/sys/arch/amd64/conf/GENERIC cvs rdiff -u -r1.71 -r1.72 src/sys/arch/amd64/conf/Makefile.amd64 cvs rdiff -u -r1.105 -r1.106 src/sys/arch/amd64/conf/files.amd64 cvs rdiff -u -r1.26 -r1.27 src/sys/arch/amd64/conf/kern.ldscript cvs rdiff -u -r1.54 -r1.55 src/sys/arch/amd64/include/pmap.h cvs rdiff -u -r1.56 -r1.57 src/sys/arch/amd64/include/types.h cvs rdiff -u -r1.84 -r1.85 src/sys/arch/x86/include/pmap.h cvs rdiff -u -r1.303 -r1.304 src/sys/arch/x86/x86/pmap.c cvs rdiff -u -r1.1203 -r1.1204 src/sys/conf/files cvs rdiff -u -r1.147 -r1.148 src/sys/kern/kern_malloc.c cvs rdiff -u -r1.68 -r1.69 src/sys/kern/subr_kmem.c cvs rdiff -u -r1.127 -r1.128 src/sys/lib/libkern/libkern.h cvs rdiff -u -r1.166 -r1.167 src/sys/sys/Makefile cvs rdiff -u -r0 -r1.1 src/sys/sys/asan.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/amd64/amd64/machdep.c diff -u src/sys/arch/amd64/amd64/machdep.c:1.314 src/sys/arch/amd64/amd64/machdep.c:1.315 --- src/sys/arch/amd64/amd64/machdep.c:1.314 Sun Aug 12 15:31:01 2018 +++ src/sys/arch/amd64/amd64/machdep.c Mon Aug 20 15:04:51 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: machdep.c,v 1.314 2018/08/12 15:31:01 maxv Exp $ */ +/* $NetBSD: machdep.c,v 1.315 2018/08/20 15:04:51 maxv Exp $ */ /* * Copyright (c) 1996, 1997, 1998, 2000, 2006, 2007, 2008, 2011 @@ -110,7 +110,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: machdep.c,v 1.314 2018/08/12 15:31:01 maxv Exp $"); +__KERNEL_RCSID(0, "$NetBSD: machdep.c,v 1.315 2018/08/20 15:04:51 maxv Exp $"); #include "opt_modular.h" #include "opt_user_ldt.h" @@ -122,6 +122,7 @@ __KERNEL_RCSID(0, "$NetBSD: machdep.c,v #include "opt_xen.h" #include "opt_svs.h" #include "opt_kaslr.h" +#include "opt_kasan.h" #ifndef XEN #include "opt_physmem.h" #endif @@ -1656,6 +1657,15 @@ init_slotspace(void) slotspace.area[SLAREA_HYPV].dropmax = false; #endif +#ifdef KASAN + /* ASAN. */ + slotspace.area[SLAREA_ASAN].sslot = L4_SLOT_KASAN; + slotspace.area[SLAREA_ASAN].mslot = NL4_SLOT_KASAN; + slotspace.area[SLAREA_ASAN].nslot = NL4_SLOT_KASAN; + slotspace.area[SLAREA_ASAN].active = true; + slotspace.area[SLAREA_ASAN].dropmax = false; +#endif + /* Kernel. */ slotspace.area[SLAREA_KERN].sslot = L4_SLOT_KERNBASE; slotspace.area[SLAREA_KERN].mslot = 1; @@ -1781,6 +1791,11 @@ init_x86_64(paddr_t first_avail) init_x86_msgbuf(); +#ifdef KASAN + void kasan_init(void); + kasan_init(); +#endif + pmap_growkernel(VM_MIN_KERNEL_ADDRESS + 32 * 1024 * 1024); kpreempt_disable(); Index: src/sys/arch/amd64/conf/GENERIC diff -u src/sys/arch/amd64/conf/GENERIC:1.503 src/sys/arch/amd64/conf/GENERIC:1.504 --- src/sys/arch/amd64/conf/GENERIC:1.503 Tue Aug 14 06:37:59 2018 +++ src/sys/arch/amd64/conf/GENERIC Mon Aug 20 15:04:51 2018 @@ -1,4 +1,4 @@ -# $NetBSD: GENERIC,v 1.503 2018/08/14 06:37:59 maxv Exp $ +# $NetBSD: GENERIC,v 1.504 2018/08/20 15:04:51 maxv Exp $ # # GENERIC machine description file # @@ -22,7 +22,7 @@ include "arch/amd64/conf/std.amd64" options INCLUDE_CONFIG_FILE # embed config file in kernel binary -#ident "GENERIC-$Revision: 1.503 $" +#ident "GENERIC-$Revision: 1.504 $" maxusers 64 # estimated number of users @@ -117,6 +117,11 @@ makeoptions DEBUG="-g" # compile full sy #options SYSCALL_TIMES_HASCOUNTER # use 'broken' rdtsc (soekris) options KDTRACE_HOOKS # kernel DTrace hooks +# Kernel Address Sanitizer (kASan). You need to disable SVS to use it. +#makeoptions KASAN=1 # Kernel Address Sanitizer +#options KASAN +#no options SVS + # Compatibility options # x86_64 never shipped with a.out binaries; the two options below are # only relevant to 32-bit i386 binaries Index: src/sys/arch/amd64/conf/Makefile.amd64 diff -u src/sys/arch/amd64/conf/Makefile.amd64:1.71 src/sys/arch/amd64/conf/Makefile.amd64:1.72 --- src/sys/arch/amd64/conf/Makefile.amd64:1.71 Sat Jun 2 15:09:37 2018 +++ src/sys/arch/amd64/conf/Makefile.amd64 Mon Aug 20 15:04:51 2018 @@ -1,4 +1,4 @@ -# $NetBSD: Makefile.amd64,v 1.71 2018/06/02 15:09:37 christos Exp $ +# $NetBSD: Makefile.amd64,v 1.72 2018/08/20 15:04:51 maxv Exp $ # Makefile for NetBSD # @@ -49,6 +49,11 @@ CFLAGS+= -mindirect-branch=thunk-in CFLAGS+= -mindirect-branch-register .endif +.if ${KASAN:U0} > 0 && ${HAVE_GCC:U0} > 0 +CFLAGS+= -fsanitize=kernel-address --param asan-globals=1 +COPTS.asan.c+= -fno-sanitize=kernel-address +.endif + ## ## (3) libkern and compat ## Index: src/sys/arch/amd64/conf/files.amd64 diff -u src/sys/arch/amd64/conf/files.amd64:1.105 src/sys/arch/amd64/conf/files.amd64:1.106 --- src/sys/arch/amd64/conf/files.amd64:1.105 Fri Jul 13 09:37:32 2018 +++ src/sys/arch/amd64/conf/files.amd64 Mon Aug 20 15:04:51 2018 @@ -1,4 +1,4 @@ -# $NetBSD: files.amd64,v 1.105 2018/07/13 09:37:32 maxv Exp $ +# $NetBSD: files.amd64,v 1.106 2018/08/20 15:04:51 maxv Exp $ # # new style config file for amd64 architecture # @@ -39,6 +39,7 @@ file arch/amd64/amd64/spl.S machdep file arch/amd64/amd64/amd64func.S machdep file arch/amd64/amd64/amd64_trap.S machdep +file arch/amd64/amd64/asan.c kasan file arch/amd64/amd64/autoconf.c machdep file arch/amd64/amd64/busfunc.S machdep file arch/amd64/amd64/cpu_in_cksum.S (inet | inet6) & cpu_in_cksum Index: src/sys/arch/amd64/conf/kern.ldscript diff -u src/sys/arch/amd64/conf/kern.ldscript:1.26 src/sys/arch/amd64/conf/kern.ldscript:1.27 --- src/sys/arch/amd64/conf/kern.ldscript:1.26 Sun Jan 21 11:21:40 2018 +++ src/sys/arch/amd64/conf/kern.ldscript Mon Aug 20 15:04:51 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: kern.ldscript,v 1.26 2018/01/21 11:21:40 maxv Exp $ */ +/* $NetBSD: kern.ldscript,v 1.27 2018/08/20 15:04:51 maxv Exp $ */ #include "assym.h" @@ -48,6 +48,10 @@ SECTIONS { *(.rodata) *(.rodata.*) + . = ALIGN(COHERENCY_UNIT); + __CTOR_LIST__ = .; + *(.ctors) + __CTOR_END__ = .; } . = ALIGN(__LARGE_PAGE_SIZE); Index: src/sys/arch/amd64/include/pmap.h diff -u src/sys/arch/amd64/include/pmap.h:1.54 src/sys/arch/amd64/include/pmap.h:1.55 --- src/sys/arch/amd64/include/pmap.h:1.54 Fri Aug 17 14:39:51 2018 +++ src/sys/arch/amd64/include/pmap.h Mon Aug 20 15:04:51 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: pmap.h,v 1.54 2018/08/17 14:39:51 maxv Exp $ */ +/* $NetBSD: pmap.h,v 1.55 2018/08/20 15:04:51 maxv Exp $ */ /* * Copyright (c) 1997 Charles D. Cranor and Washington University. @@ -67,6 +67,7 @@ #if defined(_KERNEL_OPT) #include "opt_xen.h" +#include "opt_kasan.h" #endif #include <sys/atomic.h> @@ -91,6 +92,11 @@ /* XXXfvdl this one's not right. */ #define VA_SIGN_POS(va) ((va) & ~VA_SIGN_MASK) +#ifdef KASAN +#define L4_SLOT_KASAN 256 +#define NL4_SLOT_KASAN 32 +#endif + #ifndef XEN #define L4_SLOT_PTE slotspace.area[SLAREA_PTE].sslot #else Index: src/sys/arch/amd64/include/types.h diff -u src/sys/arch/amd64/include/types.h:1.56 src/sys/arch/amd64/include/types.h:1.57 --- src/sys/arch/amd64/include/types.h:1.56 Thu Jul 12 10:46:41 2018 +++ src/sys/arch/amd64/include/types.h Mon Aug 20 15:04:51 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: types.h,v 1.56 2018/07/12 10:46:41 maxv Exp $ */ +/* $NetBSD: types.h,v 1.57 2018/08/20 15:04:51 maxv Exp $ */ /*- * Copyright (c) 1990 The Regents of the University of California. @@ -99,11 +99,14 @@ typedef unsigned char __cpu_simple_lock #define __HAVE_RAS #include "opt_xen.h" +#include "opt_kasan.h" #if defined(__x86_64__) && !defined(XEN) +#if !defined(KASAN) #define __HAVE_PCPU_AREA 1 #define __HAVE_DIRECT_MAP 1 #define __HAVE_MM_MD_DIRECT_MAPPED_IO #define __HAVE_MM_MD_DIRECT_MAPPED_PHYS +#endif #if !defined(NO_PCI_MSI_MSIX) #define __HAVE_PCI_MSI_MSIX #endif Index: src/sys/arch/x86/include/pmap.h diff -u src/sys/arch/x86/include/pmap.h:1.84 src/sys/arch/x86/include/pmap.h:1.85 --- src/sys/arch/x86/include/pmap.h:1.84 Sun Aug 12 13:31:16 2018 +++ src/sys/arch/x86/include/pmap.h Mon Aug 20 15:04:52 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: pmap.h,v 1.84 2018/08/12 13:31:16 maxv Exp $ */ +/* $NetBSD: pmap.h,v 1.85 2018/08/20 15:04:52 maxv Exp $ */ /* * Copyright (c) 1997 Charles D. Cranor and Washington University. @@ -161,8 +161,9 @@ struct bootspace { #define SLAREA_PCPU 4 #define SLAREA_DMAP 5 #define SLAREA_HYPV 6 -#define SLAREA_KERN 7 -#define SLSPACE_NAREAS 8 +#define SLAREA_ASAN 7 +#define SLAREA_KERN 8 +#define SLSPACE_NAREAS 9 struct slotspace { struct { @@ -552,6 +553,8 @@ int pmap_enter_ma(struct pmap *, vaddr_t bool pmap_extract_ma(pmap_t, vaddr_t, paddr_t *); void pmap_free_ptps(struct vm_page *); +paddr_t pmap_get_physpage(void); + /* * Hooks for the pool allocator. */ Index: src/sys/arch/x86/x86/pmap.c diff -u src/sys/arch/x86/x86/pmap.c:1.303 src/sys/arch/x86/x86/pmap.c:1.304 --- src/sys/arch/x86/x86/pmap.c:1.303 Sat Aug 18 08:45:55 2018 +++ src/sys/arch/x86/x86/pmap.c Mon Aug 20 15:04:52 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: pmap.c,v 1.303 2018/08/18 08:45:55 maxv Exp $ */ +/* $NetBSD: pmap.c,v 1.304 2018/08/20 15:04:52 maxv Exp $ */ /* * Copyright (c) 2008, 2010, 2016, 2017 The NetBSD Foundation, Inc. @@ -157,13 +157,14 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: pmap.c,v 1.303 2018/08/18 08:45:55 maxv Exp $"); +__KERNEL_RCSID(0, "$NetBSD: pmap.c,v 1.304 2018/08/20 15:04:52 maxv Exp $"); #include "opt_user_ldt.h" #include "opt_lockdebug.h" #include "opt_multiprocessor.h" #include "opt_xen.h" #include "opt_svs.h" +#include "opt_kasan.h" #include <sys/param.h> #include <sys/systm.h> @@ -570,7 +571,6 @@ static bool pmap_remove_pte(struct pmap static void pmap_remove_ptes(struct pmap *, struct vm_page *, vaddr_t, vaddr_t, vaddr_t, struct pv_entry **); -static paddr_t pmap_get_physpage(void); static void pmap_alloc_level(struct pmap *, vaddr_t, long *); static void pmap_reactivate(struct pmap *); @@ -1386,7 +1386,7 @@ pmap_pagetree_nentries_range(vaddr_t sta } #endif -#if defined(__HAVE_DIRECT_MAP) +#if defined(__HAVE_DIRECT_MAP) || defined(KASAN) static inline void slotspace_copy(int type, pd_entry_t *dst, pd_entry_t *src) { @@ -2377,6 +2377,9 @@ pmap_pdp_ctor(void *arg, void *v, int fl #ifdef __HAVE_DIRECT_MAP slotspace_copy(SLAREA_DMAP, pdir, PDP_BASE); #endif +#ifdef KASAN + slotspace_copy(SLAREA_ASAN, pdir, PDP_BASE); +#endif #endif /* XEN && __x86_64__*/ #ifdef XEN @@ -4470,7 +4473,7 @@ out: return error; } -static paddr_t +paddr_t pmap_get_physpage(void) { struct vm_page *ptp; @@ -4649,6 +4652,12 @@ pmap_growkernel(vaddr_t maxkvaddr) } #endif +#ifdef KASAN + void kasan_shadow_map(void *, size_t); + kasan_shadow_map((void *)pmap_maxkvaddr, + (size_t)(maxkvaddr - pmap_maxkvaddr)); +#endif + pmap_alloc_level(cpm, pmap_maxkvaddr, needed_kptp); /* Index: src/sys/conf/files diff -u src/sys/conf/files:1.1203 src/sys/conf/files:1.1204 --- src/sys/conf/files:1.1203 Tue Aug 14 14:49:13 2018 +++ src/sys/conf/files Mon Aug 20 15:04:52 2018 @@ -1,4 +1,4 @@ -# $NetBSD: files,v 1.1203 2018/08/14 14:49:13 maxv Exp $ +# $NetBSD: files,v 1.1204 2018/08/20 15:04:52 maxv Exp $ # @(#)files.newconf 7.5 (Berkeley) 5/10/93 version 20171118 @@ -29,6 +29,7 @@ defflag KEYLOCK defparam opt_syslimits.h CHILD_MAX OPEN_MAX defflag opt_diagnostic.h _DIAGNOSTIC defflag GPROF +defflag KASAN defparam opt_copy_symtab.h makeoptions_COPY_SYMTAB Index: src/sys/kern/kern_malloc.c diff -u src/sys/kern/kern_malloc.c:1.147 src/sys/kern/kern_malloc.c:1.148 --- src/sys/kern/kern_malloc.c:1.147 Mon Aug 20 11:46:44 2018 +++ src/sys/kern/kern_malloc.c Mon Aug 20 15:04:52 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: kern_malloc.c,v 1.147 2018/08/20 11:46:44 maxv Exp $ */ +/* $NetBSD: kern_malloc.c,v 1.148 2018/08/20 15:04:52 maxv Exp $ */ /* * Copyright (c) 1987, 1991, 1993 @@ -70,12 +70,18 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: kern_malloc.c,v 1.147 2018/08/20 11:46:44 maxv Exp $"); +__KERNEL_RCSID(0, "$NetBSD: kern_malloc.c,v 1.148 2018/08/20 15:04:52 maxv Exp $"); + +#include "opt_kasan.h" #include <sys/param.h> #include <sys/malloc.h> #include <sys/kmem.h> +#ifdef KASAN +#include <sys/asan.h> +#endif + /* * Built-in malloc types. Note: ought to be removed. */ @@ -100,10 +106,17 @@ void * kern_malloc(unsigned long size, int flags) { const int kmflags = (flags & M_NOWAIT) ? KM_NOSLEEP : KM_SLEEP; +#ifdef KASAN + size_t origsize = size; +#endif size_t allocsize, hdroffset; struct malloc_header *mh; void *p; +#ifdef KASAN + kasan_add_redzone(&size); +#endif + if (size >= PAGE_SIZE) { if (size > (ULONG_MAX-PAGE_SIZE)) allocsize = ULONG_MAX; /* this will fail later */ @@ -126,6 +139,10 @@ kern_malloc(unsigned long size, int flag mh->mh_size = allocsize - hdroffset; mh++; +#ifdef KASAN + kasan_alloc(mh, origsize, size); +#endif + return mh; } @@ -137,6 +154,10 @@ kern_free(void *addr) mh = addr; mh--; +#ifdef KASAN + kasan_free(addr, mh->mh_size); +#endif + if (mh->mh_size >= PAGE_SIZE + sizeof(struct malloc_header)) kmem_intr_free((char *)addr - PAGE_SIZE, mh->mh_size + PAGE_SIZE - sizeof(struct malloc_header)); Index: src/sys/kern/subr_kmem.c diff -u src/sys/kern/subr_kmem.c:1.68 src/sys/kern/subr_kmem.c:1.69 --- src/sys/kern/subr_kmem.c:1.68 Mon Aug 20 11:46:44 2018 +++ src/sys/kern/subr_kmem.c Mon Aug 20 15:04:52 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: subr_kmem.c,v 1.68 2018/08/20 11:46:44 maxv Exp $ */ +/* $NetBSD: subr_kmem.c,v 1.69 2018/08/20 15:04:52 maxv Exp $ */ /*- * Copyright (c) 2009-2015 The NetBSD Foundation, Inc. @@ -92,10 +92,11 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: subr_kmem.c,v 1.68 2018/08/20 11:46:44 maxv Exp $"); +__KERNEL_RCSID(0, "$NetBSD: subr_kmem.c,v 1.69 2018/08/20 15:04:52 maxv Exp $"); #ifdef _KERNEL_OPT #include "opt_kmem.h" +#include "opt_kasan.h" #endif #include <sys/param.h> @@ -106,6 +107,10 @@ __KERNEL_RCSID(0, "$NetBSD: subr_kmem.c, #include <sys/lockdebug.h> #include <sys/cpu.h> +#ifdef KASAN +#include <sys/asan.h> +#endif + #include <uvm/uvm_extern.h> #include <uvm/uvm_map.h> @@ -222,6 +227,9 @@ CTASSERT(KM_NOSLEEP == PR_NOWAIT); void * kmem_intr_alloc(size_t requested_size, km_flag_t kmflags) { +#ifdef KASAN + size_t origsize = requested_size; +#endif size_t allocsz, index; size_t size; pool_cache_t pc; @@ -239,6 +247,10 @@ kmem_intr_alloc(size_t requested_size, k } #endif +#ifdef KASAN + kasan_add_redzone(&requested_size); +#endif + size = kmem_roundup_size(requested_size); allocsz = size + SIZE_SIZE; @@ -266,7 +278,9 @@ kmem_intr_alloc(size_t requested_size, k FREECHECK_OUT(&kmem_freecheck, p); kmem_size_set(p, requested_size); p += SIZE_SIZE; - +#ifdef KASAN + kasan_alloc(p, origsize, size); +#endif return p; } return p; @@ -309,9 +323,17 @@ kmem_intr_free(void *p, size_t requested } #endif +#ifdef KASAN + kasan_add_redzone(&requested_size); +#endif + size = kmem_roundup_size(requested_size); allocsz = size + SIZE_SIZE; +#ifdef KASAN + kasan_free(p, size); +#endif + if ((index = ((allocsz -1) >> KMEM_SHIFT)) < kmem_cache_maxidx) { pc = kmem_cache[index]; Index: src/sys/lib/libkern/libkern.h diff -u src/sys/lib/libkern/libkern.h:1.127 src/sys/lib/libkern/libkern.h:1.128 --- src/sys/lib/libkern/libkern.h:1.127 Sun Jul 8 17:54:42 2018 +++ src/sys/lib/libkern/libkern.h Mon Aug 20 15:04:52 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: libkern.h,v 1.127 2018/07/08 17:54:42 christos Exp $ */ +/* $NetBSD: libkern.h,v 1.128 2018/08/20 15:04:52 maxv Exp $ */ /*- * Copyright (c) 1992, 1993 @@ -36,6 +36,7 @@ #ifdef _KERNEL_OPT #include "opt_diagnostic.h" +#include "opt_kasan.h" #endif #include <sys/types.h> @@ -368,11 +369,18 @@ int memcmp(const void *, const void *, void *memset(void *, int, size_t); void *memmem(const void *, size_t, const void *, size_t); #if __GNUC_PREREQ__(2, 95) && !defined(_STANDALONE) +#if defined(_KERNEL) && defined(KASAN) +void *kasan_memset(void *, int, size_t); +int kasan_memcmp(const void *, const void *, size_t); +void *kasan_memcpy(void *, const void *, size_t); +#define memcpy(d, s, l) kasan_memcpy(d, s, l) +#define memcmp(a, b, l) kasan_memcmp(a, b, l) +#define memset(d, v, l) kasan_memset(d, v, l) +#else #define memcpy(d, s, l) __builtin_memcpy(d, s, l) #define memcmp(a, b, l) __builtin_memcmp(a, b, l) -#endif -#if __GNUC_PREREQ__(2, 95) && !defined(_STANDALONE) #define memset(d, v, l) __builtin_memset(d, v, l) +#endif /* _KERNEL && KASAN */ #endif char *strcpy(char *, const char *); Index: src/sys/sys/Makefile diff -u src/sys/sys/Makefile:1.166 src/sys/sys/Makefile:1.167 --- src/sys/sys/Makefile:1.166 Thu Jul 12 10:46:48 2018 +++ src/sys/sys/Makefile Mon Aug 20 15:04:52 2018 @@ -1,10 +1,11 @@ -# $NetBSD: Makefile,v 1.166 2018/07/12 10:46:48 maxv Exp $ +# $NetBSD: Makefile,v 1.167 2018/08/20 15:04:52 maxv Exp $ .include <bsd.own.mk> INCSDIR= /usr/include/sys -INCS= acct.h agpio.h aio.h ansi.h aout_mids.h ataio.h atomic.h audioio.h \ +INCS= acct.h agpio.h aio.h ansi.h aout_mids.h asan.h ataio.h atomic.h \ + audioio.h \ bitops.h bootblock.h bswap.h buf.h \ callback.h callout.h cdbr.h cdefs.h cdefs_aout.h \ cdefs_elf.h cdio.h chio.h clock.h clockctl.h \ Added files: Index: src/sys/arch/amd64/amd64/asan.c diff -u /dev/null src/sys/arch/amd64/amd64/asan.c:1.1 --- /dev/null Mon Aug 20 15:04:52 2018 +++ src/sys/arch/amd64/amd64/asan.c Mon Aug 20 15:04:51 2018 @@ -0,0 +1,591 @@ +/* $NetBSD: asan.c,v 1.1 2018/08/20 15:04:51 maxv Exp $ */ + +/* + * Copyright (c) 2018 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Maxime Villard, and Siddharth Muralee. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: asan.c,v 1.1 2018/08/20 15:04:51 maxv Exp $"); + +#include <sys/param.h> +#include <sys/device.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/param.h> +#include <sys/conf.h> +#include <sys/systm.h> +#include <sys/types.h> +#include <sys/asan.h> + +#include <uvm/uvm.h> +#include <amd64/pmap.h> +#include <amd64/vmparam.h> + +#define VIRTUAL_SHIFT 47 /* 48bit address space, cut half */ +#define CANONICAL_BASE 0xFFFF800000000000 + +#define KASAN_SHADOW_SCALE_SHIFT 3 +#define KASAN_SHADOW_SCALE_SIZE (1UL << KASAN_SHADOW_SCALE_SHIFT) +#define KASAN_SHADOW_MASK (KASAN_SHADOW_SCALE_SIZE - 1) + +#define KASAN_SHADOW_SIZE (1ULL << (VIRTUAL_SHIFT - KASAN_SHADOW_SCALE_SHIFT)) +#define KASAN_SHADOW_START (VA_SIGN_NEG((L4_SLOT_KASAN * NBPD_L4))) +#define KASAN_SHADOW_END (KASAN_SHADOW_START + KASAN_SHADOW_SIZE) + +#define __RET_ADDR (unsigned long)__builtin_return_address(0) + +void kasan_shadow_map(void *, size_t); +void kasan_init(void); + +static bool kasan_enabled __read_mostly = false; + +static inline int8_t *kasan_addr_to_shad(const void *addr) +{ + vaddr_t va = (vaddr_t)addr; + return (int8_t *)(KASAN_SHADOW_START + + ((va - CANONICAL_BASE) >> KASAN_SHADOW_SCALE_SHIFT)); +} + +static __always_inline bool +kasan_unsupported(vaddr_t addr) +{ + return (addr >= (vaddr_t)PTE_BASE && + addr < ((vaddr_t)PTE_BASE + NBPD_L4)); +} + +static void +kasan_shadow_map_page(vaddr_t va) +{ + paddr_t pa; + + if (!pmap_valid_entry(L4_BASE[pl4_i(va)])) { + pa = pmap_get_physpage(); + L4_BASE[pl4_i(va)] = pa | PG_KW | pmap_pg_nx | PG_V; + } + if (!pmap_valid_entry(L3_BASE[pl3_i(va)])) { + pa = pmap_get_physpage(); + L3_BASE[pl3_i(va)] = pa | PG_KW | pmap_pg_nx | PG_V; + } + if (!pmap_valid_entry(L2_BASE[pl2_i(va)])) { + pa = pmap_get_physpage(); + L2_BASE[pl2_i(va)] = pa | PG_KW | pmap_pg_nx | PG_V; + } + if (!pmap_valid_entry(L1_BASE[pl1_i(va)])) { + pa = pmap_get_physpage(); + L1_BASE[pl1_i(va)] = pa | PG_KW | pmap_pg_g | pmap_pg_nx | PG_V; + } +} + +/* + * Allocate the necessary stuff in the shadow, so that we can monitor the + * passed area. + */ +void +kasan_shadow_map(void *addr, size_t size) +{ + size_t sz, npages, i; + vaddr_t va; + + va = (vaddr_t)kasan_addr_to_shad(addr); + sz = roundup(size, KASAN_SHADOW_SCALE_SIZE) / KASAN_SHADOW_SCALE_SIZE; + va = rounddown(va, PAGE_SIZE); + npages = roundup(sz, PAGE_SIZE) / PAGE_SIZE; + + KASSERT(va >= KASAN_SHADOW_START && va < KASAN_SHADOW_END); + + for (i = 0; i < npages; i++) { + kasan_shadow_map_page(va + i * PAGE_SIZE); + } +} + +/* -------------------------------------------------------------------------- */ + +#ifdef __HAVE_PCPU_AREA +#error "PCPU area not allowed with KASAN" +#endif +#ifdef __HAVE_DIRECT_MAP +#error "DMAP not allowed with KASAN" +#endif + +static void +kasan_ctors(void) +{ + extern uint64_t __CTOR_LIST__, __CTOR_END__; + size_t nentries, i; + uint64_t *ptr; + + nentries = ((size_t)&__CTOR_END__ - (size_t)&__CTOR_LIST__) / + sizeof(uintptr_t); + + ptr = &__CTOR_LIST__; + for (i = 0; i < nentries; i++) { + void (*func)(void); + + func = (void *)(*ptr); + (*func)(); + + ptr++; + } +} + +/* + * Create the shadow mapping. We don't create the 'User' area, because we + * exclude it from the monitoring. The 'Main' area is created dynamically + * in pmap_growkernel. + */ +void +kasan_init(void) +{ + extern struct bootspace bootspace; + size_t i; + + CTASSERT((KASAN_SHADOW_SIZE / NBPD_L4) == NL4_SLOT_KASAN); + + /* Kernel. */ + for (i = 0; i < BTSPACE_NSEGS; i++) { + if (bootspace.segs[i].type == BTSEG_NONE) { + continue; + } + kasan_shadow_map((void *)bootspace.segs[i].va, + bootspace.segs[i].sz); + } + + /* Boot region. */ + kasan_shadow_map((void *)bootspace.boot.va, bootspace.boot.sz); + + /* Module map. */ + kasan_shadow_map((void *)bootspace.smodule, + (size_t)(bootspace.emodule - bootspace.smodule)); + + /* The bootstrap spare va. */ + kasan_shadow_map((void *)bootspace.spareva, PAGE_SIZE); + + kasan_enabled = true; + + /* Call the ASAN constructors. */ + kasan_ctors(); +} + +/* -------------------------------------------------------------------------- */ + +static void +kasan_report(unsigned long addr, size_t size, bool write, unsigned long rip) +{ + printf("kASan: Unauthorized Access In %p: Addr %p [%zu byte%s, %s]\n", + (void *)rip, (void *)addr, size, (size > 1 ? "s" : ""), + (write ? "write" : "read")); +} + +/* -------------------------------------------------------------------------- */ + +/* Our redzone values. */ +#define KASAN_GLOBAL_REDZONE 0xFA +#define KASAN_MEMORY_REDZONE 0xFB + +/* Stack redzone shadow values. Part of the compiler ABI. */ +#define KASAN_STACK_LEFT 0xF1 +#define KASAN_STACK_MID 0xF2 +#define KASAN_STACK_RIGHT 0xF3 +#define KASAN_STACK_PARTIAL 0xF4 +#define KASAN_USE_AFTER_SCOPE 0xF8 + +static void +kasan_shadow_fill(const void *addr, size_t size, uint8_t val) +{ + void *shad; + + if (__predict_false(!kasan_enabled)) + return; + if (__predict_false(size == 0)) + return; + if (__predict_false(kasan_unsupported((vaddr_t)addr))) + return; + + KASSERT((vaddr_t)addr % KASAN_SHADOW_SCALE_SIZE == 0); + KASSERT(size % KASAN_SHADOW_SCALE_SIZE == 0); + + shad = (void *)kasan_addr_to_shad(addr); + size = size >> KASAN_SHADOW_SCALE_SHIFT; + + __builtin_memset(shad, val, size); +} + +static __always_inline void +kasan_shadow_1byte_markvalid(unsigned long addr) +{ + int8_t *byte = kasan_addr_to_shad((void *)addr); + int8_t last = (addr & KASAN_SHADOW_MASK) + 1; + + *byte = last; +} + +void +kasan_add_redzone(size_t *size) +{ + *size = roundup(*size, KASAN_SHADOW_SCALE_SIZE); + *size += KASAN_SHADOW_SCALE_SIZE; +} + +static void +kasan_markmem(const void *addr, size_t size, bool valid) +{ + size_t i; + + KASSERT((vaddr_t)addr % KASAN_SHADOW_SCALE_SIZE == 0); + + if (valid) { + for (i = 0; i < size; i++) { + kasan_shadow_1byte_markvalid((unsigned long)addr+i); + } + } else { + KASSERT(size % KASAN_SHADOW_SCALE_SIZE == 0); + kasan_shadow_fill(addr, size, KASAN_MEMORY_REDZONE); + } +} + +void +kasan_alloc(const void *addr, size_t size, size_t sz_with_redz) +{ + kasan_markmem(addr, sz_with_redz, false); + kasan_markmem(addr, size, true); +} + +void +kasan_free(const void *addr, size_t sz_with_redz) +{ + kasan_markmem(addr, sz_with_redz, true); +} + +/* -------------------------------------------------------------------------- */ + +#define ADDR_CROSSES_SCALE_BOUNDARY(addr, size) \ + (addr >> KASAN_SHADOW_SCALE_SHIFT) != \ + ((addr + size - 1) >> KASAN_SHADOW_SCALE_SHIFT) + +static __always_inline bool +kasan_shadow_1byte_isvalid(unsigned long addr) +{ + int8_t *byte = kasan_addr_to_shad((void *)addr); + int8_t last = (addr & KASAN_SHADOW_MASK) + 1; + + return __predict_true(*byte == 0 || last <= *byte); +} + +static __always_inline bool +kasan_shadow_2byte_isvalid(unsigned long addr) +{ + int8_t *byte, last; + + if (ADDR_CROSSES_SCALE_BOUNDARY(addr, 2)) { + return (kasan_shadow_1byte_isvalid(addr) && + kasan_shadow_1byte_isvalid(addr+1)); + } + + byte = kasan_addr_to_shad((void *)addr); + last = ((addr + 1) & KASAN_SHADOW_MASK) + 1; + + return __predict_true(*byte == 0 || last <= *byte); +} + +static __always_inline bool +kasan_shadow_4byte_isvalid(unsigned long addr) +{ + int8_t *byte, last; + + if (ADDR_CROSSES_SCALE_BOUNDARY(addr, 4)) { + return (kasan_shadow_2byte_isvalid(addr) && + kasan_shadow_2byte_isvalid(addr+2)); + } + + byte = kasan_addr_to_shad((void *)addr); + last = ((addr + 3) & KASAN_SHADOW_MASK) + 1; + + return __predict_true(*byte == 0 || last <= *byte); +} + +static __always_inline bool +kasan_shadow_8byte_isvalid(unsigned long addr) +{ + int8_t *byte, last; + + if (ADDR_CROSSES_SCALE_BOUNDARY(addr, 8)) { + return (kasan_shadow_4byte_isvalid(addr) && + kasan_shadow_4byte_isvalid(addr+4)); + } + + byte = kasan_addr_to_shad((void *)addr); + last = ((addr + 7) & KASAN_SHADOW_MASK) + 1; + + return __predict_true(*byte == 0 || last <= *byte); +} + +static __always_inline bool +kasan_shadow_Nbyte_isvalid(unsigned long addr, size_t size) +{ + size_t i; + + for (i = 0; i < size; i++) { + if (!kasan_shadow_1byte_isvalid(addr+i)) + return false; + } + + return true; +} + +static __always_inline void +kasan_shadow_check(unsigned long addr, size_t size, bool write, + unsigned long retaddr) +{ + bool valid; + + if (__predict_false(!kasan_enabled)) + return; + if (__predict_false(size == 0)) + return; + if (__predict_false(kasan_unsupported(addr))) + return; + + if (__builtin_constant_p(size)) { + switch (size) { + case 1: + valid = kasan_shadow_1byte_isvalid(addr); + break; + case 2: + valid = kasan_shadow_2byte_isvalid(addr); + break; + case 4: + valid = kasan_shadow_4byte_isvalid(addr); + break; + case 8: + valid = kasan_shadow_8byte_isvalid(addr); + break; + default: + valid = kasan_shadow_Nbyte_isvalid(addr, size); + break; + } + } else { + valid = kasan_shadow_Nbyte_isvalid(addr, size); + } + + if (__predict_false(!valid)) { + kasan_report(addr, size, write, retaddr); + } +} + +/* -------------------------------------------------------------------------- */ + +void * +kasan_memcpy(void *dst, const void *src, size_t len) +{ + kasan_shadow_check((unsigned long)src, len, false, __RET_ADDR); + kasan_shadow_check((unsigned long)dst, len, true, __RET_ADDR); + return __builtin_memcpy(dst, src, len); +} + +int +kasan_memcmp(const void *b1, const void *b2, size_t len) +{ + kasan_shadow_check((unsigned long)b1, len, false, __RET_ADDR); + kasan_shadow_check((unsigned long)b2, len, false, __RET_ADDR); + return __builtin_memcmp(b1, b2, len); +} + +void * +kasan_memset(void *b, int c, size_t len) +{ + kasan_shadow_check((unsigned long)b, len, true, __RET_ADDR); + return __builtin_memset(b, c, len); +} + +/* -------------------------------------------------------------------------- */ + +#if defined(__clang__) && (__clang_major__ - 0 >= 6) +#define ASAN_ABI_VERSION 8 +#elif __GNUC_PREREQ__(7, 1) && !defined(__clang__) +#define ASAN_ABI_VERSION 8 +#elif __GNUC_PREREQ__(6, 1) && !defined(__clang__) +#define ASAN_ABI_VERSION 6 +#else +#error "Unsupported compiler version" +#endif + +/* + * Part of the compiler ABI. + */ +struct __asan_global_source_location { + const char *filename; + int line_no; + int column_no; +}; +struct __asan_global { + const void *beg; /* address of the global variable */ + size_t size; /* size of the global variable */ + size_t size_with_redzone; /* size with the redzone */ + const void *name; /* name of the variable */ + const void *module_name; /* name of the module where the var is declared */ + unsigned long has_dynamic_init; /* the var has dyn initializer (c++) */ + struct __asan_global_source_location *location; +#if ASAN_ABI_VERSION >= 7 + uintptr_t odr_indicator; /* the address of the ODR indicator symbol */ +#endif +}; + +void __asan_register_globals(struct __asan_global *, size_t); +void __asan_unregister_globals(struct __asan_global *, size_t); + +static void +kasan_register_global(struct __asan_global *global) +{ + size_t aligned_size = roundup(global->size, KASAN_SHADOW_SCALE_SIZE); + + /* Poison the redzone following the var. */ + kasan_shadow_fill((void *)((uintptr_t)global->beg + aligned_size), + global->size_with_redzone - aligned_size, KASAN_GLOBAL_REDZONE); +} + +void +__asan_register_globals(struct __asan_global *globals, size_t size) +{ + size_t i; + for (i = 0; i < size; i++) { + kasan_register_global(&globals[i]); + } +} + +void +__asan_unregister_globals(struct __asan_global *globals, size_t size) +{ +} + +#define ASAN_LOAD_STORE(size) \ + void __asan_load##size(unsigned long); \ + void __asan_load##size(unsigned long addr) \ + { \ + kasan_shadow_check(addr, size, false, __RET_ADDR);\ + } \ + void __asan_load##size##_noabort(unsigned long); \ + void __asan_load##size##_noabort(unsigned long addr) \ + { \ + kasan_shadow_check(addr, size, false, __RET_ADDR);\ + } \ + void __asan_store##size(unsigned long); \ + void __asan_store##size(unsigned long addr) \ + { \ + kasan_shadow_check(addr, size, true, __RET_ADDR);\ + } \ + void __asan_store##size##_noabort(unsigned long); \ + void __asan_store##size##_noabort(unsigned long addr) \ + { \ + kasan_shadow_check(addr, size, true, __RET_ADDR);\ + } + +ASAN_LOAD_STORE(1); +ASAN_LOAD_STORE(2); +ASAN_LOAD_STORE(4); +ASAN_LOAD_STORE(8); +ASAN_LOAD_STORE(16); + +void __asan_loadN(unsigned long, size_t); +void __asan_loadN_noabort(unsigned long, size_t); +void __asan_storeN(unsigned long, size_t); +void __asan_storeN_noabort(unsigned long, size_t); +void __asan_handle_no_return(void); +void __asan_poison_stack_memory(const void *, size_t); +void __asan_unpoison_stack_memory(const void *, size_t); +void __asan_alloca_poison(unsigned long, size_t); +void __asan_allocas_unpoison(const void *, const void *); + +void +__asan_loadN(unsigned long addr, size_t size) +{ + kasan_shadow_check(addr, size, false, __RET_ADDR); +} + +void +__asan_loadN_noabort(unsigned long addr, size_t size) +{ + kasan_shadow_check(addr, size, false, __RET_ADDR); +} + +void +__asan_storeN(unsigned long addr, size_t size) +{ + kasan_shadow_check(addr, size, true, __RET_ADDR); +} + +void +__asan_storeN_noabort(unsigned long addr, size_t size) +{ + kasan_shadow_check(addr, size, true, __RET_ADDR); +} + +void +__asan_handle_no_return(void) +{ + /* nothing */ +} + +void +__asan_poison_stack_memory(const void *addr, size_t size) +{ + KASSERT((vaddr_t)addr % KASAN_SHADOW_SCALE_SIZE == 0); + kasan_shadow_fill(addr, size, KASAN_USE_AFTER_SCOPE); +} + +void +__asan_unpoison_stack_memory(const void *addr, size_t size) +{ + KASSERT((vaddr_t)addr % KASAN_SHADOW_SCALE_SIZE == 0); + kasan_shadow_fill(addr, size, 0); +} + +void +__asan_alloca_poison(unsigned long addr, size_t size) +{ + panic("%s: impossible!", __func__); +} + +void +__asan_allocas_unpoison(const void *stack_top, const void *stack_bottom) +{ + panic("%s: impossible!", __func__); +} + +#define ASAN_SET_SHADOW(byte) \ + void __asan_set_shadow_##byte(void *, size_t); \ + void __asan_set_shadow_##byte(void *addr, size_t size) \ + { \ + __builtin_memset((void *)addr, 0x##byte, size); \ + } + +ASAN_SET_SHADOW(00); +ASAN_SET_SHADOW(f1); +ASAN_SET_SHADOW(f2); +ASAN_SET_SHADOW(f3); +ASAN_SET_SHADOW(f5); +ASAN_SET_SHADOW(f8); Index: src/sys/sys/asan.h diff -u /dev/null src/sys/sys/asan.h:1.1 --- /dev/null Mon Aug 20 15:04:52 2018 +++ src/sys/sys/asan.h Mon Aug 20 15:04:52 2018 @@ -0,0 +1,41 @@ +/* $NetBSD: asan.h,v 1.1 2018/08/20 15:04:52 maxv Exp $ */ + +/* + * Copyright (c) 2018 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Maxime Villard. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SYS_ASAN_H_ +#define _SYS_ASAN_H_ + +#include <sys/types.h> + +void kasan_add_redzone(size_t *); +void kasan_alloc(const void *, size_t, size_t); +void kasan_free(const void *, size_t); + +#endif /* !_SYS_ASAN_H_ */