[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, ¶m);
+ 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