[compiler-rt] r182249 - [lsan] Common leak checking module.

Sergey Matveev earthdok at google.com
Mon May 20 04:06:50 PDT 2013


Author: smatveev
Date: Mon May 20 06:06:50 2013
New Revision: 182249

URL: http://llvm.org/viewvc/llvm-project?rev=182249&view=rev
Log:
[lsan] Common leak checking module.

Leak checking functionality which will be shared between
LSan/ASan/MSan.

Added:
    compiler-rt/trunk/lib/lsan/lsan_common.cc
    compiler-rt/trunk/lib/lsan/lsan_common.h
    compiler-rt/trunk/lib/lsan/lsan_common_linux.cc

Added: compiler-rt/trunk/lib/lsan/lsan_common.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/lsan/lsan_common.cc?rev=182249&view=auto
==============================================================================
--- compiler-rt/trunk/lib/lsan/lsan_common.cc (added)
+++ compiler-rt/trunk/lib/lsan/lsan_common.cc Mon May 20 06:06:50 2013
@@ -0,0 +1,380 @@
+//=-- lsan_common.cc ------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of LeakSanitizer.
+// Implementation of common leak checking functionality.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lsan_common.h"
+
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_flags.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
+#include "sanitizer_common/sanitizer_stacktrace.h"
+#include "sanitizer_common/sanitizer_stoptheworld.h"
+
+namespace __lsan {
+
+Flags lsan_flags;
+
+static void InitializeFlags() {
+  Flags *f = flags();
+  // Default values.
+  f->sources = kSourceAllAligned;
+  f->report_blocks = false;
+  f->resolution = 0;
+  f->max_leaks = 0;
+  f->log_pointers = false;
+  f->log_threads = false;
+
+  const char *options = GetEnv("LSAN_OPTIONS");
+  if (options) {
+    bool aligned = true;
+    ParseFlag(options, &aligned, "aligned");
+    if (!aligned) f->sources |= kSourceUnaligned;
+    ParseFlag(options, &f->report_blocks, "report_blocks");
+    ParseFlag(options, &f->resolution, "resolution");
+    CHECK_GE(&f->resolution, 0);
+    ParseFlag(options, &f->max_leaks, "max_leaks");
+    CHECK_GE(&f->max_leaks, 0);
+    ParseFlag(options, &f->log_pointers, "log_pointers");
+    ParseFlag(options, &f->log_threads, "log_threads");
+  }
+}
+
+void InitCommonLsan() {
+  InitializeFlags();
+  InitializePlatformSpecificModules();
+}
+
+static inline bool CanBeAHeapPointer(uptr p) {
+  // Since our heap is located in mmap-ed memory, we can assume a sensible lower
+  // boundary on heap addresses.
+  const uptr kMinAddress = 4 * 4096;
+  if (p < kMinAddress) return false;
+#ifdef __x86_64__
+  // Accept only canonical form user-space addresses.
+  return ((p >> 47) == 0);
+#else
+  return true;
+#endif
+}
+
+// Scan the memory range, looking for byte patterns that point into allocator
+// chunks. Mark those chunks with tag and add them to the frontier.
+// There are two usage modes for this function: finding non-leaked chunks
+// (tag = kReachable) and finding indirectly leaked chunks
+// (tag = kIndirectlyLeaked). In the second case, there's no flood fill,
+// so frontier = 0.
+void ScanRangeForPointers(uptr begin, uptr end, InternalVector<uptr> *frontier,
+                          const char *region_type, ChunkTag tag) {
+  const uptr alignment = flags()->pointer_alignment();
+  if (flags()->log_pointers)
+    Report("Scanning %s range %p-%p.\n", region_type, begin, end);
+  uptr pp = begin;
+  if (pp % alignment)
+    pp = pp + alignment - pp % alignment;
+  for (; pp + sizeof(uptr) <= end; pp += alignment) {
+    void *p = *reinterpret_cast<void**>(pp);
+    if (!CanBeAHeapPointer(reinterpret_cast<uptr>(p))) continue;
+    // FIXME: PointsIntoChunk is SLOW because GetBlockBegin() in
+    // LargeMmapAllocator involves a lock and a linear search.
+    void *chunk = PointsIntoChunk(p);
+    if (!chunk) continue;
+    LsanMetadata m(chunk);
+    if (m.tag() == kReachable) continue;
+    m.set_tag(tag);
+    if (flags()->log_pointers)
+      Report("%p: found %p pointing into chunk %p-%p of size %llu.\n", pp, p,
+             chunk, reinterpret_cast<uptr>(chunk) + m.requested_size(),
+             m.requested_size());
+    if (frontier)
+      frontier->push_back(reinterpret_cast<uptr>(chunk));
+  }
+}
+
+// Scan thread data (stacks and TLS) for heap pointers.
+static void ProcessThreads(SuspendedThreadsList const &suspended_threads,
+                           InternalVector<uptr> *frontier) {
+  InternalScopedBuffer<uptr> registers(SuspendedThreadsList::RegisterCount());
+  uptr registers_begin = reinterpret_cast<uptr>(registers.data());
+  uptr registers_end = registers_begin + registers.size();
+  for (uptr i = 0; i < suspended_threads.thread_count(); i++) {
+    uptr os_id = static_cast<uptr>(suspended_threads.GetThreadID(i));
+    if (flags()->log_threads) Report("Processing thread %d.\n", os_id);
+    uptr stack_begin, stack_end, tls_begin, tls_end, cache_begin, cache_end;
+    bool thread_found = GetThreadRangesLocked(os_id, &stack_begin, &stack_end,
+                                              &tls_begin, &tls_end,
+                                              &cache_begin, &cache_end);
+    if (!thread_found) {
+      // If a thread can't be found in the thread registry, it's probably in the
+      // process of destruction. Log this event and move on.
+      if (flags()->log_threads)
+        Report("Thread %d not found in registry.\n", os_id);
+      continue;
+    }
+    uptr sp;
+    bool have_registers =
+        (suspended_threads.GetRegistersAndSP(i, registers.data(), &sp) == 0);
+    if (!have_registers) {
+      Report("Unable to get registers from thread %d.\n");
+      // If unable to get SP, consider the entire stack to be reachable.
+      sp = stack_begin;
+    }
+
+    if (flags()->use_registers() && have_registers)
+      ScanRangeForPointers(registers_begin, registers_end, frontier,
+                           "REGISTERS", kReachable);
+
+    if (flags()->use_stacks()) {
+      if (flags()->log_threads)
+        Report("Stack at %p-%p, SP = %p.\n", stack_begin, stack_end, sp);
+      if (sp < stack_begin || sp >= stack_end) {
+        // SP is outside the recorded stack range (e.g. the thread is running a
+        // signal handler on alternate stack). Again, consider the entire stack
+        // range to be reachable.
+        if (flags()->log_threads)
+          Report("WARNING: stack_pointer not in stack_range.\n");
+      } else {
+        // Shrink the stack range to ignore out-of-scope values.
+        stack_begin = sp;
+      }
+      ScanRangeForPointers(stack_begin, stack_end, frontier, "STACK",
+                           kReachable);
+    }
+
+    if (flags()->use_tls()) {
+      if (flags()->log_threads) Report("TLS at %p-%p.\n", tls_begin, tls_end);
+      // Because LSan should not be loaded with dlopen(), we can assume
+      // that allocator cache will be part of static TLS image.
+      CHECK_LE(tls_begin, cache_begin);
+      CHECK_GE(tls_end, cache_end);
+      if (tls_begin < cache_begin)
+        ScanRangeForPointers(tls_begin, cache_begin, frontier, "TLS",
+                             kReachable);
+      if (tls_end > cache_end)
+        ScanRangeForPointers(cache_end, tls_end, frontier, "TLS", kReachable);
+    }
+  }
+}
+
+static void FloodFillReachable(InternalVector<uptr> *frontier) {
+  while (frontier->size()) {
+    uptr next_chunk = frontier->back();
+    frontier->pop_back();
+    LsanMetadata m(reinterpret_cast<void *>(next_chunk));
+    ScanRangeForPointers(next_chunk, next_chunk + m.requested_size(), frontier,
+                         "HEAP", kReachable);
+  }
+}
+
+// Mark leaked chunks which are reachable from other leaked chunks.
+void MarkIndirectlyLeakedCb::operator()(void *p) const {
+  LsanMetadata m(p);
+  if (m.allocated() && m.tag() != kReachable) {
+    ScanRangeForPointers(reinterpret_cast<uptr>(p),
+                         reinterpret_cast<uptr>(p) + m.requested_size(),
+                         /* frontier */ 0, "HEAP", kIndirectlyLeaked);
+  }
+}
+
+// Set the appropriate tag on each chunk.
+static void ClassifyAllChunks(SuspendedThreadsList const &suspended_threads) {
+  // Holds the flood fill frontier.
+  InternalVector<uptr> frontier(GetPageSizeCached());
+
+  if (flags()->use_globals())
+    ProcessGlobalRegions(&frontier);
+  ProcessThreads(suspended_threads, &frontier);
+  FloodFillReachable(&frontier);
+  ProcessPlatformSpecificAllocations(&frontier);
+  FloodFillReachable(&frontier);
+
+  // Now all reachable chunks are marked. Iterate over leaked chunks and mark
+  // those that are reachable from other leaked chunks.
+  if (flags()->log_pointers)
+    Report("Now scanning leaked blocks for pointers.\n");
+  ForEachChunk(MarkIndirectlyLeakedCb());
+}
+
+void ClearTagCb::operator()(void *p) const {
+  LsanMetadata m(p);
+  m.set_tag(kDirectlyLeaked);
+}
+
+static void PrintStackTraceById(u32 stack_trace_id) {
+  CHECK(stack_trace_id);
+  uptr size = 0;
+  const uptr *trace = StackDepotGet(stack_trace_id, &size);
+  StackTrace::PrintStack(trace, size, common_flags()->symbolize,
+                         common_flags()->strip_path_prefix, 0);
+}
+
+static void LockAndSuspendThreads(StopTheWorldCallback callback, void *arg) {
+  LockThreadRegistry();
+  LockAllocator();
+  StopTheWorld(callback, arg);
+  // Allocator must be unlocked by the callback.
+  UnlockThreadRegistry();
+}
+
+///// Normal leak checking. /////
+
+void CollectLeaksCb::operator()(void *p) const {
+  LsanMetadata m(p);
+  if (!m.allocated()) return;
+  if (m.tag() != kReachable) {
+    uptr resolution = flags()->resolution;
+    if (resolution > 0) {
+      uptr size = 0;
+      const uptr *trace = StackDepotGet(m.stack_trace_id(), &size);
+      size = Min(size, resolution);
+      leak_report_->Add(StackDepotPut(trace, size), m.requested_size(),
+                        m.tag());
+    } else {
+      leak_report_->Add(m.stack_trace_id(), m.requested_size(), m.tag());
+    }
+  }
+}
+
+static void CollectLeaks(LeakReport *leak_report) {
+  ForEachChunk(CollectLeaksCb(leak_report));
+}
+
+void PrintLeakedCb::operator()(void *p) const {
+  LsanMetadata m(p);
+  if (!m.allocated()) return;
+  if (m.tag() != kReachable) {
+    CHECK(m.tag() == kDirectlyLeaked || m.tag() == kIndirectlyLeaked);
+    Printf("%s leaked %llu byte block at %p\n",
+           m.tag() == kDirectlyLeaked ? "Directly" : "Indirectly",
+           m.requested_size(), p);
+  }
+}
+
+static void PrintLeaked() {
+  Printf("\nReporting individual blocks:\n");
+  ForEachChunk(PrintLeakedCb());
+}
+
+static void DoLeakCheckCallback(const SuspendedThreadsList &suspended_threads,
+                                void *arg) {
+  // Allocator must not be locked when we call GetRegionBegin().
+  UnlockAllocator();
+  bool *success = reinterpret_cast<bool *>(arg);
+  ClassifyAllChunks(suspended_threads);
+  LeakReport leak_report;
+  CollectLeaks(&leak_report);
+  if (!leak_report.IsEmpty()) {
+    leak_report.PrintLargest(flags()->max_leaks);
+    if (flags()->report_blocks)
+      PrintLeaked();
+  }
+  ForEachChunk(ClearTagCb());
+  *success = true;
+}
+
+void DoLeakCheck() {
+  bool success = false;
+  LockAndSuspendThreads(DoLeakCheckCallback, &success);
+  if (!success)
+    Report("Leak check failed!\n");
+}
+
+///// Reporting of leaked blocks' addresses (for testing). /////
+
+void ReportLeakedCb::operator()(void *p) const {
+  LsanMetadata m(p);
+  if (m.allocated() && m.tag() != kReachable)
+    leaked_->push_back(p);
+}
+
+struct ReportLeakedParam {
+  InternalVector<void *> *leaked;
+  uptr sources;
+  bool success;
+};
+
+static void ReportLeakedCallback(const SuspendedThreadsList &suspended_threads,
+                                 void *arg) {
+  // Allocator must not be locked when we call GetRegionBegin().
+  UnlockAllocator();
+  ReportLeakedParam *param = reinterpret_cast<ReportLeakedParam *>(arg);
+  flags()->sources = param->sources;
+  ClassifyAllChunks(suspended_threads);
+  ForEachChunk(ReportLeakedCb(param->leaked));
+  ForEachChunk(ClearTagCb());
+  param->success = true;
+}
+
+void ReportLeaked(InternalVector<void *> *leaked, uptr sources) {
+  CHECK_EQ(0, leaked->size());
+  ReportLeakedParam param;
+  param.leaked = leaked;
+  param.success = false;
+  param.sources = sources;
+  LockAndSuspendThreads(ReportLeakedCallback, &param);
+  CHECK(param.success);
+}
+
+///// LeakReport implementation. /////
+
+// A hard limit on the number of distinct leaks, to avoid quadratic complexity
+// in LeakReport::Add(). We don't expect to ever see this many leaks in
+// real-world applications.
+// FIXME: Get rid of this limit by changing the implementation of LeakReport to
+// use a hash table.
+const uptr kMaxLeaksConsidered = 1000;
+
+void LeakReport::Add(u32 stack_trace_id, uptr leaked_size, ChunkTag tag) {
+  CHECK(tag == kDirectlyLeaked || tag == kIndirectlyLeaked);
+  bool is_directly_leaked = (tag == kDirectlyLeaked);
+  for (uptr i = 0; i < leaks_.size(); i++)
+    if (leaks_[i].stack_trace_id == stack_trace_id &&
+        leaks_[i].is_directly_leaked == is_directly_leaked) {
+      leaks_[i].hit_count++;
+      leaks_[i].total_size += leaked_size;
+      return;
+    }
+  if (leaks_.size() == kMaxLeaksConsidered) return;
+  Leak leak = { /* hit_count */ 1, leaked_size, stack_trace_id,
+                is_directly_leaked };
+  leaks_.push_back(leak);
+}
+
+static bool IsLarger(const Leak &leak1, const Leak &leak2) {
+  return leak1.total_size > leak2.total_size;
+}
+
+void LeakReport::PrintLargest(uptr max_leaks) {
+  CHECK(leaks_.size() <= kMaxLeaksConsidered);
+  Printf("\n");
+  if (leaks_.size() == kMaxLeaksConsidered)
+    Printf("Too many leaks! Only the first %llu leaks encountered will be "
+           "reported.\n",
+           kMaxLeaksConsidered);
+  if (max_leaks > 0 && max_leaks < leaks_.size())
+    Printf("The %llu largest leak%s:\n", max_leaks, max_leaks > 1 ? "s" : "");
+  InternalSort(&leaks_, leaks_.size(), IsLarger);
+  max_leaks = max_leaks > 0 ? Min(max_leaks, leaks_.size()) : leaks_.size();
+  for (uptr i = 0; i < max_leaks; i++) {
+    Printf("\n%s leak of %llu bytes in %llu objects allocated from:\n",
+           leaks_[i].is_directly_leaked ? "Direct" : "Indirect",
+           leaks_[i].total_size, leaks_[i].hit_count);
+    PrintStackTraceById(leaks_[i].stack_trace_id);
+  }
+  if (max_leaks < leaks_.size()) {
+    uptr remaining = leaks_.size() - max_leaks;
+    Printf("\nOmitting %llu more leak%s.\n", remaining,
+           remaining > 1 ? "s" : "");
+  }
+}
+
+}  // namespace __lsan

