Deliver LSan fixes for Tizen.
authorMaxim Ostapenko <m.ostapenko@samsung.com>
Mon, 19 Sep 2016 14:38:07 +0000 (17:38 +0300)
committerIvan Baravy <i.baravy@samsung.com>
Tue, 21 Feb 2017 17:39:33 +0000 (20:39 +0300)
    * Read LSAN_OPTIONS from /LSAN_OPTIONS file.
    * Add MaybeMountProcFS stuff for LSan. This would allow us to preload
    liblsan.so even for systemd when /proc/ isn't mounted yet.
    * Add ARM and x86 ports for LSan.
    * Add PointsIntoChunk method to protect against segfault when pointer
    points to metadata.
    * Make cf.exitcode = 0 by default.

commit a6549ffda72b4f84bbd2c763640181eaeccf6582

libsanitizer/configure.tgt
libsanitizer/lsan/lsan.cc
libsanitizer/lsan/lsan.h
libsanitizer/lsan/lsan_allocator.cc
libsanitizer/lsan/lsan_common.cc
libsanitizer/lsan/lsan_common.h
libsanitizer/lsan/lsan_interceptors.cc
libsanitizer/sanitizer_common/sanitizer_allocator.h
libsanitizer/sanitizer_common/sanitizer_linux.cc
libsanitizer/sanitizer_common/sanitizer_linux.h
libsanitizer/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc

index 05ead76..0b036a3 100644 (file)
@@ -24,15 +24,17 @@ case "${target}" in
   x86_64-*-linux* | i?86-*-linux*)
        if test x$ac_cv_sizeof_void_p = x8; then
                TSAN_SUPPORTED=yes
-               LSAN_SUPPORTED=yes
                TSAN_TARGET_DEPENDENT_OBJECTS=tsan_rtl_amd64.lo
        fi
+       LSAN_SUPPORTED=yes
        ;;
-  powerpc*-*-linux*)
+  powerpc*le-*-linux*)
+       UNSUPPORTED=1
        ;;
   sparc*-*-linux*)
        ;;
   arm*-*-linux*)
+       LSAN_SUPPORTED=yes
        ;;
   aarch64*-*-linux*)
        if test x$ac_cv_sizeof_void_p = x8; then
index 6e7429c..49b4432 100644 (file)
@@ -29,6 +29,20 @@ bool WordIsPoisoned(uptr addr) {
   return false;
 }
 
+#if LSAN_DYNAMIC
+// Initialize runtime in case it's LD_PRELOAD-ed into unsanitized executable
+// (and thus normal initializers from .preinit_array or modules haven't run).
+
+class LsanInitializer {
+  public:  // NOLINT
+    LsanInitializer() {
+      __lsan_init();
+   }
+};
+
+static LsanInitializer lsan_initializer;
+#endif  // LSAN_DYNAMIC
+
 }  // namespace __lsan
 
 using namespace __lsan;  // NOLINT
@@ -42,7 +56,7 @@ static void InitializeFlags() {
     cf.external_symbolizer_path = GetEnv("LSAN_SYMBOLIZER_PATH");
     cf.malloc_context_size = 30;
     cf.detect_leaks = true;
-    cf.exitcode = 23;
+    cf.exitcode = 0;
     OverrideCommonFlags(cf);
   }
 
