From ce75cf115b8739632fd54bf4a64745749ab13299 Mon Sep 17 00:00:00 2001 From: Ivan Maidanski Date: Tue, 15 Nov 2016 17:35:40 +0300 Subject: [PATCH] Support AddressSanitizer and MemorySanitizer (clang) * include/private/gc_priv.h (GC_ATTR_NO_SANITIZE_ADDR, GC_ATTR_NO_SANITIZE_MEMORY): New macro. * include/private/gcconfig.h [__has_feature(address_sanitizer)] (ADDRESS_SANITIZER): New macro. * include/private/gcconfig.h [__has_feature(memory_sanitizer)] (MEMORY_SANITIZER): Likewise. * mach_dep.c (GC_with_callee_saves_pushed): Use GC_ATTR_NO_SANITIZE_ADDR attribute. * mark.c (GC_mark_from, GC_push_all_eager): Likewise. * mark.c (GC_mark_from, GC_push_all_eager): Use GC_ATTR_NO_SANITIZE_MEMORY attribute. * os_dep.c [ADDRESS_SANITIZER && (UNIX_LIKE || NEED_FIND_LIMIT || MPROTECT_VDB)] (__asan_default_options): New function. * os_dep.c [(NEED_FIND_LIMIT || UNIX_LIKE) && CPPCHECK && ADDRESS_SANITIZER] (GC_set_and_save_fault_handler): Reference __asan_default_options. * os_dep.c [MPROTECT_VDB && !DARWIN && CPPCHECK && ADDRESS_SANITIZER] (GC_dirty_init): Likewise. * tests/test.c [MEMORY_SANITIZER] (check_heap_stats): Increase max_heap_sz by 25% (to avoid "Unexpected heap growth" error if MSan is used). * tests/test_cpp.cc [ADDRESS_SANITIZER || MEMORY_SANITIZER] (main): Call GC_FREE(a) instead of delete a; add comment. --- include/private/gc_priv.h | 16 ++++++++++++++++ include/private/gcconfig.h | 10 ++++++++++ mach_dep.c | 1 + mark.c | 2 ++ os_dep.c | 15 +++++++++++++++ tests/test.c | 3 +++ tests/test_cpp.cc | 9 ++++++++- 7 files changed, 55 insertions(+), 1 deletion(-) diff --git a/include/private/gc_priv.h b/include/private/gc_priv.h index f1a5185..e5c870d 100644 --- a/include/private/gc_priv.h +++ b/include/private/gc_priv.h @@ -148,6 +148,22 @@ typedef char * ptr_t; /* A generic pointer to which we can add */ # include "gc_hdrs.h" #endif +#ifndef GC_ATTR_NO_SANITIZE_ADDR +# ifdef ADDRESS_SANITIZER +# define GC_ATTR_NO_SANITIZE_ADDR __attribute__((no_sanitize("address"))) +# else +# define GC_ATTR_NO_SANITIZE_ADDR /* empty */ +# endif +#endif /* !GC_ATTR_NO_SANITIZE_ADDR */ + +#ifndef GC_ATTR_NO_SANITIZE_MEMORY +# ifdef MEMORY_SANITIZER +# define GC_ATTR_NO_SANITIZE_MEMORY __attribute__((no_sanitize("memory"))) +# else +# define GC_ATTR_NO_SANITIZE_MEMORY /* empty */ +# endif +#endif /* !GC_ATTR_NO_SANITIZE_MEMORY */ + #ifndef GC_ATTR_UNUSED # if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) # define GC_ATTR_UNUSED __attribute__((__unused__)) diff --git a/include/private/gcconfig.h b/include/private/gcconfig.h index d45abda..b1f370f 100644 --- a/include/private/gcconfig.h +++ b/include/private/gcconfig.h @@ -3070,6 +3070,16 @@ # endif #endif /* !GC_WORD_C */ +#if defined(__has_feature) + /* __has_feature() is supported. */ +# if __has_feature(address_sanitizer) && !defined(ADDRESS_SANITIZER) +# define ADDRESS_SANITIZER +# endif +# if __has_feature(memory_sanitizer) && !defined(MEMORY_SANITIZER) +# define MEMORY_SANITIZER +# endif +#endif + #if defined(SPARC) # define ASM_CLEAR_CODE /* Stack clearing is crucial, and we */ /* include assembly code to do it well. */ diff --git a/mach_dep.c b/mach_dep.c index 61b5ed2..850a990 100644 --- a/mach_dep.c +++ b/mach_dep.c @@ -223,6 +223,7 @@ /* ctxt is either a pointer to a ucontext_t we generated, or NULL. */ GC_INNER void GC_with_callee_saves_pushed(void (*fn)(ptr_t, void *), volatile ptr_t arg) + GC_ATTR_NO_SANITIZE_ADDR { volatile int dummy; void * context = 0; diff --git a/mark.c b/mark.c index 06a0b4b..06ed99c 100644 --- a/mark.c +++ b/mark.c @@ -598,6 +598,7 @@ GC_INNER mse * GC_signal_mark_stack_overflow(mse *msp) */ GC_INNER mse * GC_mark_from(mse *mark_stack_top, mse *mark_stack, mse *mark_stack_limit) + GC_ATTR_NO_SANITIZE_ADDR GC_ATTR_NO_SANITIZE_MEMORY { signed_word credit = HBLKSIZE; /* Remaining credit for marking work */ ptr_t current_p; /* Pointer to current candidate ptr. */ @@ -1527,6 +1528,7 @@ GC_API void GC_CALL GC_print_trace(word gc_no) * change. */ GC_API void GC_CALL GC_push_all_eager(char *bottom, char *top) + GC_ATTR_NO_SANITIZE_ADDR GC_ATTR_NO_SANITIZE_MEMORY { word * b = (word *)(((word) bottom + ALIGNMENT-1) & ~(ALIGNMENT-1)); word * t = (word *)(((word) top) & ~(ALIGNMENT-1)); diff --git a/os_dep.c b/os_dep.c index e8601b2..a0ded5f 100644 --- a/os_dep.c +++ b/os_dep.c @@ -505,6 +505,15 @@ GC_INNER char * GC_get_maps(void) } #endif /* NETBSD */ +#if defined(ADDRESS_SANITIZER) && (defined(UNIX_LIKE) \ + || defined(NEED_FIND_LIMIT) || defined(MPROTECT_VDB)) + /* To tell ASan to allow GC to use its own SIGBUS/SEGV handlers. */ + const char *__asan_default_options(void) + { + return "allow_user_segv_handler=1"; + } +#endif + #ifdef OPENBSD static struct sigaction old_segv_act; STATIC sigjmp_buf GC_jmp_buf_openbsd; @@ -894,6 +903,9 @@ GC_INNER size_t GC_page_size = 0; old_bus_handler = signal(SIGBUS, h); # endif # endif +# if defined(CPPCHECK) && defined(ADDRESS_SANITIZER) + GC_noop1((word)&__asan_default_options); +# endif } # endif /* NEED_FIND_LIMIT || UNIX_LIKE */ @@ -3410,6 +3422,9 @@ GC_INNER void GC_remove_protection(struct hblk *h, word nblocks, /* MPROTECT_VDB is unsupported for WinCE at present. */ /* FIXME: implement it (if possible). */ # endif +# if defined(CPPCHECK) && defined(ADDRESS_SANITIZER) + GC_noop1((word)&__asan_default_options); +# endif } #endif /* !DARWIN */ diff --git a/tests/test.c b/tests/test.c index 59bb665..5515511 100644 --- a/tests/test.c +++ b/tests/test.c @@ -1523,6 +1523,9 @@ void check_heap_stats(void) # endif # endif # endif +# ifdef MEMORY_SANITIZER + max_heap_sz += max_heap_sz / 4; +# endif max_heap_sz *= n_tests; # if defined(USE_MMAP) || defined(MSWIN32) max_heap_sz = NUMBER_ROUND_UP(max_heap_sz, 4 * 1024 * 1024); diff --git a/tests/test_cpp.cc b/tests/test_cpp.cc index 024efae..16b2d73 100644 --- a/tests/test_cpp.cc +++ b/tests/test_cpp.cc @@ -312,7 +312,14 @@ void* Undisguise( GC_word i ) { A* a = static_cast(Undisguise(as[i])); B* b = static_cast(Undisguise(bs[i])); a->Test( i ); - delete a; +# if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) + // Workaround for ASan/MSan: the linker uses operator delete + // implementation from libclang_rt instead of gc_cpp (thus + // causing incompatible alloc/free). + GC_FREE(a); +# else + delete a; +# endif b->Test( i ); B::Deleting( 1 ); delete b; -- 2.7.4