Added: compiler-rt/trunk/lib/lsan/lsan_common.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/lsan/lsan_common.h?rev=182249&view=auto
==============================================================================
--- compiler-rt/trunk/lib/lsan/lsan_common.h (added)
+++ compiler-rt/trunk/lib/lsan/lsan_common.h Mon May 20 06:06:50 2013
@@ -0,0 +1,190 @@
+//=-- lsan_common.h -------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of LeakSanitizer.
+// Private LSan header.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LSAN_COMMON_H
+#define LSAN_COMMON_H
+
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_internal_defs.h"
+#include "sanitizer_common/sanitizer_symbolizer.h"
+
+namespace __lsan {
+
+// Chunk tags.
+enum ChunkTag {
+  kDirectlyLeaked = 0,  // default
+  kIndirectlyLeaked = 1,
+  kReachable = 2
+};
+
+// Sources of pointers.
+// Global variables (.data and .bss).
+const uptr kSourceGlobals = 1 << 0;
+// Thread stacks.
+const uptr kSourceStacks = 1 << 1;
+// TLS and thread-specific storage.
+const uptr kSourceTLS = 1 << 2;
+// Thread registers.
+const uptr kSourceRegisters = 1 << 3;
+// Unaligned pointers.
+const uptr kSourceUnaligned = 1 << 4;
+
+// Aligned pointers everywhere.
+const uptr kSourceAllAligned =
+    kSourceGlobals | kSourceStacks | kSourceTLS | kSourceRegisters;
+
+struct Flags {
+  bool use_registers() const { return sources & kSourceRegisters; }
+  bool use_globals() const { return sources & kSourceGlobals; }
+  bool use_stacks() const { return sources & kSourceStacks; }
+  bool use_tls() const { return sources & kSourceTLS; }
+  uptr pointer_alignment() const {
+    return (sources & kSourceUnaligned) ? 1 : sizeof(uptr);
+  }
+
+  uptr sources;
+  // Print addresses of leaked blocks after main leak report.
+  bool report_blocks;
+  // Aggregate two blocks into one leak if this many stack frames match. If
+  // zero, the entire stack trace must match.
+  int resolution;
+  // The number of leaks reported.
+  int max_leaks;
+
+  // Debug logging.
+  bool log_pointers;
+  bool log_threads;
+};
+
+extern Flags lsan_flags;
+inline Flags *flags() { return &lsan_flags; }
+
+void InitCommonLsan();
+// Testing interface. Find leaked chunks and dump their addresses to vector.
+void ReportLeaked(InternalVector<void *> *leaked, uptr sources);
+// Normal leak check. Find leaks and print a report according to flags.
+void DoLeakCheck();
+
+struct Leak {
+  uptr hit_count;
+  uptr total_size;
+  u32 stack_trace_id;
+  bool is_directly_leaked;
+};
+
+// Aggregates leaks by stack trace prefix.
+class LeakReport {
+ public:
+  LeakReport() : leaks_(1) {}
+  void Add(u32 stack_trace_id, uptr leaked_size, ChunkTag tag);
+  void PrintLargest(uptr max_leaks);
+  bool IsEmpty() { return leaks_.size() == 0; }
+ private:
+  InternalVector<Leak> leaks_;
+};
+
+// Platform-specific functions.
+void InitializePlatformSpecificModules();
+void ProcessGlobalRegions(InternalVector<uptr> *frontier);
+void ProcessPlatformSpecificAllocations(InternalVector<uptr> *frontier);
+
+void ScanRangeForPointers(uptr begin, uptr end, InternalVector<uptr> *frontier,
+                          const char *region_type, ChunkTag tag);
+
+// Callables for iterating over chunks. Those classes are used as template
+// parameters in ForEachChunk, so we must expose them here to allow for explicit
+// template instantiation.
+
+// Identifies unreachable chunks which must be treated as reachable. Marks them
+// as reachable and adds them to the frontier.
+class ProcessPlatformSpecificAllocationsCb {
+ public:
+  explicit ProcessPlatformSpecificAllocationsCb(InternalVector<uptr> *frontier)
+      : frontier_(frontier) {}
+  void operator()(void *p) const;
+ private:
+  InternalVector<uptr> *frontier_;
+};
+
+// Prints addresses of unreachable chunks.
+class PrintLeakedCb {
+ public:
+  void operator()(void *p) const;
+};
+
+// Aggregates unreachable chunks into a LeakReport.
+class CollectLeaksCb {
+ public:
+  explicit CollectLeaksCb(LeakReport *leak_report)
+      : leak_report_(leak_report) {}
+  void operator()(void *p) const;
+ private:
+  LeakReport *leak_report_;
+};
+
+// Dumps addresses of unreachable chunks to a vector (for testing).
+class ReportLeakedCb {
+ public:
+  explicit ReportLeakedCb(InternalVector<void *> *leaked) : leaked_(leaked) {}
+  void operator()(void *p) const;
+ private:
+  InternalVector<void *> *leaked_;
+};
+
+// Resets each chunk's tag to default (kDirectlyLeaked).
+class ClearTagCb {
+ public:
+  void operator()(void *p) const;
+};
+
+// Scans each leaked chunk for pointers to other leaked chunks, and marks each
+// of them as indirectly leaked.
+class MarkIndirectlyLeakedCb {
+ public:
+  void operator()(void *p) const;
+};
+
+// The following must be implemented in the parent tool.
+
+template<typename Callable> void ForEachChunk(Callable const &callback);
+// The address range occupied by the global allocator object.
+void GetAllocatorGlobalRange(uptr *begin, uptr *end);
+// Wrappers for allocator's ForceLock()/ForceUnlock().
+void LockAllocator();
+void UnlockAllocator();
+// Wrappers for ThreadRegistry access.
+void LockThreadRegistry();
+void UnlockThreadRegistry();
+bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end,
+                           uptr *tls_begin, uptr *tls_end,
+                           uptr *cache_begin, uptr *cache_end);
+// If p points into a chunk that has been allocated to the user, return its
+// address. Otherwise, return 0.
+void *PointsIntoChunk(void *p);
+// Wrapper for chunk metadata operations.
+class LsanMetadata {
+ public:
+  explicit LsanMetadata(void *chunk);
+  bool allocated() const;
+  ChunkTag tag() const;
+  void set_tag(ChunkTag value);
+  uptr requested_size() const;
+  u32 stack_trace_id() const;
+ private:
+  void *metadata_;
+};
+
+}  // namespace __lsan
+
+#endif  // LSAN_COMMON_H