@@ -64,8 +78,9 @@ static void InitializeFlags() {
 
 extern "C" void __lsan_init() {
   CHECK(!lsan_init_is_running);
-  if (lsan_inited)
+  if (LIKELY(lsan_inited))
     return;
+  MaybeMountProcFS();
   lsan_init_is_running = true;
   SanitizerToolName = "LeakSanitizer";
   CacheBinaryName();
index ee2fc02..30f06ca 100644 (file)
   GET_STACK_TRACE(__sanitizer::common_flags()->malloc_context_size, \
                   common_flags()->fast_unwind_on_malloc)
 
+#ifndef LSAN_DYNAMIC
+# ifdef PIC
+#  define LSAN_DYNAMIC 1
+# else
+#  define LSAN_DYNAMIC 0
+# endif
+#endif
+
 namespace __lsan {
 
 void InitializeInterceptors();
index 1908c8e..c3fd6ae 100644 (file)
@@ -26,10 +26,16 @@ namespace __lsan {
 struct ChunkMetadata {
   u8 allocated : 8;  // Must be first.
   ChunkTag tag : 2;
+#if SANITIZER_WORDSIZE == 64
   uptr requested_size : 54;
+#else
+  uptr requested_size : 32;
+  uptr padding : 22;
+#endif
   u32 stack_trace_id;
 };
 
+#if SANITIZER_WORDSIZE == 64
 #if defined(__mips64) || defined(__aarch64__)
 static const uptr kMaxAllowedMallocSize = 4UL << 30;
 static const uptr kRegionSizeLog = 20;
@@ -48,6 +54,17 @@ static const uptr kAllocatorSize = 0x40000000000ULL; // 4T.
 typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize,
         sizeof(ChunkMetadata), DefaultSizeClassMap> PrimaryAllocator;
 #endif
+#else // SANITIZER_WORDSIZE == 32
+typedef CompactSizeClassMap SizeClassMap;
+static const uptr kRegionSizeLog = 20;
+static const uptr kNumRegions = SANITIZER_MMAP_RANGE_SIZE >> kRegionSizeLog;
+typedef FlatByteMap<kNumRegions> ByteMap;
+typedef SizeClassAllocator32<0, SANITIZER_MMAP_RANGE_SIZE,
+     sizeof(ChunkMetadata), SizeClassMap, kRegionSizeLog, ByteMap>
+     PrimaryAllocator;
+static const uptr kMaxAllowedMallocSize = 1UL << 30;
+#endif
+
 typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache;
 typedef LargeMmapAllocator<> SecondaryAllocator;
 typedef CombinedAllocator<PrimaryAllocator, AllocatorCache,
@@ -156,7 +173,9 @@ uptr PointsIntoChunk(void* p) {
   if (addr < chunk) return 0;
   ChunkMetadata *m = Metadata(reinterpret_cast<void *>(chunk));
   CHECK(m);
-  if (!m->allocated)
+  if (!allocator.PointsIntoChunk(p))
+    return 0;
+  if (m->allocated != 1)
     return 0;
   if (addr < chunk + m->requested_size)
     return chunk;
index 6d674c5..1bc0a78 100644 (file)
@@ -221,6 +221,8 @@ static void ProcessThreads(SuspendedThreadsList const &suspended_threads,
       ForEachExtraStackRange(os_id, ForEachExtraStackRangeCb, frontier);
     }
 
+// TODO: support static TLS for ARM and x86.
+#if !defined(__arm__) && !defined(__i386__)
     if (flags()->use_tls) {
       LOG_THREADS("TLS at %p-%p.\n", tls_begin, tls_end);
       if (cache_begin == cache_end) {
@@ -237,6 +239,7 @@ static void ProcessThreads(SuspendedThreadsList const &suspended_threads,
           ScanRangeForPointers(cache_end, tls_end, frontier, "TLS", kReachable);
       }
     }
+#endif
   }
 }
 
@@ -284,6 +287,10 @@ static void FloodFillTag(Frontier *frontier, ChunkTag tag) {
 // ForEachChunk callback. If the chunk is marked as leaked, marks all chunks
 // which are reachable from it as indirectly leaked.
 static void MarkIndirectlyLeakedCb(uptr chunk, void *arg) {
+// On 32-bit archs it's hard to distinguish between direct and indirect leaks.
+#if SANITIZER_WORDSIZE == 32
+  return;
+#endif
   chunk = GetUserBegin(chunk);
   LsanMetadata m(chunk);
   if (m.allocated() && m.tag() != kReachable) {
index b415567..3c434a9 100644 (file)
@@ -23,6 +23,8 @@
 #if (SANITIZER_LINUX && !SANITIZER_ANDROID) && (SANITIZER_WORDSIZE == 64) \
      && (defined(__x86_64__) ||  defined(__mips64) ||  defined(__aarch64__))
 #define CAN_SANITIZE_LEAKS 1
+#elif defined(__arm__) || defined(__i386__)
+#define CAN_SANITIZE_LEAKS 1
 #else
 #define CAN_SANITIZE_LEAKS 0
 #endif
index 57581e8..8edd0e0 100644 (file)
@@ -21,6 +21,7 @@
 #include "lsan.h"
 #include "lsan_allocator.h"
 #include "lsan_thread.h"
+#include <stddef.h>
 
 using namespace __lsan;
 
@@ -149,13 +150,13 @@ INTERCEPTOR(void, cfree, void *p) ALIAS(WRAPPER_NAME(free));
   return Allocate(stack, size, 1, kAlwaysClearMemory);
 
 INTERCEPTOR_ATTRIBUTE
-void *operator new(uptr size) { OPERATOR_NEW_BODY; }
+void *operator new(size_t size) { OPERATOR_NEW_BODY; }
 INTERCEPTOR_ATTRIBUTE
-void *operator new[](uptr size) { OPERATOR_NEW_BODY; }
+void *operator new[](size_t size) { OPERATOR_NEW_BODY; }
 INTERCEPTOR_ATTRIBUTE
-void *operator new(uptr size, std::nothrow_t const&) { OPERATOR_NEW_BODY; }
+void *operator new(size_t size, std::nothrow_t const&) { OPERATOR_NEW_BODY; }
 INTERCEPTOR_ATTRIBUTE
-void *operator new[](uptr size, std::nothrow_t const&) { OPERATOR_NEW_BODY; }
+void *operator new[](size_t size, std::nothrow_t const&) { OPERATOR_NEW_BODY; }
 
 #define OPERATOR_DELETE_BODY \
   ENSURE_LSAN_INITED;        \
index d98528c..0490a3c 100644 (file)
@@ -396,6 +396,17 @@ class SizeClassAllocator64 {
                                    (1 + chunk_idx) * kMetadataSize);
   }
 
+  bool PointsIntoChunk(const void *p) {
+    CHECK(PointerIsMine(p));
+    uptr mem = reinterpret_cast<uptr>(p);
+    uptr class_id = GetSizeClass(p);
+    uptr size = SizeClassMap::Size(class_id);
+    uptr beg = kSpaceBeg + kRegionSize * class_id;
+    uptr n_chunks = kRegionSize / (size + kMetadataSize);
+    uptr gap_start = beg + n_chunks * size + 1;
+    return (beg <= mem) && (mem < gap_start);
+  }
+
   uptr TotalMemoryUsed() {
     uptr res = 0;
     for (uptr i = 0; i < kNumClasses; i++)
@@ -725,6 +736,16 @@ class SizeClassAllocator32 {
     return reinterpret_cast<void*>(meta);
   }
 
+  bool PointsIntoChunk(const void *p) {
+    CHECK(PointerIsMine(p));
+    uptr mem = reinterpret_cast<uptr>(p);
+    uptr beg = ComputeRegionBeg(mem);
+    uptr size = SizeClassMap::Size(GetSizeClass(p));
+    uptr n_chunks = kRegionSize / (size + kMetadataSize);
+    uptr gap_start = beg + n_chunks * size + 1;
+    return (beg <= mem) && (mem < gap_start);
+  }
+
   NOINLINE Batch* AllocateBatch(AllocatorStats *stat, AllocatorCache *c,
                                 uptr class_id) {
     CHECK_LT(class_id, kNumClasses);
@@ -1430,6 +1451,12 @@ class CombinedAllocator {
     primary_.ForceUnlock();
   }
 
+  bool PointsIntoChunk(const void *p) {
+    if (primary_.PointerIsMine(p))
+      return primary_.PointsIntoChunk(p);
+    return true;
+  }
+
   // Iterate over all existing chunks.
   // The allocator must be locked when calling this function.
   void ForEachChunk(ForEachChunkCallback callback, void *arg) {
index 9b9ad0a..5e27871 100644 (file)
@@ -987,6 +987,111 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
                        : "x30", "memory");
   return res;
 }
+#elif defined(__arm__) && SANITIZER_LINUX
+uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
+                    int *parent_tidptr, void *newtls, int *child_tidptr) {
+  unsigned int res;
+  if (!fn || !child_stack)
+    return -EINVAL;
+  CHECK_EQ(0, (uptr)child_stack % 16);
+  child_stack = (char *)child_stack - 2 * sizeof(unsigned int);
+  ((unsigned int *)child_stack)[0] = (uptr)fn;
+  ((unsigned int *)child_stack)[1] = (uptr)arg;
+  register int r0 __asm__("r0") = flags;
+  register void *r1 __asm__("r1") = child_stack;
+  register int *r2 __asm__("r2") = parent_tidptr;
+  register void *r3 __asm__("r3") = newtls;
+  register int *r4 __asm__("r4") = child_tidptr;
+  register int r7 __asm__("r7") = __NR_clone;
+  __asm__ __volatile__("push    {r4, r7}\n"
+                       "swi 0x0\n"
+                       "cmp r0, #0\n"
+                       "bne 1f\n"
+                       "ldr r0, [sp, #4]\n"
+#if defined(__ARM_ARCH_4T__) && defined(__THUMB_INTERWORK__)
+                       "ldr     ip, [sp], #8\n"
+                       "mov lr, pc\n"
+                       "bx      ip\n"
+#else
+                       "ldr     lr, [sp], #8\n"
+                       "blx lr\n"
+#endif
+                       "mov r7, %7\n"
+                       "swi 0x0\n"
+                       "1:\n"
+                       "pop {r4, r7}\n"
+                       "mov %0, r0\n"
+                       : "=r"(res)
+                       : "r"(r0), "r"(r1), "r"(r2), "r"(r3), "r"(r4), "r"(r7),
+                         "i"(__NR_exit)
+                       : "memory");
+  return res;
+}
+#elif defined(__i386__) && SANITIZER_LINUX
+uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
+                    int *parent_tidptr, void *newtls, int *child_tidptr) {
+  int res;
+  if (!fn || !child_stack)
+    return -EINVAL;
+  CHECK_EQ(0, (uptr)child_stack % 16);
+  child_stack = (char *)child_stack - 7 * sizeof(unsigned int);
+  ((unsigned int *)child_stack)[0] = (uptr)flags;
+  ((unsigned int *)child_stack)[1] = (uptr)0;
+  ((unsigned int *)child_stack)[2] = (uptr)fn;
+  ((unsigned int *)child_stack)[3] = (uptr)arg;
+  __asm__ __volatile__(
+                       /* %eax = syscall(%eax = SYSCALL(clone),
+                        *                %ebx = flags,
+                        *                %ecx = child_stack,
+                        *                %edx = parent_tidptr,
+                        *                %esi  = new_tls,
+                        *                %edi = child_tidptr)
+                        */
+
+                        /* Obtain flags */
+                        "movl    (%%ecx), %%ebx\n"
+                        /* Do the system call */
+                        "pushl   %%ebx\n"
+                        "pushl   %%esi\n"
+                        "pushl   %%edi\n"
+                        /* Remember the flag value.  */
+                        "movl    %%ebx, (%%ecx)\n"
+                        "int     $0x80\n"
+                        "popl    %%edi\n"
+                        "popl    %%esi\n"
+                        "popl    %%ebx\n"
+
+                        /* if (%eax != 0)
+                         *   return;
+                         */
+
+                        "test    %%eax,%%eax\n"
+                        "jnz    1f\n"
+
+                        /* terminate the stack frame */
+                        "xorl   %%ebp,%%ebp\n"
+                        /* Call FN. */
+                        "call    *%%ebx\n"
+#ifdef PIC
+                        "call    here\n"
+                        "here:\n"
+                        "popl    %%ebx\n"
+                        "addl    $_GLOBAL_OFFSET_TABLE_+[.-here], %%ebx\n"
+#endif
+                        /* Call exit */
+                        "movl    %%eax, %%ebx\n"
+                        "movl    %2, %%eax\n"
+                        "int     $0x80\n"
+                        "1:\n"
+                       : "=a" (res)
+                       : "a"(SYSCALL(clone)), "i"(SYSCALL(exit)),
+                         "c"(child_stack),
+                         "d"(parent_tidptr),
+                         "S"(newtls),
+                         "D"(child_tidptr)
+                       : "memory");
+  return res;
+}
 #endif  // defined(__x86_64__) && SANITIZER_LINUX
 
 #if SANITIZER_ANDROID
index 4497702..eed4f78 100644 (file)
@@ -42,7 +42,7 @@ uptr internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5);
 // internal_sigaction instead.
 int internal_sigaction_norestorer(int signum, const void *act, void *oldact);
 void internal_sigdelset(__sanitizer_sigset_t *set, int signum);
-#if defined(__x86_64__) || defined(__mips__) || defined(__aarch64__)
+#if defined(__x86_64__) || defined(__mips__) || defined(__aarch64__) || defined(__arm__) || defined(__i386__)
 uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
                     int *parent_tidptr, void *newtls, int *child_tidptr);
 #endif
index c919e4f..da5348e 100644 (file)
@@ -13,7 +13,8 @@
 #include "sanitizer_platform.h"
 
 #if SANITIZER_LINUX && (defined(__x86_64__) || defined(__mips__) || \
-                        defined(__aarch64__))
+                        defined(__aarch64__) || defined(__arm__) || \
+                        defined(__i386__))
 
 #include "sanitizer_stoptheworld.h"
 
@@ -510,4 +511,4 @@ uptr SuspendedThreadsList::RegisterCount() {
 } // namespace __sanitizer
 
 #endif // SANITIZER_LINUX && (defined(__x86_64__) || defined(__mips__)
-       // || defined(__aarch64__)
+       // || defined(__aarch64__) || defined(__arm__))