[compiler-rt] r183412 - [lsan] Implement __lsan_ignore_object().

Sergey Matveev earthdok at google.com
Thu Jun 6 07:17:56 PDT 2013


Author: smatveev
Date: Thu Jun  6 09:17:56 2013
New Revision: 183412

URL: http://llvm.org/viewvc/llvm-project?rev=183412&view=rev
Log:
[lsan] Implement __lsan_ignore_object().

Leak annotation similar to HeapChecker's IgnoreObject().

Added:
    compiler-rt/trunk/lib/lsan/lit_tests/TestCases/ignore_object.cc
    compiler-rt/trunk/lib/lsan/lit_tests/TestCases/ignore_object_errors.cc
Modified:
    compiler-rt/trunk/include/sanitizer/lsan_interface.h
    compiler-rt/trunk/lib/asan/asan_allocator2.cc
    compiler-rt/trunk/lib/lsan/lsan_allocator.cc
    compiler-rt/trunk/lib/lsan/lsan_common.cc
    compiler-rt/trunk/lib/lsan/lsan_common.h
    compiler-rt/trunk/lib/sanitizer_common/sanitizer_allocator.h

Modified: compiler-rt/trunk/include/sanitizer/lsan_interface.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/include/sanitizer/lsan_interface.h?rev=183412&r1=183411&r2=183412&view=diff
==============================================================================
--- compiler-rt/trunk/include/sanitizer/lsan_interface.h (original)
+++ compiler-rt/trunk/include/sanitizer/lsan_interface.h Thu Jun  6 09:17:56 2013
@@ -23,7 +23,8 @@ extern "C" {
   // be treated as non-leaks. Disable/enable pairs can be nested.
   void __lsan_disable();
   void __lsan_enable();
-
+  // The heap object into which p points will be treated as a non-leak.
+  void __lsan_ignore_object(const void *p);
 #ifdef __cplusplus
 }  // extern "C"
 

Modified: compiler-rt/trunk/lib/asan/asan_allocator2.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/asan/asan_allocator2.cc?rev=183412&r1=183411&r2=183412&view=diff
==============================================================================
--- compiler-rt/trunk/lib/asan/asan_allocator2.cc (original)
+++ compiler-rt/trunk/lib/asan/asan_allocator2.cc Thu Jun  6 09:17:56 2013
@@ -792,6 +792,20 @@ template void ForEachChunk<MarkIndirectl
 template void ForEachChunk<CollectSuppressedCb>(
     CollectSuppressedCb const &callback);
 #endif  // CAN_SANITIZE_LEAKS
+
+IgnoreObjectResult IgnoreObjectLocked(const void *p) {
+  uptr addr = reinterpret_cast<uptr>(p);
+  __asan::AsanChunk *m = __asan::GetAsanChunkByAddr(addr);
+  if (!m) return kIgnoreObjectInvalid;
+  if ((m->chunk_state == __asan::CHUNK_ALLOCATED) && m->AddrIsInside(addr)) {
+    if (m->lsan_tag == kSuppressed)
+      return kIgnoreObjectAlreadyIgnored;
+    m->lsan_tag = __lsan::kSuppressed;
+    return kIgnoreObjectSuccess;
+  } else {
+    return kIgnoreObjectInvalid;
+  }
+}
 }  // namespace __lsan
 
 extern "C" {

Added: compiler-rt/trunk/lib/lsan/lit_tests/TestCases/ignore_object.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/lsan/lit_tests/TestCases/ignore_object.cc?rev=183412&view=auto
==============================================================================
--- compiler-rt/trunk/lib/lsan/lit_tests/TestCases/ignore_object.cc (added)
+++ compiler-rt/trunk/lib/lsan/lit_tests/TestCases/ignore_object.cc Thu Jun  6 09:17:56 2013
@@ -0,0 +1,30 @@
+// Test for __lsan_ignore_object().
+// RUN: LSAN_BASE="report_blocks=1:use_registers=0:use_stacks=0:use_globals=0:use_tls=0:verbosity=2"
+// RUN: %clangxx_lsan -I %p/../../../../include %s -o %t
+// RUN: LSAN_OPTIONS=$LSAN_BASE %t 2>&1 | FileCheck %s
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "sanitizer/lsan_interface.h"
+
+int main() {
+  {
+    // The first malloc call can cause an allocation in libdl. Ignore it here so
+    // it doesn't show up in our output.
+    __lsan::ScopedDisabler d;
+    malloc(1);
+  }
+  // Explicitly ignored block.
+  void **p = new void *;
+  // Transitively ignored block.
+  *p = malloc(666);
+  // Non-ignored block.
+  volatile void *q = malloc(1337);
+  fprintf(stderr, "Test alloc: %p.\n", p);
+  __lsan_ignore_object(p);
+  return 0;
+}
+// CHECK: Test alloc: [[ADDR:.*]].
+// CHECK: ignoring heap object at [[ADDR]] 
+// CHECK: SUMMARY: LeakSanitizer: 1337 byte(s) leaked in 1 allocation(s)

Added: compiler-rt/trunk/lib/lsan/lit_tests/TestCases/ignore_object_errors.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/lsan/lit_tests/TestCases/ignore_object_errors.cc?rev=183412&view=auto
==============================================================================
--- compiler-rt/trunk/lib/lsan/lit_tests/TestCases/ignore_object_errors.cc (added)
+++ compiler-rt/trunk/lib/lsan/lit_tests/TestCases/ignore_object_errors.cc Thu Jun  6 09:17:56 2013
@@ -0,0 +1,22 @@
+// Test for incorrect use of __lsan_ignore_object().
+// RUN: LSAN_BASE="verbosity=1"
+// RUN: %clangxx_lsan -I %p/../../../../include %s -o %t
+// RUN: LSAN_OPTIONS=$LSAN_BASE %t 2>&1 | FileCheck %s
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "sanitizer/lsan_interface.h"
+
+int main() {
+  void *p = malloc(1337);
+  fprintf(stderr, "Test alloc: %p.\n", p);
+  __lsan_ignore_object(p);
+  __lsan_ignore_object(p);
+  free(p);
+  __lsan_ignore_object(p);
+  return 0;
+}
+// CHECK: Test alloc: [[ADDR:.*]].
+// CHECK: heap object at [[ADDR]] is already being ignored
+// CHECK: no heap object found at [[ADDR]]

Modified: compiler-rt/trunk/lib/lsan/lsan_allocator.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/lsan/lsan_allocator.cc?rev=183412&r1=183411&r2=183412&view=diff
==============================================================================
--- compiler-rt/trunk/lib/lsan/lsan_allocator.cc (original)
+++ compiler-rt/trunk/lib/lsan/lsan_allocator.cc Thu Jun  6 09:17:56 2013
@@ -190,6 +190,21 @@ template void ForEachChunk<MarkIndirectl
     MarkIndirectlyLeakedCb const &callback);
 template void ForEachChunk<CollectSuppressedCb>(
     CollectSuppressedCb const &callback);
