diff options
author | Sergey Matveev <earthdok@google.com> | 2013-06-06 14:17:56 +0000 |
---|---|---|
committer | Sergey Matveev <earthdok@google.com> | 2013-06-06 14:17:56 +0000 |
commit | cd571e07fd1179383188c70338fa0dc1c452cb19 (patch) | |
tree | b83b2c886c931d18b6542013180cc4b0170af411 /lib | |
parent | 722f2e6a6125a0d5c9d453278b0f292e3410124d (diff) |
[lsan] Implement __lsan_ignore_object().
Leak annotation similar to HeapChecker's IgnoreObject().
git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@183412 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib')
-rw-r--r-- | lib/asan/asan_allocator2.cc | 14 | ||||
-rw-r--r-- | lib/lsan/lit_tests/TestCases/ignore_object.cc | 30 | ||||
-rw-r--r-- | lib/lsan/lit_tests/TestCases/ignore_object_errors.cc | 22 | ||||
-rw-r--r-- | lib/lsan/lsan_allocator.cc | 15 | ||||
-rw-r--r-- | lib/lsan/lsan_common.cc | 28 | ||||
-rw-r--r-- | lib/lsan/lsan_common.h | 11 | ||||
-rw-r--r-- | lib/sanitizer_common/sanitizer_allocator.h | 18 |
7 files changed, 127 insertions, 11 deletions
diff --git a/lib/asan/asan_allocator2.cc b/lib/asan/asan_allocator2.cc index ea4181d17..c1f012492 100644 --- a/lib/asan/asan_allocator2.cc +++ b/lib/asan/asan_allocator2.cc @@ -792,6 +792,20 @@ template void ForEachChunk<MarkIndirectlyLeakedCb>( 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" { diff --git a/lib/lsan/lit_tests/TestCases/ignore_object.cc b/lib/lsan/lit_tests/TestCases/ignore_object.cc new file mode 100644 index 000000000..b01619853 --- /dev/null +++ b/lib/lsan/lit_tests/TestCases/ignore_object.cc @@ -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) diff --git a/lib/lsan/lit_tests/TestCases/ignore_object_errors.cc b/lib/lsan/lit_tests/TestCases/ignore_object_errors.cc new file mode 100644 index 000000000..4b0e5d928 --- /dev/null +++ b/lib/lsan/lit_tests/TestCases/ignore_object_errors.cc @@ -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]] diff --git a/lib/lsan/lsan_allocator.cc b/lib/lsan/lsan_allocator.cc index eca739943..cbfaa1572 100644 --- a/lib/lsan/lsan_allocator.cc +++ b/lib/lsan/lsan_allocator.cc @@ -190,6 +190,21 @@ template void ForEachChunk<MarkIndirectlyLeakedCb>( 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" { diff --git a/lib/lsan/lsan_common.cc b/lib/lsan/lsan_common.cc index ae1fca740..a42469b03 100644 --- a/lib/lsan/lsan_common.cc +++ b/lib/lsan/lsan_common.cc @@ -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 SuspendedThreadsList &suspended_threads, } 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 diff --git a/lib/lsan/lsan_common.h b/lib/lsan/lsan_common.h index 18660ba68..4cd6082be 100644 --- a/lib/lsan/lsan_common.h +++ b/lib/lsan/lsan_common.h @@ -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, uptr *stack_begin, uptr *stack_end, 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: diff --git a/lib/sanitizer_common/sanitizer_allocator.h b/lib/sanitizer_common/sanitizer_allocator.h index d24f42b3c..6d61392f9 100644 --- a/lib/sanitizer_common/sanitizer_allocator.h +++ b/lib/sanitizer_common/sanitizer_allocator.h @@ -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); |