Expose thread local quarantine size as ASAN option.
authorEvgeniy Stepanov <eugeni.stepanov@gmail.com>
Thu, 22 Dec 2016 21:43:22 +0000 (21:43 +0000)
committerEvgeniy Stepanov <eugeni.stepanov@gmail.com>
Thu, 22 Dec 2016 21:43:22 +0000 (21:43 +0000)
Summary: Make thread local quarantine size an option so it can be turned off to save memory.

Reviewers: eugenis

Patch by Alex Shlyapnikov.

Subscribers: kubabrecka, llvm-commits

Differential Revision: https://reviews.llvm.org/D28027

llvm-svn: 290373

compiler-rt/lib/asan/asan_allocator.cc
compiler-rt/lib/asan/asan_allocator.h
compiler-rt/lib/asan/asan_flags.cc
compiler-rt/lib/asan/asan_flags.inc
compiler-rt/lib/asan/asan_rtl.cc
compiler-rt/lib/sanitizer_common/sanitizer_quarantine.h
compiler-rt/test/asan/TestCases/Linux/thread_local_quarantine_size_kb.cc [new file with mode: 0644]

index 1055a8d..36bd046 100644 (file)
@@ -207,6 +207,7 @@ QuarantineCache *GetQuarantineCache(AsanThreadLocalMallocStorage *ms) {
 
 void AllocatorOptions::SetFrom(const Flags *f, const CommonFlags *cf) {
   quarantine_size_mb = f->quarantine_size_mb;
+  thread_local_quarantine_size_kb = f->thread_local_quarantine_size_kb;
   min_redzone = f->redzone;
   max_redzone = f->max_redzone;
   may_return_null = cf->allocator_may_return_null;
@@ -216,6 +217,7 @@ void AllocatorOptions::SetFrom(const Flags *f, const CommonFlags *cf) {
 
 void AllocatorOptions::CopyTo(Flags *f, CommonFlags *cf) {
   f->quarantine_size_mb = quarantine_size_mb;
+  f->thread_local_quarantine_size_kb = thread_local_quarantine_size_kb;
   f->redzone = min_redzone;
   f->max_redzone = max_redzone;
   cf->allocator_may_return_null = may_return_null;
@@ -226,13 +228,6 @@ void AllocatorOptions::CopyTo(Flags *f, CommonFlags *cf) {
 struct Allocator {
   static const uptr kMaxAllowedMallocSize =
       FIRST_32_SECOND_64(3UL << 30, 1ULL << 40);
-  static const uptr kMaxThreadLocalQuarantine =
-      // It is not advised to go lower than 64Kb, otherwise quarantine batches
-      // pushed from thread local quarantine to global one will create too much
-      // overhead. One quarantine batch size is 8Kb and it  holds up to 1021
-      // chunk, which amounts to 1/8 memory overhead per batch when thread local
-      // quarantine is set to 64Kb.
-      (ASAN_LOW_MEMORY) ? 1 << 16 : FIRST_32_SECOND_64(1 << 18, 1 << 20);
 
   AsanAllocator allocator;
   AsanQuarantine quarantine;
@@ -261,7 +256,7 @@ struct Allocator {
   void SharedInitCode(const AllocatorOptions &options) {
     CheckOptions(options);
     quarantine.Init((uptr)options.quarantine_size_mb << 20,
-                    kMaxThreadLocalQuarantine);
+                    (uptr)options.thread_local_quarantine_size_kb << 10);
     atomic_store(&alloc_dealloc_mismatch, options.alloc_dealloc_mismatch,
                  memory_order_release);
     atomic_store(&min_redzone, options.min_redzone, memory_order_release);
@@ -315,6 +310,7 @@ struct Allocator {
 
   void GetOptions(AllocatorOptions *options) const {
     options->quarantine_size_mb = quarantine.GetSize() >> 20;
+    options->thread_local_quarantine_size_kb = quarantine.GetCacheSize() >> 10;
     options->min_redzone = atomic_load(&min_redzone, memory_order_acquire);
     options->max_redzone = atomic_load(&max_redzone, memory_order_acquire);
     options->may_return_null = allocator.MayReturnNull();
index 62415f3..51de678 100644 (file)
@@ -33,6 +33,7 @@ struct AsanChunk;
 
 struct AllocatorOptions {
   u32 quarantine_size_mb;
+  u32 thread_local_quarantine_size_kb;
   u16 min_redzone;
   u16 max_redzone;
   u8 may_return_null;
index 369ef0a..4db407d 100644 (file)
@@ -159,6 +159,16 @@ void InitializeFlags() {
         (ASAN_LOW_MEMORY) ? 1UL << 4 : 1UL << 8;
     f->quarantine_size_mb = kDefaultQuarantineSizeMb;
   }
+  if (f->thread_local_quarantine_size_kb < 0) {
+    const u32 kDefaultThreadLocalQuarantineSizeKb =
+        // It is not advised to go lower than 64Kb, otherwise quarantine batches
+        // pushed from thread local quarantine to global one will create too
+        // much overhead. One quarantine batch size is 8Kb and it  holds up to
+        // 1021 chunk, which amounts to 1/8 memory overhead per batch when
+        // thread local quarantine is set to 64Kb.
+        (ASAN_LOW_MEMORY) ? 1 << 6 : FIRST_32_SECOND_64(1 << 8, 1 << 10);
+    f->thread_local_quarantine_size_kb = kDefaultThreadLocalQuarantineSizeKb;
+  }
   if (!f->replace_str && common_flags()->intercept_strlen) {
     Report("WARNING: strlen interceptor is enabled even though replace_str=0. "
            "Use intercept_strlen=0 to disable it.");
index 5272477..4712efb 100644 (file)
@@ -23,6 +23,12 @@ ASAN_FLAG(int, quarantine_size_mb, -1,
           "Size (in Mb) of quarantine used to detect use-after-free "
           "errors. Lower value may reduce memory usage but increase the "
           "chance of false negatives.")
+ASAN_FLAG(int, thread_local_quarantine_size_kb, -1,
+          "Size (in Kb) of thread local quarantine used to detect "
+          "use-after-free errors. Lower value may reduce memory usage but "
+          "increase the chance of false negatives. It is not advised to go "
+          "lower than 64Kb, otherwise frequent transfers to global quarantine "
+          "might affect performance.")
 ASAN_FLAG(int, redzone, 16,
           "Minimal size (in bytes) of redzones around heap objects. "
           "Requirement: redzone >= 16, is a power of two.")
index 5c191fe..fee7b8a 100644 (file)
@@ -410,6 +410,8 @@ static void PrintAddressSpaceLayout() {
   Printf("redzone=%zu\n", (uptr)flags()->redzone);
   Printf("max_redzone=%zu\n", (uptr)flags()->max_redzone);
   Printf("quarantine_size_mb=%zuM\n", (uptr)flags()->quarantine_size_mb);
+  Printf("thread_local_quarantine_size_kb=%zuK\n",
+         (uptr)flags()->thread_local_quarantine_size_kb);
   Printf("malloc_context_size=%zu\n",
          (uptr)common_flags()->malloc_context_size);
 
index ccc22bf..ff8f3fa 100644 (file)
@@ -56,6 +56,7 @@ class Quarantine {
   }
 
   uptr GetSize() const { return atomic_load(&max_size_, memory_order_acquire); }
+  uptr GetCacheSize() const { return max_cache_size_; }
 
   void Put(Cache *c, Callback cb, Node *ptr, uptr size) {
     c->Enqueue(cb, ptr, size);
diff --git a/compiler-rt/test/asan/TestCases/Linux/thread_local_quarantine_size_kb.cc b/compiler-rt/test/asan/TestCases/Linux/thread_local_quarantine_size_kb.cc
new file mode 100644 (file)
index 0000000..7176484
--- /dev/null
@@ -0,0 +1,40 @@
+// Test thread_local_quarantine_size_kb
+
+// RUN: %clangxx_asan  %s -o %t
+// RUN: %env_asan_opts=thread_local_quarantine_size_kb=256:verbosity=1 %run %t 2>&1 | \
+// RUN:   FileCheck %s --check-prefix=CHECK-VALUE
+// RUN: %env_asan_opts=thread_local_quarantine_size_kb=64:quarantine_size_mb=64 %run %t 2>&1 | \
+// RUN:   FileCheck %s --allow-empty --check-prefix=CHECK-SMALL-LOCAL-CACHE-SMALL-OVERHEAD
+// RUN: %env_asan_opts=thread_local_quarantine_size_kb=0:quarantine_size_mb=64 %run %t 2>&1 | \
+// RUN:   FileCheck %s --check-prefix=CHECK-NO-LOCAL-CACHE-HUGE-OVERHEAD
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sanitizer/allocator_interface.h>
+
+// The idea is allocate a lot of small blocks, totaling 5Mb of user memory
+// total, and verify that quarantine does not incur too much memory overhead.
+// There's always an overhead for red zones, shadow memory and such, but
+// quarantine accounting should not significantly contribute to that.
+static const int kNumAllocs = 20000;
+static const int kAllocSize = 256;
+static const size_t kHeapSizeLimit = 12 << 20;
+
+int main() {
+  size_t old_heap_size = __sanitizer_get_heap_size();
+  for (int i = 0; i < kNumAllocs; i++) {
+    char *g = new char[kAllocSize];
+    memset(g, -1, kAllocSize);
+    delete [] (g);
+  }
+  size_t new_heap_size = __sanitizer_get_heap_size();
+  fprintf(stderr, "heap size: new: %zd old: %zd\n", new_heap_size,
+          old_heap_size);
+  if (new_heap_size - old_heap_size > kHeapSizeLimit)
+    fprintf(stderr, "Heap size limit exceeded");
+}
+
+// CHECK-VALUE: thread_local_quarantine_size_kb=256K
+// CHECK-SMALL-LOCAL-CACHE-SMALL-OVERHEAD-NOT: Heap size limit exceeded
+// CHECK-NO-LOCAL-CACHE-HUGE-OVERHEAD: Heap size limit exceeded