+
+IgnoreObjectResult IgnoreObjectLocked(const void *p) {
+  void *chunk = allocator.GetBlockBegin(p);
+  if (!chunk || p < chunk) return kIgnoreObjectInvalid;
+  ChunkMetadata *m = Metadata(chunk);
+  CHECK(m);
+  if (m->allocated && (uptr)p < (uptr)chunk + m->requested_size) {
+    if (m->tag == kSuppressed)
+      return kIgnoreObjectAlreadyIgnored;
+    m->tag = kSuppressed;
+    return kIgnoreObjectSuccess;
+  } else {
+    return kIgnoreObjectInvalid;
+  }
+}
 }  // namespace __lsan
 
 extern "C" {

Modified: compiler-rt/trunk/lib/lsan/lsan_common.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/lsan/lsan_common.cc?rev=183412&r1=183411&r2=183412&view=diff
==============================================================================
--- compiler-rt/trunk/lib/lsan/lsan_common.cc (original)
+++ compiler-rt/trunk/lib/lsan/lsan_common.cc Thu Jun  6 09:17:56 2013
@@ -23,6 +23,9 @@
 #if CAN_SANITIZE_LEAKS
 namespace __lsan {
 
+// This mutex is used to prevent races between DoLeakCheck and SuppressObject.
+BlockingMutex global_mutex(LINKER_INITIALIZED);
+
 Flags lsan_flags;
 
 static void InitializeFlags() {
@@ -37,6 +40,7 @@ static void InitializeFlags() {
   f->use_stacks = true;
   f->use_tls = true;
   f->use_unaligned = false;
+  f->verbosity = 0;
   f->log_pointers = false;
   f->log_threads = false;
 
@@ -52,6 +56,7 @@ static void InitializeFlags() {
     CHECK_GE(&f->resolution, 0);
     ParseFlag(options, &f->max_leaks, "max_leaks");
     CHECK_GE(&f->max_leaks, 0);
+    ParseFlag(options, &f->verbosity, "verbosity");
     ParseFlag(options, &f->log_pointers, "log_pointers");
     ParseFlag(options, &f->log_threads, "log_threads");
     ParseFlag(options, &f->exitcode, "exitcode");
@@ -311,12 +316,13 @@ static void DoLeakCheckCallback(const Su
 }
 
 void DoLeakCheck() {
+  BlockingMutexLock l(&global_mutex);
   static bool already_done;
+  CHECK(!already_done);
+  already_done = true;
   LeakCheckResult result = kFatalError;
   LockThreadRegistry();
   LockAllocator();
-  CHECK(!already_done);
-  already_done = true;
   StopTheWorld(DoLeakCheckCallback, &result);
   UnlockAllocator();
   UnlockThreadRegistry();
@@ -394,4 +400,22 @@ void LeakReport::PrintSummary() {
 }
 
 }  // namespace __lsan
+
+using namespace __lsan;  // NOLINT
+
+extern "C" {
+void __lsan_ignore_object(const void *p) {
+  // Cannot use PointsIntoChunk or LsanMetadata here, since the allocator is not
+  // locked.
+  BlockingMutexLock l(&global_mutex);
+  IgnoreObjectResult res = IgnoreObjectLocked(p);
+  if (res == kIgnoreObjectInvalid && flags()->verbosity >= 1)
+    Report("__lsan_ignore_object(): no heap object found at %p", p);
+  if (res == kIgnoreObjectAlreadyIgnored && flags()->verbosity >= 1)
+    Report("__lsan_ignore_object(): "
+           "heap object at %p is already being ignored\n", p);
+  if (res == kIgnoreObjectSuccess && flags()->verbosity >= 2)
+    Report("__lsan_ignore_object(): ignoring heap object at %p\n", p);
+}
+}  // extern "C"
 #endif  // CAN_SANITIZE_LEAKS

Modified: compiler-rt/trunk/lib/lsan/lsan_common.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/lsan/lsan_common.h?rev=183412&r1=183411&r2=183412&view=diff
==============================================================================
--- compiler-rt/trunk/lib/lsan/lsan_common.h (original)
+++ compiler-rt/trunk/lib/lsan/lsan_common.h Thu Jun  6 09:17:56 2013
@@ -64,6 +64,9 @@ struct Flags {
   // Consider unaligned pointers valid.
   bool use_unaligned;
 
+  // User-visible verbosity.
+  int verbosity;
+
   // Debug logging.
   bool log_pointers;
   bool log_threads;
@@ -153,6 +156,12 @@ class CollectSuppressedCb {
   InternalVector<uptr> *frontier_;
 };
 
+enum IgnoreObjectResult {
+  kIgnoreObjectSuccess,
+  kIgnoreObjectAlreadyIgnored,
+  kIgnoreObjectInvalid
+};
+
 // The following must be implemented in the parent tool.
 
 template<typename Callable> void ForEachChunk(Callable const &callback);
@@ -172,6 +181,8 @@ bool GetThreadRangesLocked(uptr os_id, u
 void *PointsIntoChunk(void *p);
 // Return address of user-visible chunk contained in this allocator chunk.
 void *GetUserBegin(void *p);
+// Helper for __lsan_ignore_object().
+IgnoreObjectResult IgnoreObjectLocked(const void *p);
 // Wrapper for chunk metadata operations.
 class LsanMetadata {
  public:

Modified: compiler-rt/trunk/lib/sanitizer_common/sanitizer_allocator.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_allocator.h?rev=183412&r1=183411&r2=183412&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_allocator.h (original)
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_allocator.h Thu Jun  6 09:17:56 2013
@@ -344,15 +344,15 @@ class SizeClassAllocator64 {
     region->n_freed += b->count;
   }
 
-  static bool PointerIsMine(void *p) {
+  static bool PointerIsMine(const void *p) {
     return reinterpret_cast<uptr>(p) / kSpaceSize == kSpaceBeg / kSpaceSize;
   }
 
-  static uptr GetSizeClass(void *p) {
+  static uptr GetSizeClass(const void *p) {
     return (reinterpret_cast<uptr>(p) / kRegionSize) % kNumClassesRounded;
   }
 
-  void *GetBlockBegin(void *p) {
+  void *GetBlockBegin(const void *p) {
     uptr class_id = GetSizeClass(p);
     uptr size = SizeClassMap::Size(class_id);
     if (!size) return 0;
@@ -671,15 +671,15 @@ class SizeClassAllocator32 {
     sci->free_list.push_front(b);
   }
 
-  bool PointerIsMine(void *p) {
+  bool PointerIsMine(const void *p) {
     return GetSizeClass(p) != 0;
   }
 
-  uptr GetSizeClass(void *p) {
+  uptr GetSizeClass(const void *p) {
     return possible_regions[ComputeRegionId(reinterpret_cast<uptr>(p))];
   }
 
-  void *GetBlockBegin(void *p) {
+  void *GetBlockBegin(const void *p) {
     CHECK(PointerIsMine(p));
     uptr mem = reinterpret_cast<uptr>(p);
     uptr beg = ComputeRegionBeg(mem);
@@ -1006,7 +1006,7 @@ class LargeMmapAllocator {
     return res;
   }
 
-  bool PointerIsMine(void *p) {
+  bool PointerIsMine(const void *p) {
     return GetBlockBegin(p) != 0;
   }
 
@@ -1021,7 +1021,7 @@ class LargeMmapAllocator {
     return GetHeader(p) + 1;
   }
 
-  void *GetBlockBegin(void *ptr) {
+  void *GetBlockBegin(const void *ptr) {
     uptr p = reinterpret_cast<uptr>(ptr);
     SpinMutexLock l(&mutex_);
     uptr nearest_chunk = 0;
@@ -1231,7 +1231,7 @@ class CombinedAllocator {
     return secondary_.GetMetaData(p);
   }
 
-  void *GetBlockBegin(void *p) {
+  void *GetBlockBegin(const void *p) {
     if (primary_.PointerIsMine(p))
       return primary_.GetBlockBegin(p);
     return secondary_.GetBlockBegin(p);





More information about the llvm-commits mailing list