Support AddressSanitizer and MemorySanitizer (clang)
authorIvan Maidanski <ivmai@mail.ru>
Tue, 15 Nov 2016 14:35:40 +0000 (17:35 +0300)
committerIvan Maidanski <ivmai@mail.ru>
Tue, 15 Nov 2016 21:20:27 +0000 (00:20 +0300)
* 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
include/private/gcconfig.h
mach_dep.c
mark.c
os_dep.c
tests/test.c
tests/test_cpp.cc

index f1a5185..e5c870d 100644 (file)
@@ -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__))
index d45abda..b1f370f 100644 (file)
 # 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. */
index 61b5ed2..850a990 100644 (file)
 /* 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 (file)
--- 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));
index e8601b2..a0ded5f 100644 (file)
--- 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 */
 
index 59bb665..5515511 100644 (file)
@@ -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);
index 024efae..16b2d73 100644 (file)
@@ -312,7 +312,14 @@ void* Undisguise( GC_word i ) {
             A* a = static_cast<A*>(Undisguise(as[i]));
             B* b = static_cast<B*>(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;