Added: compiler-rt/trunk/lib/lsan/lsan_common_linux.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/lsan/lsan_common_linux.cc?rev=182249&view=auto
==============================================================================
--- compiler-rt/trunk/lib/lsan/lsan_common_linux.cc (added)
+++ compiler-rt/trunk/lib/lsan/lsan_common_linux.cc Mon May 20 06:06:50 2013
@@ -0,0 +1,122 @@
+//=-- lsan_common_linux.cc ------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of LeakSanitizer.
+// Implementation of common leak checking functionality. Linux-specific code.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_LINUX
+#include "lsan_common.h"
+
+#include <link.h>
+
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_linux.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
+
+namespace __lsan {
+
+static const char kLinkerName[] = "ld";
+// We request 2 modules matching "ld", so we can print a warning if there's more
+// than one match. But only the first one is actually used.
+static char linker_placeholder[2 * sizeof(LoadedModule)] ALIGNED(64);
+static LoadedModule *linker = 0;
+
+static bool IsLinker(const char* full_name) {
+  return LibraryNameIs(full_name, kLinkerName);
+}
+
+void InitializePlatformSpecificModules() {
+  internal_memset(linker_placeholder, 0, sizeof(linker_placeholder));
+  uptr num_matches = GetListOfModules(
+      reinterpret_cast<LoadedModule *>(linker_placeholder), 2, IsLinker);
+  if (num_matches == 1) {
+    linker = reinterpret_cast<LoadedModule *>(linker_placeholder);
+    return;
+  }
+  if (num_matches == 0)
+    Report("%s: Dynamic linker not found. TLS will not be handled correctly.\n",
+           SanitizerToolName);
+  else if (num_matches > 1)
+    Report("%s: Multiple modules match \"%s\". TLS will not be handled "
+           "correctly.\n", SanitizerToolName, kLinkerName);
+  linker = 0;
+}
+
+static int ProcessGlobalRegionsCallback(struct dl_phdr_info *info, size_t size,
+                                        void *data) {
+  InternalVector<uptr> *frontier =
+      reinterpret_cast<InternalVector<uptr> *>(data);
+  for (uptr j = 0; j < info->dlpi_phnum; j++) {
+    const ElfW(Phdr) *phdr = &(info->dlpi_phdr[j]);
+    // We're looking for .data and .bss sections, which reside in writeable,
+    // loadable segments.
+    if (!(phdr->p_flags & PF_W) || (phdr->p_type != PT_LOAD) ||
+        (phdr->p_memsz == 0))
+      continue;
+    uptr begin = info->dlpi_addr + phdr->p_vaddr;
+    uptr end = begin + phdr->p_memsz;
+    uptr allocator_begin = 0, allocator_end = 0;
+    GetAllocatorGlobalRange(&allocator_begin, &allocator_end);
+    if (begin <= allocator_begin && allocator_begin < end) {
+      CHECK_LE(allocator_begin, allocator_end);
+      CHECK_LT(allocator_end, end);
+      if (begin < allocator_begin)
+        ScanRangeForPointers(begin, allocator_begin, frontier, "GLOBAL",
+                             kReachable);
+      if (allocator_end < end)
+        ScanRangeForPointers(allocator_end, end, frontier, "GLOBAL",
+                             kReachable);
+    } else {
+      ScanRangeForPointers(begin, end, frontier, "GLOBAL", kReachable);
+    }
+  }
+  return 0;
+}
+
+// Scan global variables for heap pointers.
+void ProcessGlobalRegions(InternalVector<uptr> *frontier) {
+  // FIXME: dl_iterate_phdr acquires a linker lock, so we run a risk of
+  // deadlocking by running this under StopTheWorld. However, the lock is
+  // reentrant, so we should be able to fix this by acquiring the lock before
+  // suspending threads.
+  dl_iterate_phdr(ProcessGlobalRegionsCallback, frontier);
+}
+
+static uptr GetCallerPC(u32 stack_id) {
+  CHECK(stack_id);
+  uptr size = 0;
+  const uptr *trace = StackDepotGet(stack_id, &size);
+  // The top frame is our malloc/calloc/etc. The next frame is the caller.
+  CHECK_GE(size, 2);
+  return trace[1];
+}
+
+void ProcessPlatformSpecificAllocationsCb::operator()(void *p) const {
+  LsanMetadata m(p);
+  if (m.allocated() && m.tag() != kReachable) {
+    if (linker->containsAddress(GetCallerPC(m.stack_trace_id()))) {
+      m.set_tag(kReachable);
+      frontier_->push_back(reinterpret_cast<uptr>(p));
+    }
+  }
+}
+
+// Handle dynamically allocated TLS blocks by treating all chunks allocated from
+// ld-linux.so as reachable.
+void ProcessPlatformSpecificAllocations(InternalVector<uptr> *frontier) {
+  if (!flags()->use_tls()) return;
+  if (!linker) return;
+  ForEachChunk(ProcessPlatformSpecificAllocationsCb(frontier));
+}
+
+}  // namespace __lsan
+#endif  // SANITIZER_LINUX





More information about the llvm-commits mailing list