diff --git a/arch/arm/src/Makefile b/arch/arm/src/Makefile index d1a15e75fcc..d00fbfea591 100644 --- a/arch/arm/src/Makefile +++ b/arch/arm/src/Makefile @@ -180,13 +180,27 @@ $(KBIN): $(OBJS) board$(DELIM)libboard$(LIBEXT): $(Q) $(MAKE) -C board libboard$(LIBEXT) EXTRAFLAGS="$(EXTRAFLAGS)" -define LINK_ALLSYMS +# When multiple linking, these two additional linking objects will be included + +ifeq ($(CONFIG_MM_KASAN_GLOBAL),y) +EXTRA_LIBS += kasan_globals$(OBJEXT) +endif +ifeq ($(CONFIG_ALLSYMS),y) +EXTRA_LIBS += allsyms$(OBJEXT) +endif + +define LINK_ALLSYMS_KASAN + $(if $(CONFIG_ALLSYMS), $(Q) $(TOPDIR)/tools/mkallsyms.py $(NUTTX) allsyms.tmp --orderbyname $(CONFIG_SYMTAB_ORDEREDBYNAME) $(Q) $(call COMPILE, allsyms.tmp, allsyms$(OBJEXT), -x c) + $(Q) $(call DELFILE, allsyms.tmp)) + $(if $(CONFIG_MM_KASAN_GLOBAL), + $(Q) $(TOPDIR)/tools/kasan_global.py -e $(NUTTX) -o kasan_globals.tmp + $(Q) $(call COMPILE, kasan_globals.tmp, kasan_globals$(OBJEXT) -fno-sanitize=kernel-address, -x c) + $(Q) $(call DELFILE, kasan_globals.tmp)) $(Q) $(LD) --entry=__start $(LDFLAGS) $(LIBPATHS) $(EXTRA_LIBPATHS) \ - -o $(NUTTX) $(HEAD_OBJ) allsyms$(OBJEXT) $(EXTRA_OBJS) \ + -o $(NUTTX) $(HEAD_OBJ) $(EXTRA_OBJS) \ $(LDSTARTGROUP) $(LDLIBS) $(EXTRA_LIBS) $(LDENDGROUP) - $(Q) $(call DELFILE, allsyms.tmp allsyms$(OBJEXT)) endef $(addsuffix .tmp,$(ARCHSCRIPT)): $(ARCHSCRIPT) @@ -194,17 +208,19 @@ $(addsuffix .tmp,$(ARCHSCRIPT)): $(ARCHSCRIPT) nuttx$(EXEEXT): $(HEAD_OBJ) board$(DELIM)libboard$(LIBEXT) $(addsuffix .tmp,$(ARCHSCRIPT)) $(Q) echo "LD: nuttx" -ifneq ($(CONFIG_ALLSYMS),y) +ifeq ($(CONFIG_ALLSYMS)$(CONFIG_MM_KASAN_GLOBAL),) $(Q) $(LD) --entry=__start $(LDFLAGS) $(LIBPATHS) $(EXTRA_LIBPATHS) \ -o $(NUTTX) $(HEAD_OBJ) $(EXTRA_OBJS) \ $(LDSTARTGROUP) $(LDLIBS) $(EXTRA_LIBS) $(LDENDGROUP) else - $(Q) # Link and generate default table - $(Q) $(if $(wildcard $(shell echo $(NUTTX))),,$(call LINK_ALLSYMS,$^)) - $(Q) # Extract all symbols - $(Q) $(call LINK_ALLSYMS, $^) - $(Q) # Extract again since the table offset may changed - $(Q) $(call LINK_ALLSYMS, $^) + $(Q) $(call LINK_ALLSYMS_KASAN) + $(Q) $(call LINK_ALLSYMS_KASAN) + $(Q) $(call LINK_ALLSYMS_KASAN) + $(Q) $(call LINK_ALLSYMS_KASAN) +endif +ifeq ($(CONFIG_MM_KASAN_GLOBAL),y) + $(Q) $(OBJCOPY) -R .kasan.global $(NUTTX) + $(Q) $(OBJCOPY) -R .kasan.unused $(NUTTX) endif ifneq ($(CONFIG_WINDOWS_NATIVE),y) $(Q) $(NM) $(NUTTX) | \ diff --git a/arch/arm/src/common/Toolchain.defs b/arch/arm/src/common/Toolchain.defs index 6ac51393f5a..22c65a5ee8f 100644 --- a/arch/arm/src/common/Toolchain.defs +++ b/arch/arm/src/common/Toolchain.defs @@ -78,6 +78,10 @@ ifeq ($(CONFIG_MM_KASAN_ALL),y) ARCHOPTIMIZATION += -fsanitize=kernel-address endif +ifeq ($(CONFIG_MM_KASAN_GLOBAL),y) + ARCHOPTIMIZATION += --param asan-globals=1 +endif + ifeq ($(CONFIG_MM_KASAN_DISABLE_READS_CHECK),y) ARCHOPTIMIZATION += --param asan-instrument-reads=0 endif diff --git a/arch/arm64/src/Makefile b/arch/arm64/src/Makefile index 70359823d1b..15f5b7b02df 100644 --- a/arch/arm64/src/Makefile +++ b/arch/arm64/src/Makefile @@ -156,13 +156,27 @@ $(KBIN): $(OBJS) board$(DELIM)libboard$(LIBEXT): $(Q) $(MAKE) -C board libboard$(LIBEXT) EXTRAFLAGS="$(EXTRAFLAGS)" -define LINK_ALLSYMS +# When multiple linking, these two additional linking objects will be included + +ifeq ($(CONFIG_MM_KASAN_GLOBAL),y) +EXTRA_LIBS += kasan_globals$(OBJEXT) +endif +ifeq ($(CONFIG_ALLSYMS),y) +EXTRA_LIBS += allsyms$(OBJEXT) +endif + +define LINK_ALLSYMS_KASAN + $(if $(CONFIG_ALLSYMS), $(Q) $(TOPDIR)/tools/mkallsyms.py $(NUTTX) allsyms.tmp --orderbyname $(CONFIG_SYMTAB_ORDEREDBYNAME) $(Q) $(call COMPILE, allsyms.tmp, allsyms$(OBJEXT), -x c) + $(Q) $(call DELFILE, allsyms.tmp)) + $(if $(CONFIG_MM_KASAN_GLOBAL), + $(Q) $(TOPDIR)/tools/kasan_global.py -e $(NUTTX) -o kasan_globals.tmp + $(Q) $(call COMPILE, kasan_globals.tmp, kasan_globals$(OBJEXT) -fno-sanitize=kernel-address, -x c) + $(Q) $(call DELFILE, kasan_globals.tmp)) $(Q) $(LD) --entry=__start $(LDFLAGS) $(LIBPATHS) $(EXTRA_LIBPATHS) \ - -o $(NUTTX) $(HEAD_OBJ) allsyms$(OBJEXT) $(EXTRA_OBJS) \ + -o $(NUTTX) $(HEAD_OBJ) $(EXTRA_OBJS) \ $(LDSTARTGROUP) $(LDLIBS) $(EXTRA_LIBS) $(LDENDGROUP) - $(Q) $(call DELFILE, allsyms.tmp allsyms$(OBJEXT)) endef $(addsuffix .tmp,$(ARCHSCRIPT)): $(ARCHSCRIPT) @@ -170,17 +184,19 @@ $(addsuffix .tmp,$(ARCHSCRIPT)): $(ARCHSCRIPT) nuttx$(EXEEXT): $(HEAD_OBJ) board$(DELIM)libboard$(LIBEXT) $(addsuffix .tmp,$(ARCHSCRIPT)) $(Q) echo "LD: nuttx" -ifneq ($(CONFIG_ALLSYMS),y) +ifeq ($(CONFIG_ALLSYMS)$(CONFIG_MM_KASAN_GLOBAL),) $(Q) $(LD) --entry=__start $(LDFLAGS) $(LIBPATHS) $(EXTRA_LIBPATHS) \ -o $(NUTTX) $(HEAD_OBJ) $(EXTRA_OBJS) \ $(LDSTARTGROUP) $(LDLIBS) $(EXTRA_LIBS) $(LDENDGROUP) else - $(Q) # Link and generate default table - $(Q) $(if $(wildcard $(shell echo $(NUTTX))),,$(call LINK_ALLSYMS,$^)) - $(Q) # Extract all symbols - $(Q) $(call LINK_ALLSYMS, $^) - $(Q) # Extract again since the table offset may changed - $(Q) $(call LINK_ALLSYMS, $^) + $(Q) $(call LINK_ALLSYMS_KASAN) + $(Q) $(call LINK_ALLSYMS_KASAN) + $(Q) $(call LINK_ALLSYMS_KASAN) + $(Q) $(call LINK_ALLSYMS_KASAN) +endif +ifeq ($(CONFIG_MM_KASAN_GLOBAL),y) + $(Q) $(OBJCOPY) -R .kasan.global $(NUTTX) + $(Q) $(OBJCOPY) -R .kasan.unused $(NUTTX) endif ifneq ($(CONFIG_WINDOWS_NATIVE),y) $(Q) $(NM) $(NUTTX) | \ diff --git a/arch/arm64/src/Toolchain.defs b/arch/arm64/src/Toolchain.defs index cbe55788b4e..ca1ddb09d10 100644 --- a/arch/arm64/src/Toolchain.defs +++ b/arch/arm64/src/Toolchain.defs @@ -84,6 +84,10 @@ ifeq ($(CONFIG_MM_KASAN_ALL),y) ARCHOPTIMIZATION += -fsanitize=kernel-address endif +ifeq ($(CONFIG_MM_KASAN_GLOBAL),y) + ARCHOPTIMIZATION += --param asan-globals=1 +endif + # Instrumentation options ifeq ($(CONFIG_ARCH_INSTRUMENT_ALL),y) diff --git a/arch/risc-v/src/Makefile b/arch/risc-v/src/Makefile index bb1272d7755..2333c44cbf7 100644 --- a/arch/risc-v/src/Makefile +++ b/arch/risc-v/src/Makefile @@ -162,13 +162,27 @@ $(KBIN): $(OBJS) board/libboard$(LIBEXT): $(Q) $(MAKE) -C board libboard$(LIBEXT) EXTRAFLAGS="$(EXTRAFLAGS)" -define LINK_ALLSYMS +# When multiple linking, these two additional linking objects will be included + +ifeq ($(CONFIG_MM_KASAN_GLOBAL),y) +EXTRA_LIBS += kasan_globals$(OBJEXT) +endif +ifeq ($(CONFIG_ALLSYMS),y) +EXTRA_LIBS += allsyms$(OBJEXT) +endif + +define LINK_ALLSYMS_KASAN + $(if $(CONFIG_ALLSYMS), $(Q) $(TOPDIR)/tools/mkallsyms.py $(NUTTX) allsyms.tmp --orderbyname $(CONFIG_SYMTAB_ORDEREDBYNAME) $(Q) $(call COMPILE, allsyms.tmp, allsyms$(OBJEXT), -x c) + $(Q) $(call DELFILE, allsyms.tmp)) + $(if $(CONFIG_MM_KASAN_GLOBAL), + $(Q) $(TOPDIR)/tools/kasan_global.py -e $(NUTTX) -o kasan_globals.tmp + $(Q) $(call COMPILE, kasan_globals.tmp, kasan_globals$(OBJEXT) -fno-sanitize=kernel-address, -x c) + $(Q) $(call DELFILE, kasan_globals.tmp)) $(Q) $(LD) --entry=__start $(LDFLAGS) $(LIBPATHS) $(EXTRA_LIBPATHS) \ - -o $(NUTTX) $(HEAD_OBJ) allsyms$(OBJEXT) $(EXTRA_OBJS) \ + -o $(NUTTX) $(HEAD_OBJ) $(EXTRA_OBJS) \ $(LDSTARTGROUP) $(LDLIBS) $(EXTRA_LIBS) $(LDENDGROUP) - $(Q) $(call DELFILE, allsyms.tmp allsyms$(OBJEXT)) endef $(addsuffix .tmp,$(ARCHSCRIPT)): $(ARCHSCRIPT) @@ -176,17 +190,19 @@ $(addsuffix .tmp,$(ARCHSCRIPT)): $(ARCHSCRIPT) nuttx$(EXEEXT): $(HEAD_OBJ) board/libboard$(LIBEXT) $(addsuffix .tmp,$(ARCHSCRIPT)) $(Q) echo "LD: nuttx" -ifneq ($(CONFIG_ALLSYMS),y) +ifeq ($(CONFIG_ALLSYMS)$(CONFIG_MM_KASAN_GLOBAL),) $(Q) $(LD) --entry=__start $(LDFLAGS) $(LIBPATHS) $(EXTRA_LIBPATHS) \ -o $(NUTTX) $(HEAD_OBJ) $(EXTRA_OBJS) \ $(LDSTARTGROUP) $(LDLIBS) $(EXTRA_LIBS) $(LDENDGROUP) else - $(Q) # Link and generate default table - $(Q) $(if $(wildcard $(shell echo $(NUTTX))),,$(call LINK_ALLSYMS,$^)) - $(Q) # Extract all symbols - $(Q) $(call LINK_ALLSYMS, $^) - $(Q) # Extract again since the table offset may changed - $(Q) $(call LINK_ALLSYMS, $^) + $(Q) $(call LINK_ALLSYMS_KASAN) + $(Q) $(call LINK_ALLSYMS_KASAN) + $(Q) $(call LINK_ALLSYMS_KASAN) + $(Q) $(call LINK_ALLSYMS_KASAN) +endif +ifeq ($(CONFIG_MM_KASAN_GLOBAL),y) + $(Q) $(OBJCOPY) -R .kasan.global $(NUTTX) + $(Q) $(OBJCOPY) -R .kasan.unused $(NUTTX) endif ifneq ($(CONFIG_WINDOWS_NATIVE),y) $(Q) $(NM) $(NUTTX) | \ diff --git a/arch/risc-v/src/common/Toolchain.defs b/arch/risc-v/src/common/Toolchain.defs index 44852e69543..66d2a7805b6 100644 --- a/arch/risc-v/src/common/Toolchain.defs +++ b/arch/risc-v/src/common/Toolchain.defs @@ -266,6 +266,10 @@ ifeq ($(CONFIG_MM_KASAN_ALL),y) ARCHOPTIMIZATION += -fsanitize=kernel-address endif +ifeq ($(CONFIG_MM_KASAN_GLOBAL),y) + ARCHOPTIMIZATION += --param asan-globals=1 +endif + ifeq ($(CONFIG_MM_KASAN_DISABLE_READS_CHECK),y) ARCHOPTIMIZATION += --param asan-instrument-reads=0 endif diff --git a/arch/sim/src/Makefile b/arch/sim/src/Makefile index 81dc2b310bd..3001eb7cf17 100644 --- a/arch/sim/src/Makefile +++ b/arch/sim/src/Makefile @@ -351,17 +351,32 @@ board/libboard$(LIBEXT): nuttx-names.dat: nuttx-names.in $(call PREPROCESS, nuttx-names.in, nuttx-names.dat) -define LINK_ALLSYMS - $(if $(CONFIG_HOST_MACOS), \ - $(Q) $(TOPDIR)/tools/mkallsyms.sh noconst $(NUTTX) $(CROSSDEV) > allsyms.tmp, \ - $(Q) $(TOPDIR)/tools/mkallsyms.py $(NUTTX) allsyms.tmp --orderbyname $(CONFIG_SYMTAB_ORDEREDBYNAME)) - $(Q) $(call COMPILE, allsyms.tmp, allsyms$(OBJEXT), -x c) +# When multiple linking, these two additional linking objects will be included + +ifeq ($(CONFIG_MM_KASAN_GLOBAL),y) +EXTRALD_OBJ += kasan_globals$(OBJEXT) +endif +ifeq ($(CONFIG_ALLSYMS),y) +EXTRALD_OBJ += allsyms$(OBJEXT) +endif + +define LINK_ALLSYMS_KASAN + $(if $(CONFIG_ALLSYMS), \ + $(if $(CONFIG_HOST_MACOS), \ + $(Q) $(TOPDIR)/tools/mkallsyms.sh noconst $(NUTTX) $(CROSSDEV) > allsyms.tmp, \ + $(Q) $(TOPDIR)/tools/mkallsyms.py $(NUTTX) allsyms.tmp --orderbyname $(CONFIG_SYMTAB_ORDEREDBYNAME))) + $(if $(CONFIG_ALLSYMS), \ + $(Q) $(call COMPILE, allsyms.tmp, allsyms$(OBJEXT), -x c) + $(Q) $(call DELFILE, allsyms.tmp)) + $(if $(CONFIG_MM_KASAN_GLOBAL), + $(Q) $(TOPDIR)/tools/kasan_global.py -e $(NUTTX) -o kasan_globals.tmp + $(Q) $(call COMPILE, kasan_globals.tmp, kasan_globals$(OBJEXT) -fno-sanitize=kernel-address, -x c) + $(Q) $(call DELFILE, kasan_globals.tmp)) $(if $(CONFIG_HAVE_CXX),\ $(Q) "$(CXX)" $(CFLAGS) $(LDFLAGS) -o $(NUTTX) \ - $(HEADOBJ) nuttx.rel $(HOSTOBJS) $(STDLIBS) allsyms$(OBJEXT),\ + $(HEADOBJ) nuttx.rel $(HOSTOBJS) $(STDLIBS) $(EXTRALD_OBJ),\ $(Q) "$(CC)" $(CFLAGS) $(LDFLAGS) -o $(NUTTX) \ - $(HEADOBJ) nuttx.rel $(HOSTOBJS) $(STDLIBS) allsyms$(OBJEXT)) - $(Q) $(call DELFILE, allsyms.tmp allsyms$(OBJEXT)) + $(HEADOBJ) nuttx.rel $(HOSTOBJS) $(STDLIBS) $(EXTRALD_OBJ)) endef # Note: Use objcopy for Linux because for some reasons visibility=hidden @@ -382,17 +397,20 @@ ifneq ($(CONFIG_HOST_MACOS),y) -e 's/__fini_array_start/_sfini/g' -e 's/__fini_array_end/_efini/g' >nuttx.ld $(Q) echo "__init_array_start = .; __init_array_end = .; __fini_array_start = .; __fini_array_end = .;" >>nuttx.ld endif -ifneq ($(CONFIG_ALLSYMS),y) +ifeq ($(CONFIG_MM_KASAN_GLOBAL),y) + $(Q) sed -i 's/\s*\.interp\s*:\s*{\s*\*(\.interp)\s*}/ \ + .kasan.global : {KEEP(*(.data..LASAN0)) KEEP (*(.data.rel.local..LASAN0)) }\n \ + .interp : {*(.interp)}/g' nuttx.ld +endif +ifeq ($(CONFIG_ALLSYMS)$(CONFIG_MM_KASAN_GLOBAL),) $(if $(CONFIG_HAVE_CXX),\ $(Q) "$(CXX)" $(CFLAGS) $(LDFLAGS) -o $(TOPDIR)/$@ $(HEADOBJ) nuttx.rel $(HOSTOBJS) $(STDLIBS),\ $(Q) "$(CC)" $(CFLAGS) $(LDFLAGS) -o $(TOPDIR)/$@ $(HEADOBJ) nuttx.rel $(HOSTOBJS) $(STDLIBS)) else - $(Q) # Link and generate default table - $(Q) $(if $(wildcard $(shell echo $(NUTTX))),,$(call LINK_ALLSYMS, $@)) - $(Q) # Extract all symbols - $(Q) $(call LINK_ALLSYMS, $^) - $(Q) # Extract again since the table offset may changed - $(Q) $(call LINK_ALLSYMS, $^) + $(Q) $(call LINK_ALLSYMS_KASAN) + $(Q) $(call LINK_ALLSYMS_KASAN) + $(Q) $(call LINK_ALLSYMS_KASAN) + $(Q) $(call LINK_ALLSYMS_KASAN) endif $(Q) $(NM) $(TOPDIR)/$@ | \ grep -v '\(compiled\)\|\(\.o$$\)\|\( [aUw] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | \ diff --git a/boards/arm/qemu/qemu-armv7a/scripts/dramboot.ld b/boards/arm/qemu/qemu-armv7a/scripts/dramboot.ld index 38db4219382..6afca6ca6ce 100644 --- a/boards/arm/qemu/qemu-armv7a/scripts/dramboot.ld +++ b/boards/arm/qemu/qemu-armv7a/scripts/dramboot.ld @@ -26,6 +26,20 @@ PHDRS SECTIONS { . = 0x40101000; + + /* where the global variable out-of-bounds detection information located */ +#ifdef CONFIG_MM_KASAN_GLOBAL + .kasan.unused : + { + *(.data..LASANLOC*) + } > ROM + .kasan.global : + { + KEEP (*(.data..LASAN0)) + KEEP (*(.data.rel.local..LASAN0)) + } > ROM +#endif + .text : { _stext = .; /* Text section */ *(.vectors) diff --git a/boards/arm64/qemu/qemu-armv8a/scripts/dramboot.ld b/boards/arm64/qemu/qemu-armv8a/scripts/dramboot.ld index c4cbc499579..55cfc44b9dd 100644 --- a/boards/arm64/qemu/qemu-armv8a/scripts/dramboot.ld +++ b/boards/arm64/qemu/qemu-armv8a/scripts/dramboot.ld @@ -18,6 +18,8 @@ * ****************************************************************************/ +#include + OUTPUT_ARCH(aarch64) ENTRY(__start) @@ -30,6 +32,20 @@ PHDRS SECTIONS { . = 0x40280000; /* uboot load address */ + + /* where the global variable out-of-bounds detection information located */ +#ifdef CONFIG_MM_KASAN_GLOBAL + .kasan.unused : + { + *(.data..LASANLOC*) + } + .kasan.global : + { + KEEP (*(.data..LASAN0)) + KEEP (*(.data.rel.local..LASAN0)) + } +#endif + _start = .; .text : { _stext = .; /* Text section */ diff --git a/boards/risc-v/qemu-rv/rv-virt/scripts/ld-kernel32.script b/boards/risc-v/qemu-rv/rv-virt/scripts/ld-kernel32.script index 7ce1e080168..dc74d1608ee 100644 --- a/boards/risc-v/qemu-rv/rv-virt/scripts/ld-kernel32.script +++ b/boards/risc-v/qemu-rv/rv-virt/scripts/ld-kernel32.script @@ -44,6 +44,19 @@ SECTIONS { . = 0x80000000; + /* where the global variable out-of-bounds detection information located */ +#ifdef CONFIG_MM_KASAN_GLOBAL + .kasan.unused : + { + *(.data..LASANLOC*) + } + .kasan.global : + { + KEEP (*(.data..LASAN0)) + KEEP (*(.data.rel.local..LASAN0)) + } +#endif + .text : { _stext = . ; diff --git a/boards/risc-v/qemu-rv/rv-virt/scripts/ld-kernel64.script b/boards/risc-v/qemu-rv/rv-virt/scripts/ld-kernel64.script index 514d23efcd8..8763194d667 100644 --- a/boards/risc-v/qemu-rv/rv-virt/scripts/ld-kernel64.script +++ b/boards/risc-v/qemu-rv/rv-virt/scripts/ld-kernel64.script @@ -44,6 +44,19 @@ SECTIONS { . = 0x80000000; + /* where the global variable out-of-bounds detection information located */ +#ifdef CONFIG_MM_KASAN_GLOBAL + .kasan.unused : + { + *(.data..LASANLOC*) + } + .kasan.global : + { + KEEP (*(.data..LASAN0)) + KEEP (*(.data.rel.local..LASAN0)) + } +#endif + .text : { _stext = . ; diff --git a/boards/risc-v/qemu-rv/rv-virt/scripts/ld.script b/boards/risc-v/qemu-rv/rv-virt/scripts/ld.script index e1ab1cfd96e..f2a6326ad80 100644 --- a/boards/risc-v/qemu-rv/rv-virt/scripts/ld.script +++ b/boards/risc-v/qemu-rv/rv-virt/scripts/ld.script @@ -22,6 +22,19 @@ SECTIONS { . = 0x80000000; + /* where the global variable out-of-bounds detection information located */ +#ifdef CONFIG_MM_KASAN_GLOBAL + .kasan.unused : + { + *(.data..LASANLOC*) + } + .kasan.global : + { + KEEP (*(.data..LASAN0)) + KEEP (*(.data.rel.local..LASAN0)) + } +#endif + .text : { _stext = . ; diff --git a/boards/sim/sim/sim/scripts/Make.defs b/boards/sim/sim/sim/scripts/Make.defs index b74ec9a0891..4b7399fedf6 100644 --- a/boards/sim/sim/sim/scripts/Make.defs +++ b/boards/sim/sim/sim/scripts/Make.defs @@ -83,6 +83,10 @@ else ifeq ($(CONFIG_MM_KASAN_ALL),y) ARCHOPTIMIZATION += -fsanitize=kernel-address endif +ifeq ($(CONFIG_MM_KASAN_GLOBAL),y) + ARCHOPTIMIZATION += --param asan-globals=1 +endif + ifeq ($(CONFIG_SIM_UBSAN),y) ARCHOPTIMIZATION += -fsanitize=undefined else diff --git a/mm/Kconfig b/mm/Kconfig index 37e5a4f2551..813135f914c 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -281,6 +281,21 @@ config MM_KASAN_DISABLE_WRITES_CHECK ---help--- This option disable kasan writes check. +config MM_KASAN_GLOBAL + bool "Enable global data check" + depends on MM_KASAN + default n + ---help--- + This option enables KASan global data check. + It's used to extract segments in the linker script. + Two new segments need to be created, one being + ".kasan.unused: { *(.data..LASANLOC*) }", + used to eliminate excess data generated. + One is ".kasan.global:{ + KEEP ( *(. data.. LASAN0)) + KEEP ( *(. data. rel. local.. LASAN0)) + }", used to extract data generated by the compiler + config MM_UBSAN bool "Undefined Behavior Sanitizer" default n diff --git a/mm/kasan/kasan.c b/mm/kasan/kasan.c index fb91d6fc87d..b8a4843abd7 100644 --- a/mm/kasan/kasan.c +++ b/mm/kasan/kasan.c @@ -50,6 +50,16 @@ #define KASAN_REGION_SIZE(size) \ (sizeof(struct kasan_region_s) + KASAN_SHADOW_SIZE(size)) +#ifdef CONFIG_MM_KASAN_GLOBAL + +# define KASAN_GLOBAL_SHADOW_SCALE (32) + +# define KASAN_GLOBAL_NEXT_REGION(region) \ + (FAR struct kasan_region_s *) \ + ((FAR char *)region->shadow + (size_t)region->next) + +#endif + #define KASAN_INIT_VALUE 0xDEADCAFE /**************************************************************************** @@ -59,9 +69,9 @@ struct kasan_region_s { FAR struct kasan_region_s *next; - uintptr_t begin; - uintptr_t end; - uintptr_t shadow[1]; + uintptr_t begin; + uintptr_t end; + uintptr_t shadow[1]; }; /**************************************************************************** @@ -72,6 +82,14 @@ static spinlock_t g_lock; static FAR struct kasan_region_s *g_region; static uint32_t g_region_init; +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#ifdef CONFIG_MM_KASAN_GLOBAL +extern const unsigned char g_globals_region[]; +#endif + /**************************************************************************** * Private Functions ****************************************************************************/ @@ -99,6 +117,22 @@ static FAR uintptr_t *kasan_mem_to_shadow(FAR const void *ptr, size_t size, } } +#ifdef CONFIG_MM_KASAN_GLOBAL + for (region = (FAR struct kasan_region_s *)g_globals_region; + region->next; + region = KASAN_GLOBAL_NEXT_REGION(region)) + { + if (addr >= region->begin && addr < region->end) + { + DEBUGASSERT(addr + size <= region->end); + addr -= region->begin; + addr /= KASAN_GLOBAL_SHADOW_SCALE; + *bit = addr % KASAN_BITS_PER_WORD; + return ®ion->shadow[addr / KASAN_BITS_PER_WORD]; + } + } +#endif + return NULL; } @@ -314,3 +348,15 @@ DEFINE_ASAN_LOAD_STORE(2) DEFINE_ASAN_LOAD_STORE(4) DEFINE_ASAN_LOAD_STORE(8) DEFINE_ASAN_LOAD_STORE(16) + +#ifdef CONFIG_MM_KASAN_GLOBAL +void __asan_register_globals(void *ptr, ssize_t size) +{ + /* Shut up compiler complaints */ +} + +void __asan_unregister_globals(void *ptr, ssize_t size) +{ + /* Shut up compiler complaints */ +} +#endif diff --git a/tools/kasan_global.py b/tools/kasan_global.py new file mode 100755 index 00000000000..3967079dffc --- /dev/null +++ b/tools/kasan_global.py @@ -0,0 +1,306 @@ +#!/usr/bin/env python3 +############################################################################ +# tools/kasan_global.py +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +import argparse +import os + +from construct import Int32ul, Int64ul, Struct +from elftools.elf.elffile import ELFFile + +debug = False + +# N-byte aligned shadow area 1 bit +KASAN_GLOBAL_ALIGN = 32 + +# The maximum gap that two data segments can tolerate +KASAN_MAX_DATA_GAP = 1 << 16 + +# The section where the global variable descriptor +# generated by the compiler is located +KASAN_SECTION = ".kasan.global" + +# The structure of parsing strings required for 32-bit and 64 bit +KASAN_GLOBAL_STRUCT_32 = Struct( + "beg" / Int32ul, + "size" / Int32ul, + "size_with_redzone" / Int32ul, + "name" / Int32ul, + "module_name" / Int32ul, + "has_dynamic_init" / Int32ul, + "location" / Int32ul, + "odr_indicator" / Int32ul, +) + +KASAN_GLOBAL_STRUCT_64 = Struct( + "beg" / Int64ul, + "size" / Int64ul, + "size_with_redzone" / Int64ul, + "name" / Int64ul, + "module_name" / Int64ul, + "has_dynamic_init" / Int64ul, + "location" / Int64ul, + "odr_indicator" / Int64ul, +) + + +# Global configuration information +class Config: + def __init__(self, outpath, elf, ldscript): + self.outpath = outpath + self.elf = elf + self.ldscript = ldscript + + if self.elf is None or os.path.exists(self.elf) is False: + self.elf = None + return + + with open(self.elf, "rb") as file: + elf_file = ELFFile(file) + elf_header = elf_file.header + + # Obtain bit width + bitness = elf_header["e_ident"]["EI_CLASS"] + if bitness == "ELFCLASS32": + self.bitwides = 32 + elif bitness == "ELFCLASS64": + self.bitwides = 64 + + # Big and little end + endianness = elf_header["e_ident"]["EI_DATA"] + if endianness == "ELFDATA2LSB": + self.endian = "little" + elif endianness == "ELFDATA2MSB": + self.endian = "big" + + +class KASanRegion: + def __init__(self, start, end) -> None: + self.start = start + self.end = end + self.size = int((end - start) // KASAN_GLOBAL_ALIGN // 8) + 1 + self.shadow = bytearray(b"\x00" * self.size) + + def mark_bit(self, index, nbits): + self.shadow[index] |= 1 << nbits + + def poison(self, dict): + dict_size = dict["size"] + if dict_size % 32: + dict_size = int((dict_size + 31) // 32) * 32 + + distance = (dict["beg"] + dict_size - self.start) // KASAN_GLOBAL_ALIGN + index = int(distance // 8) + nbits = distance % 8 + if debug: + print( + "regin: %08x addr: %08x size: %d bits: %d || poison index: %d nbits: %d" + % ( + self.start, + dict["beg"], + dict["size"], + int(dict["size_with_redzone"] // KASAN_GLOBAL_ALIGN), + index, + nbits, + ) + ) + + # Using 32bytes: with 1bit alignment, + # only one bit of inaccessible area exists for each pair of global variables. + self.mark_bit(index, nbits) + + +class KASanInfo: + def __init__(self) -> None: + # Record the starting position of the merged data block + self.data_sections = [] + # Record the kasan region corresponding to each data block + self.regions: list[KASanRegion] = [] + + def merge_ranges(self, dict): + if len(self.data_sections) == 0: + self.data_sections.append( + [dict["beg"], dict["beg"] + dict["size_with_redzone"]] + ) + return + start = dict["beg"] + end = dict["beg"] + dict["size_with_redzone"] + if start - self.data_sections[-1][1] <= KASAN_MAX_DATA_GAP: + self.data_sections[-1][1] = end + else: + self.data_sections.append([start, end]) + + def create_region(self): + for i in self.data_sections: + start = i[0] + end = i[1] + if debug: + print("KAsan Shadow Block: %08x ---- %08x" % (start, end)) + self.regions.append(KASanRegion(start, end)) + + def mark_shadow(self, dict): + for i in self.regions: + start = i.start + end = i.end + if start <= dict["beg"] and dict["beg"] <= end: + i.poison(dict) + break + + +# Global variable descriptor +def get_global_dict(GLOBAL_STRUCT: Struct, bytes: bytes): + dict = GLOBAL_STRUCT.parse(bytes) + return { + "beg": dict.beg, + "size": dict.size, + "size_with_redzone": dict.size_with_redzone, + } + + +def get_elf_section(elf, section) -> bytes: + with open(elf, "rb") as file: + elf = ELFFile(file) + for i in elf.iter_sections(): + if i.name == section: + return i.data() + return None + + +def long_to_bytestring(bitwides, endian, value: int) -> str: + res = "" + byte_array = value.to_bytes(length=int(bitwides / 8), byteorder=endian) + for i in byte_array: + res += "0x%02x, " % (i) + return res + + +def create_kasan_file(config: Config, region_list=[]): + region: KASanRegion = None + with open(config.outpath, "w") as file: + file.write("const unsigned char\ng_globals_region[] = {\n") + for i in range(len(region_list)): + region = region_list[i] + + # Fill the array of regions + # The filling order is as follows, from mm/kasan/generic.c + # The data set to 0 is assigned by the program body + # 1. FAR struct kasan_region_s *next; + # This type will be used to record the size of the shadow area + # to facilitate the program to traverse the array. + # 2. uintptr_t begin; + # 3. uintptr_t end; + # 4. uintptr_t shadow[1]; + + file.write( + "%s\n" + % (long_to_bytestring(config.bitwides, config.endian, region.size)) + ) + file.write( + "%s\n" + % (long_to_bytestring(config.bitwides, config.endian, region.start)) + ) + file.write( + "%s\n" + % (long_to_bytestring(config.bitwides, config.endian, region.end)) + ) + + for j in range(len(region.shadow)): + if j % 8 == 0: + file.write("\n") + file.write("0x%02x, " % (region.shadow[j])) + + file.write("\n") + file.write("0x00, 0x00, 0x00, 0x00,0x00, 0x00,0x00, 0x00\n") + file.write("\n};") + + +# Error extraction section processing to enable the program to compile successfully +def handle_error(config: Config, string=None): + if string: + print(string) + create_kasan_file(config) + exit(0) + + +def parse_args() -> Config: + global debug + parser = argparse.ArgumentParser(description="Build kasan global variable region") + parser.add_argument("-o", "--outpath", help="outpath") + parser.add_argument("-d", "--ldscript", help="ld script path(Only support sim)") + parser.add_argument("-e", "--elffile", help="elffile") + parser.add_argument( + "--debug", + action="store_true", + default=False, + help="if enabled, it will show more logs.", + ) + + args = parser.parse_args() + debug = args.debug + return Config(args.outpath, args.elffile, args.ldscript) + + +def main(): + config = parse_args() + if config.elf is None: + handle_error(config) + + # Identify the segment information that needs to be extracted + section = get_elf_section(config.elf, KASAN_SECTION) + if section is None: + handle_error( + config, + "Please update the link script, section ['%s'] cannot be found" + % (KASAN_SECTION), + ) + + # List of global variable descriptors + dict_list = [] + + # Extract all global variable descriptors within the + if config.bitwides == 32: + global_struct = KASAN_GLOBAL_STRUCT_32 + elif config.bitwides == 64: + global_struct = KASAN_GLOBAL_STRUCT_64 + + step = global_struct.sizeof() + for i in range(0, len(section), step): + dict = get_global_dict(global_struct, section[i : i + step]) + dict_list.append(dict) + + dict_list = sorted(dict_list, key=lambda item: item["beg"]) + + # Merge all global variables to obtain several segments of the distribution range + kasan = KASanInfo() + for i in dict_list: + kasan.merge_ranges(i) + + # Create empty shadow zone + kasan.create_region() + + # Mark on the shadow area + for i in dict_list: + kasan.mark_shadow(i) + + create_kasan_file(config, kasan.regions) + + +if __name__ == "__main__": + main()