[compiler-rt] r281364 - [asan] Re-poison all redzones on activation.

Evgeniy Stepanov via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 13 11:38:40 PDT 2016


Author: eugenis
Date: Tue Sep 13 13:38:40 2016
New Revision: 281364

URL: http://llvm.org/viewvc/llvm-project?rev=281364&view=rev
Log:
[asan] Re-poison all redzones on activation.

When running with start_deactivated=1 in ASAN_OPTIONS, heap redzones
are not poisoned until the first instrumented module is loaded. This
can cause false negatives even on memory allocated after activation,
because redzones are normally poisoned only once when a new allocator
region is mapped.

This change attempts to fix it by iterating over all existing
allocator chunks and poisoning their redzones.

Modified:
    compiler-rt/trunk/lib/asan/asan_allocator.cc
    compiler-rt/trunk/test/asan/TestCases/Posix/start-deactivated.cc

Modified: compiler-rt/trunk/lib/asan/asan_allocator.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/asan/asan_allocator.cc?rev=281364&r1=281363&r2=281364&view=diff
==============================================================================
--- compiler-rt/trunk/lib/asan/asan_allocator.cc (original)
+++ compiler-rt/trunk/lib/asan/asan_allocator.cc Tue Sep 13 13:38:40 2016
@@ -266,9 +266,43 @@ struct Allocator {
     SharedInitCode(options);
   }
 
+  void RePoisonChunk(uptr chunk) {
+    // This could a user-facing chunk (with redzones), or some internal
+    // housekeeping chunk, like TransferBatch. Start by assuming the former.
+    AsanChunk *ac = GetAsanChunk((void *)chunk);
+    uptr allocated_size = allocator.GetActuallyAllocatedSize((void *)ac);
+    uptr beg = ac->Beg();
+    uptr end = ac->Beg() + ac->UsedSize(true);
+    uptr chunk_end = chunk + allocated_size;
+    if (chunk < beg && beg < end && end <= chunk_end) {
+      // Looks like a valid AsanChunk. Or maybe not. Be conservative and only
+      // poison the redzones.
+      PoisonShadow(chunk, beg - chunk, kAsanHeapLeftRedzoneMagic);
+      uptr end_aligned_down = RoundDownTo(end, SHADOW_GRANULARITY);
+      FastPoisonShadowPartialRightRedzone(
+          end_aligned_down, end - end_aligned_down,
+          chunk_end - end_aligned_down, kAsanHeapLeftRedzoneMagic);
+    } else {
+      // This can not be an AsanChunk. Poison everything. It may be reused as
+      // AsanChunk later.
+      PoisonShadow(chunk, allocated_size, kAsanHeapLeftRedzoneMagic);
+    }
+  }
+
   void ReInitialize(const AllocatorOptions &options) {
     allocator.SetMayReturnNull(options.may_return_null);
     SharedInitCode(options);
+
+    // Poison all existing allocation's redzones.
+    if (CanPoisonMemory()) {
+      allocator.ForceLock();
+      allocator.ForEachChunk(
+          [](uptr chunk, void *alloc) {
+            ((Allocator *)alloc)->RePoisonChunk(chunk);
+          },
+          this);
+      allocator.ForceUnlock();
+    }
   }
 
   void GetOptions(AllocatorOptions *options) const {

Modified: compiler-rt/trunk/test/asan/TestCases/Posix/start-deactivated.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/test/asan/TestCases/Posix/start-deactivated.cc?rev=281364&r1=281363&r2=281364&view=diff
==============================================================================
--- compiler-rt/trunk/test/asan/TestCases/Posix/start-deactivated.cc (original)
+++ compiler-rt/trunk/test/asan/TestCases/Posix/start-deactivated.cc Tue Sep 13 13:38:40 2016
@@ -2,8 +2,8 @@
 // Main executable is uninstrumented, but linked to ASan runtime. The shared
 // library is instrumented. Memory errors before dlopen are not detected.
 
-// RUN: %clangxx_asan -O0 -DSHARED_LIB %s -fPIC -shared -o %t-so.so
-// RUN: %clangxx -O0 %s -c -o %t.o
+// RUN: %clangxx_asan -O0 -DSHARED_LIB %s -std=c++11 -fPIC -shared -o %t-so.so
+// RUN: %clangxx -O0 %s -std=c++11 -c -o %t.o
 // RUN: %clangxx_asan -O0 %t.o %libdl -o %t
 // RUN: %env_asan_opts=start_deactivated=1,allocator_may_return_null=0 \
 // RUN:   ASAN_ACTIVATION_OPTIONS=allocator_may_return_null=1 not %run %t 2>&1 | FileCheck %s
@@ -32,18 +32,25 @@
 
 #include "sanitizer/asan_interface.h"
 
-void test_malloc_shadow() {
-  char *p = (char *)malloc(100);
-  char *q = (char *)__asan_region_is_poisoned(p + 95, 8);
-  fprintf(stderr, "=%zd=\n", q ? q - (p + 95) : -1);
-  free(p);
+constexpr unsigned nPtrs = 200;
+char *ptrs[nPtrs];
+
+void test_malloc_shadow(char *p, size_t sz, bool expect_redzones) {
+  assert((char *)__asan_region_is_poisoned(p - 1, sz + 1) ==
+         (expect_redzones ? p - 1 : nullptr));
+  assert((char *)__asan_region_is_poisoned(p, sz) == nullptr);
+  assert((char *)__asan_region_is_poisoned(p, sz + 1) ==
+         (expect_redzones ? p + sz : nullptr));
 }
 
 typedef void (*Fn)();
 
 int main(int argc, char *argv[]) {
-  test_malloc_shadow();
-  // CHECK: =-1=
+  // Before activation: no redzones.
+  for (size_t sz = 1; sz < nPtrs; ++sz) {
+    ptrs[sz] = (char *)malloc(sz);
+    test_malloc_shadow(ptrs[sz], sz, false);
+  }
 
   std::string path = std::string(argv[0]) + "-so.so";
   void *dso = dlopen(path.c_str(), RTLD_NOW);
@@ -52,9 +59,6 @@ int main(int argc, char *argv[]) {
     return 1;
   }
 
-  test_malloc_shadow();
-  // CHECK: =5=
-
   // After this line ASan is activated and starts detecting errors.
   void *fn = dlsym(dso, "do_another_bad_thing");
   if (!fn) {
@@ -62,6 +66,19 @@ int main(int argc, char *argv[]) {
     return 1;
   }
 
+  // After activation: redzones.
+  {
+    char *p = (char *)malloc(100);
+    test_malloc_shadow(p, 100, true);
+    free(p);
+  }
+
+  // Pre-existing allocations got redzones, too.
+  for (size_t sz = 1; sz < nPtrs; ++sz) {
+    test_malloc_shadow(ptrs[sz], sz, true);
+    free(ptrs[sz]);
+  }
+
   // Test that ASAN_ACTIVATION_OPTIONS=allocator_may_return_null=1 has effect.
   void *p = malloc((unsigned long)-2);
   assert(!p);




More information about the llvm-commits mailing list