summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorSergey Matveev <earthdok@google.com>2013-12-17 11:11:23 +0000
committerSergey Matveev <earthdok@google.com>2013-12-17 11:11:23 +0000
commitbb6f93683ef9e9d87b2ff912af651e0ea0e3abd2 (patch)
tree02722c778f84a33793e63b4ec5be7cee450e836f /lib
parenta30585a9344c6b9f21843bd48421c61e4765ba01 (diff)
[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. git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@197489 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib')
-rw-r--r--lib/lsan/lit_tests/TestCases/register_root_region.cc32
-rw-r--r--lib/lsan/lsan_common.cc97
-rw-r--r--lib/lsan/lsan_common.h2
3 files changed, 128 insertions, 3 deletions
diff --git a/lib/lsan/lit_tests/TestCases/register_root_region.cc b/lib/lsan/lit_tests/TestCases/register_root_region.cc
new file mode 100644
index 000000000..27349aaef
--- /dev/null
+++ b/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/lib/lsan/lsan_common.cc b/lib/lsan/lsan_common.cc
index 8ad96f23e..0ff492fc9 100644
--- a/lib/lsan/lsan_common.cc
+++ b/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/lib/lsan/lsan_common.h b/lib/lsan/lsan_common.h
index 714f8b650..452824e12 100644
--- a/lib/lsan/lsan_common.h
+++ b/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;