diff options
author | Sergey Matveev <earthdok@google.com> | 2013-12-17 11:11:23 +0000 |
---|---|---|
committer | Sergey Matveev <earthdok@google.com> | 2013-12-17 11:11:23 +0000 |
commit | 7237879926485847aff30093e8efb3f0d3bcfa2a (patch) | |
tree | 83ed77233a5f3c42ade441c0fb73e3a6d946fc8f /compiler-rt/lib/lsan | |
parent | 5069790b2401121ba288e643615658c90b28eae3 (diff) | |
download | bcm5719-llvm-7237879926485847aff30093e8efb3f0d3bcfa2a.tar.gz bcm5719-llvm-7237879926485847aff30093e8efb3f0d3bcfa2a.zip |
[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.
llvm-svn: 197489
Diffstat (limited to 'compiler-rt/lib/lsan')
-rw-r--r-- | compiler-rt/lib/lsan/lit_tests/TestCases/register_root_region.cc | 32 | ||||
-rw-r--r-- | compiler-rt/lib/lsan/lsan_common.cc | 97 | ||||
-rw-r--r-- | compiler-rt/lib/lsan/lsan_common.h | 2 |
3 files changed, 128 insertions, 3 deletions
diff --git a/compiler-rt/lib/lsan/lit_tests/TestCases/register_root_region.cc b/compiler-rt/lib/lsan/lit_tests/TestCases/register_root_region.cc new file mode 100644 index 00000000000..27349aaef4b --- /dev/null +++ b/compiler-rt/lib/lsan/lit_tests/TestCases/register_root_region.cc @@ -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) diff --git a/compiler-rt/lib/lsan/lsan_common.cc b/compiler-rt/lib/lsan/lsan_common.cc index 8ad96f23e23..0ff492fc930 100644 --- a/compiler-rt/lib/lsan/lsan_common.cc +++ b/compiler-rt/lib/lsan/lsan_common.cc @@ -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(SuspendedThreadsList const &suspended_threads, } } +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(SuspendedThreadsList const &suspended_threads) { 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 @@ -571,6 +622,46 @@ void __lsan_ignore_object(const void *p) { } 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 __lsan::disable_counter++; diff --git a/compiler-rt/lib/lsan/lsan_common.h b/compiler-rt/lib/lsan/lsan_common.h index 714f8b65010..452824e12f1 100644 --- a/compiler-rt/lib/lsan/lsan_common.h +++ b/compiler-rt/lib/lsan/lsan_common.h @@ -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; |