[compiler-rt] r197489 - [lsan] Introduce __lsan_(un)register_root_region().

Sergey Matveev earthdok at google.com
Tue Dec 17 03:11:23 PST 2013


Author: smatveev
Date: Tue Dec 17 05:11:23 2013
New Revision: 197489

URL: http://llvm.org/viewvc/llvm-project?rev=197489&view=rev
Log:
[lsan] Introduce __lsan_(un)register_root_region().

Add an interface for telling LSan that a region of memory is to be treated as a
source of live pointers. Useful for code which stores pointers in mapped memory.

Added:
    compiler-rt/trunk/lib/lsan/lit_tests/TestCases/register_root_region.cc
Modified:
    compiler-rt/trunk/include/sanitizer/lsan_interface.h
    compiler-rt/trunk/lib/lsan/lsan_common.cc
    compiler-rt/trunk/lib/lsan/lsan_common.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=197489&r1=197488&r2=197489&view=diff
==============================================================================
--- compiler-rt/trunk/include/sanitizer/lsan_interface.h (original)
+++ compiler-rt/trunk/include/sanitizer/lsan_interface.h Tue Dec 17 05:11:23 2013
@@ -23,13 +23,30 @@ extern "C" {
   // be treated as non-leaks. Disable/enable pairs may 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);
+
+  // Memory regions registered through this interface will be treated as sources
+  // of live pointers during leak checking. Useful if you store pointers in
+  // mapped memory.
+  // Points of note:
+  // - __lsan_unregister_root_region() must be called with the same pointer and
+  // size that have earlier been passed to __lsan_register_root_region()
+  // - LSan will skip any inaccessible memory when scanning a root region. E.g.,
+  // if you map memory within a larger region that you have mprotect'ed, you can
+  // register the entire large region.
+  // - the implementation is not optimized for performance. This interface is
+  // intended to be used for a small number of relatively static regions.
+  void __lsan_register_root_region(const void *p, size_t size);
+  void __lsan_unregister_root_region(const void *p, size_t size);
+
   // The user may optionally provide this function to disallow leak checking
   // for the program it is linked into (if the return value is non-zero). This
   // function must be defined as returning a constant value; any behavior beyond
   // that is unsupported.
   int __lsan_is_turned_off();
+
   // Calling this function makes LSan enter the leak checking phase immediately.
   // Use this if normal end-of-process leak checking happens too late (e.g. if
   // you have intentional memory leaks in your shutdown code). Calling this

Added: compiler-rt/trunk/lib/lsan/lit_tests/TestCases/register_root_region.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/lsan/lit_tests/TestCases/register_root_region.cc?rev=197489&view=auto
==============================================================================
--- compiler-rt/trunk/lib/lsan/lit_tests/TestCases/register_root_region.cc (added)
+++ compiler-rt/trunk/lib/lsan/lit_tests/TestCases/register_root_region.cc Tue Dec 17 05:11:23 2013
@@ -0,0 +1,32 @@
+// Test for __lsan_(un)register_root_region().
+// RUN: LSAN_BASE="use_stacks=0:use_registers=0"
+// RUN: %clangxx_lsan %s -o %t
+// RUN: LSAN_OPTIONS=$LSAN_BASE %t
+// RUN: LSAN_OPTIONS=$LSAN_BASE not %t foo 2>&1 | FileCheck %s
+// RUN: LSAN_OPTIONS=$LSAN_BASE:use_root_regions=0 not %t 2>&1 | FileCheck %s
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include <sanitizer/lsan_interface.h>
+
+int main(int argc, char *argv[]) {
+  size_t size = getpagesize() * 2;
+  void *p =
+      mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  assert(p);
+  // Make half of the memory inaccessible. LSan must not crash trying to read it.
+  assert(0 == mprotect((char *)p + size / 2, size / 2, PROT_NONE));
+
+  __lsan_register_root_region(p, size);
+  *((void **)p) = malloc(1337);
+  fprintf(stderr, "Test alloc: %p.\n", p);
+  if (argc > 1)
+    __lsan_unregister_root_region(p, size);
+  return 0;
+}
+// CHECK: Test alloc: [[ADDR:.*]].
+// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 1337 byte(s) leaked in 1 allocation(s)

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=197489&r1=197488&r2=197489&view=diff
==============================================================================
--- compiler-rt/trunk/lib/lsan/lsan_common.cc (original)
+++ compiler-rt/trunk/lib/lsan/lsan_common.cc Tue Dec 17 05:11:23 2013
@@ -17,6 +17,7 @@
 #include "sanitizer_common/sanitizer_common.h"
 #include "sanitizer_common/sanitizer_flags.h"
 #include "sanitizer_common/sanitizer_placement_new.h"
+#include "sanitizer_common/sanitizer_procmaps.h"
 #include "sanitizer_common/sanitizer_stackdepot.h"
 #include "sanitizer_common/sanitizer_stacktrace.h"
 #include "sanitizer_common/sanitizer_stoptheworld.h"
@@ -26,7 +27,8 @@
 #if CAN_SANITIZE_LEAKS
 namespace __lsan {
 
-// This mutex is used to prevent races between DoLeakCheck and IgnoreObject.
+// This mutex is used to prevent races between DoLeakCheck and IgnoreObject, and
+// also to protect the global list of root regions.
 BlockingMutex global_mutex(LINKER_INITIALIZED);
 
 THREADLOCAL int disable_counter;
@@ -46,6 +48,7 @@ static void InitializeFlags() {
   f->use_globals = true;
   f->use_stacks = true;
   f->use_tls = true;
+  f->use_root_regions = true;
   f->use_unaligned = false;
   f->use_poisoned = false;
   f->verbosity = 0;
@@ -58,6 +61,7 @@ static void InitializeFlags() {
     ParseFlag(options, &f->use_globals, "use_globals");
     ParseFlag(options, &f->use_stacks, "use_stacks");
     ParseFlag(options, &f->use_tls, "use_tls");
+    ParseFlag(options, &f->use_root_regions, "use_root_regions");
     ParseFlag(options, &f->use_unaligned, "use_unaligned");
     ParseFlag(options, &f->use_poisoned, "use_poisoned");
     ParseFlag(options, &f->report_objects, "report_objects");
@@ -77,8 +81,8 @@ SuppressionContext *suppression_ctx;
 
 void InitializeSuppressions() {
   CHECK(!suppression_ctx);
-  ALIGNED(64) static char placeholder_[sizeof(SuppressionContext)];
-  suppression_ctx = new(placeholder_) SuppressionContext;
+  ALIGNED(64) static char placeholder[sizeof(SuppressionContext)];
+  suppression_ctx = new(placeholder) SuppressionContext;
   char *suppressions_from_file;
   uptr buffer_size;
   if (ReadFileToBuffer(flags()->suppressions, &suppressions_from_file,
@@ -93,8 +97,22 @@ void InitializeSuppressions() {
     suppression_ctx->Parse(__lsan_default_suppressions());
 }
 
+struct RootRegion {
+  const void *begin;
+  uptr size;
+};
+
+InternalMmapVector<RootRegion> *root_regions;
+
+void InitializeRootRegions() {
+  CHECK(!root_regions);
+  ALIGNED(64) static char placeholder[sizeof(InternalMmapVector<RootRegion>)];
+  root_regions = new(placeholder) InternalMmapVector<RootRegion>(1);
+}
+
 void InitCommonLsan() {
   InitializeFlags();
+  InitializeRootRegions();
   if (common_flags()->detect_leaks) {
     // Initialization which can fail or print warnings should only be done if
     // LSan is actually enabled.
@@ -245,6 +263,38 @@ static void ProcessThreads(SuspendedThre
   }
 }
 
+static void ProcessRootRegion(Frontier *frontier, uptr root_begin,
+                              uptr root_end) {
+  MemoryMappingLayout proc_maps(/*cache_enabled*/true);
+  uptr begin, end, prot;
+  while (proc_maps.Next(&begin, &end,
+                        /*offset*/ 0, /*filename*/ 0, /*filename_size*/ 0,
+                        &prot)) {
+    uptr intersection_begin = Max(root_begin, begin);
+    uptr intersection_end = Min(end, root_end);
+    if (intersection_begin >= intersection_end) continue;
+    bool is_readable = prot & MemoryMappingLayout::kProtectionRead;
+    if (flags()->log_pointers)
+      Report("Root region %p-%p intersects with mapped region %p-%p (%s)\n",
+             root_begin, root_end, begin, end,
+             is_readable ? "readable" : "unreadable");
+    if (is_readable)
+      ScanRangeForPointers(intersection_begin, intersection_end, frontier,
+                           "ROOT", kReachable);
+  }
+}
+
+// Scans root regions for heap pointers.
+static void ProcessRootRegions(Frontier *frontier) {
+  if (!flags()->use_root_regions) return;
+  CHECK(root_regions);
+  for (uptr i = 0; i < root_regions->size(); i++) {
+    RootRegion region = (*root_regions)[i];
+    uptr begin_addr = reinterpret_cast<uptr>(region.begin);
+    ProcessRootRegion(frontier, begin_addr, begin_addr + region.size);
+  }
+}
+
 static void FloodFillTag(Frontier *frontier, ChunkTag tag) {
   while (frontier->size()) {
     uptr next_chunk = frontier->back();
@@ -284,6 +334,7 @@ static void ClassifyAllChunks(SuspendedT
   if (flags()->use_globals)
     ProcessGlobalRegions(&frontier);
   ProcessThreads(suspended_threads, &frontier);
+  ProcessRootRegions(&frontier);
   FloodFillTag(&frontier, kReachable);
   // The check here is relatively expensive, so we do this in a separate flood
   // fill. That way we can skip the check for chunks that are reachable
@@ -570,6 +621,46 @@ void __lsan_ignore_object(const void *p)
 #endif  // CAN_SANITIZE_LEAKS
 }
 
+SANITIZER_INTERFACE_ATTRIBUTE
+void __lsan_register_root_region(const void *begin, uptr size) {
+#if CAN_SANITIZE_LEAKS
+  BlockingMutexLock l(&global_mutex);
+  CHECK(root_regions);
+  RootRegion region = {begin, size};
+  root_regions->push_back(region);
+  if (flags()->verbosity)
+    Report("Registered root region at %p of size %llu\n", begin, size);
+#endif  // CAN_SANITIZE_LEAKS
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __lsan_unregister_root_region(const void *begin, uptr size) {
+#if CAN_SANITIZE_LEAKS
+  BlockingMutexLock l(&global_mutex);
+  CHECK(root_regions);
+  bool removed = false;
+  for (uptr i = 0; i < root_regions->size(); i++) {
+    RootRegion region = (*root_regions)[i];
+    if (region.begin == begin && region.size == size) {
+      removed = true;
+      uptr last_index = root_regions->size() - 1;
+      (*root_regions)[i] = (*root_regions)[last_index];
+      root_regions->pop_back();
+      if (flags()->verbosity)
+        Report("Unregistered root region at %p of size %llu\n", begin, size);
+      break;
+    }
+  }
+  if (!removed) {
+    Report(
+        "__lsan_unregister_root_region(): region at %p of size %llu has not "
+        "been registered.\n",
+        begin, size);
+    Die();
+  }
+#endif  // CAN_SANITIZE_LEAKS
+}
+
 SANITIZER_INTERFACE_ATTRIBUTE
 void __lsan_disable() {
 #if 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=197489&r1=197488&r2=197489&view=diff
==============================================================================
--- compiler-rt/trunk/lib/lsan/lsan_common.h (original)
+++ compiler-rt/trunk/lib/lsan/lsan_common.h Tue Dec 17 05:11:23 2013
@@ -63,6 +63,8 @@ struct Flags {
   bool use_registers;
   // TLS and thread-specific storage.
   bool use_tls;
+  // Regions added via __lsan_register_root_region().
+  bool use_root_regions;
 
   // Consider unaligned pointers valid.
   bool use_unaligned;





More information about the llvm-commits mailing list