]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
libsanitizer mege from upstream r171973
authorKostya Serebryany <kcc@google.com>
Thu, 10 Jan 2013 12:44:08 +0000 (12:44 +0000)
committerKostya Serebryany <kcc@gcc.gnu.org>
Thu, 10 Jan 2013 12:44:08 +0000 (12:44 +0000)
From-SVN: r195083

91 files changed:
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/asan/asan_test.cc
libsanitizer/ChangeLog
libsanitizer/MERGE
libsanitizer/asan/Makefile.am
libsanitizer/asan/Makefile.in
libsanitizer/asan/asan_allocator.cc
libsanitizer/asan/asan_allocator.h
libsanitizer/asan/asan_allocator2.cc [new file with mode: 0644]
libsanitizer/asan/asan_fake_stack.cc [new file with mode: 0644]
libsanitizer/asan/asan_flags.h
libsanitizer/asan/asan_intercepted_functions.h
libsanitizer/asan/asan_interceptors.cc
libsanitizer/asan/asan_internal.h
libsanitizer/asan/asan_linux.cc
libsanitizer/asan/asan_mac.cc
libsanitizer/asan/asan_malloc_linux.cc
libsanitizer/asan/asan_malloc_mac.cc
libsanitizer/asan/asan_malloc_win.cc
libsanitizer/asan/asan_mapping.h
libsanitizer/asan/asan_new_delete.cc
libsanitizer/asan/asan_poisoning.cc
libsanitizer/asan/asan_report.cc
libsanitizer/asan/asan_report.h
libsanitizer/asan/asan_rtl.cc
libsanitizer/asan/asan_stack.cc
libsanitizer/asan/asan_stack.h
libsanitizer/asan/asan_stats.cc
libsanitizer/asan/asan_stats.h
libsanitizer/asan/asan_thread.h
libsanitizer/asan/asan_thread_registry.cc
libsanitizer/asan/asan_win.cc
libsanitizer/include/sanitizer/asan_interface.h
libsanitizer/include/sanitizer/common_interface_defs.h
libsanitizer/interception/interception.h
libsanitizer/sanitizer_common/Makefile.am
libsanitizer/sanitizer_common/Makefile.in
libsanitizer/sanitizer_common/sanitizer_allocator.h
libsanitizer/sanitizer_common/sanitizer_atomic_msvc.h
libsanitizer/sanitizer_common/sanitizer_common.cc
libsanitizer/sanitizer_common/sanitizer_common.h
libsanitizer/sanitizer_common/sanitizer_common_interceptors.h [new file with mode: 0644]
libsanitizer/sanitizer_common/sanitizer_libc.cc
libsanitizer/sanitizer_common/sanitizer_libc.h
libsanitizer/sanitizer_common/sanitizer_linux.cc
libsanitizer/sanitizer_common/sanitizer_mac.cc
libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h [new file with mode: 0644]
libsanitizer/sanitizer_common/sanitizer_posix.cc
libsanitizer/sanitizer_common/sanitizer_printf.cc
libsanitizer/sanitizer_common/sanitizer_report_decorator.h [new file with mode: 0644]
libsanitizer/sanitizer_common/sanitizer_stackdepot.cc
libsanitizer/sanitizer_common/sanitizer_stackdepot.h
libsanitizer/sanitizer_common/sanitizer_stacktrace.cc
libsanitizer/sanitizer_common/sanitizer_stacktrace.h
libsanitizer/sanitizer_common/sanitizer_symbolizer.h
libsanitizer/sanitizer_common/sanitizer_symbolizer_itanium.cc [new file with mode: 0644]
libsanitizer/sanitizer_common/sanitizer_symbolizer_win.cc
libsanitizer/sanitizer_common/sanitizer_win.cc
libsanitizer/tsan/Makefile.am
libsanitizer/tsan/Makefile.in
libsanitizer/tsan/tsan_defs.h
libsanitizer/tsan/tsan_fd.cc [new file with mode: 0644]
libsanitizer/tsan/tsan_fd.h [new file with mode: 0644]
libsanitizer/tsan/tsan_flags.cc
libsanitizer/tsan/tsan_flags.h
libsanitizer/tsan/tsan_interceptors.cc
libsanitizer/tsan/tsan_interface_atomic.cc
libsanitizer/tsan/tsan_interface_atomic.h
libsanitizer/tsan/tsan_interface_java.cc [new file with mode: 0644]
libsanitizer/tsan/tsan_interface_java.h [new file with mode: 0644]
libsanitizer/tsan/tsan_mman.cc
libsanitizer/tsan/tsan_mman.h
libsanitizer/tsan/tsan_mutex.cc
libsanitizer/tsan/tsan_mutex.h
libsanitizer/tsan/tsan_mutexset.cc [new file with mode: 0644]
libsanitizer/tsan/tsan_mutexset.h [new file with mode: 0644]
libsanitizer/tsan/tsan_platform.h
libsanitizer/tsan/tsan_platform_linux.cc
libsanitizer/tsan/tsan_report.cc
libsanitizer/tsan/tsan_report.h
libsanitizer/tsan/tsan_rtl.cc
libsanitizer/tsan/tsan_rtl.h
libsanitizer/tsan/tsan_rtl_mutex.cc
libsanitizer/tsan/tsan_rtl_report.cc
libsanitizer/tsan/tsan_rtl_thread.cc
libsanitizer/tsan/tsan_stat.cc
libsanitizer/tsan/tsan_stat.h
libsanitizer/tsan/tsan_symbolize_addr2line_linux.cc
libsanitizer/tsan/tsan_sync.cc
libsanitizer/tsan/tsan_sync.h
libsanitizer/tsan/tsan_trace.h

index 0df71a57a3f1231ea839e8d789a5b40628499f64..3943c2fed041974d6df919c3e708326d21e6b076 100644 (file)
@@ -1,3 +1,7 @@
+2013-01-10  Kostya Serebryany  <kcc@google.com>
+
+       * g++.dg/asan/asan_test.cc: Sync from upstream.
+
 2013-01-10  Jakub Jelinek  <jakub@redhat.com>
 
        PR tree-optimization/55921
index 32c2ad2ab2ad6ceff1398f5a4b409e14602a9762..9cfa9e04ef812f24791eba87adaa6fe584c173dc 100644 (file)
 #include <stdint.h>
 #include <setjmp.h>
 #include <assert.h>
+#include <algorithm>
+
+#ifdef __linux__
+# include <sys/prctl.h>
+# include <sys/types.h>
+# include <sys/stat.h>
+# include <fcntl.h>
+#include <unistd.h>
+#endif
 
 #if defined(__i386__) || defined(__x86_64__)
 #include <emmintrin.h>
@@ -242,7 +251,7 @@ void OOBTest() {
     for (int i = 0; i < (int)(size - sizeof(T) + 1); i++)
       oob_test<T>(size, i);
 
-    for (int i = size - sizeof(T) + 1; i <= (int)(size + 3 * sizeof(T)); i++) {
+    for (int i = size - sizeof(T) + 1; i <= (int)(size + 2 * sizeof(T)); i++) {
       const char *str =
           "is located.*%d byte.*to the right";
       int off = i >= size ? (i - size) : 0;
@@ -298,6 +307,18 @@ TEST(AddressSanitizer, OOBRightTest) {
   }
 }
 
+#if ASAN_ALLOCATOR_VERSION == 2  // Broken with the asan_allocator1
+TEST(AddressSanitizer, LargeOOBRightTest) {
+  size_t large_power_of_two = 1 << 19;
+  for (size_t i = 16; i <= 256; i *= 2) {
+    size_t size = large_power_of_two - i;
+    char *p = Ident(new char[size]);
+    EXPECT_DEATH(p[size] = 0, "is located 0 bytes to the right");
+    delete [] p;
+  }
+}
+#endif  // ASAN_ALLOCATOR_VERSION == 2
+
 TEST(AddressSanitizer, UAF_char) {
   const char *uaf_string = "AddressSanitizer:.*heap-use-after-free";
   EXPECT_DEATH(uaf_test<U1>(1, 0), uaf_string);
@@ -456,6 +477,24 @@ TEST(AddressSanitizer, HugeMallocTest) {
 }
 #endif
 
+#ifndef __APPLE__
+void MemalignRun(size_t align, size_t size, int idx) {
+  char *p = (char *)memalign(align, size);
+  Ident(p)[idx] = 0;
+  free(p);
+}
+
+TEST(AddressSanitizer, memalign) {
+  for (int align = 16; align <= (1 << 23); align *= 2) {
+    size_t size = align * 5;
+    EXPECT_DEATH(MemalignRun(align, size, -1),
+                 "is located 1 bytes to the left");
+    EXPECT_DEATH(MemalignRun(align, size, size + 1),
+                 "is located 1 bytes to the right");
+  }
+}
+#endif
+
 TEST(AddressSanitizer, ThreadedMallocStressTest) {
   const int kNumThreads = 4;
   const int kNumIterations = (ASAN_LOW_MEMORY) ? 10000 : 100000;
@@ -784,14 +823,39 @@ TEST(AddressSanitizer, Store128Test) {
 }
 #endif
 
-static string RightOOBErrorMessage(int oob_distance) {
+static string RightOOBErrorMessage(int oob_distance, bool is_write) {
   assert(oob_distance >= 0);
   char expected_str[100];
-  sprintf(expected_str, "located %d bytes to the right", oob_distance);
+  sprintf(expected_str, ASAN_PCRE_DOTALL "%s.*located %d bytes to the right",
+          is_write ? "WRITE" : "READ", oob_distance);
   return string(expected_str);
 }
 
-static string LeftOOBErrorMessage(int oob_distance) {
+static string RightOOBWriteMessage(int oob_distance) {
+  return RightOOBErrorMessage(oob_distance, /*is_write*/true);
+}
+
+static string RightOOBReadMessage(int oob_distance) {
+  return RightOOBErrorMessage(oob_distance, /*is_write*/false);
+}
+
+static string LeftOOBErrorMessage(int oob_distance, bool is_write) {
+  assert(oob_distance > 0);
+  char expected_str[100];
+  sprintf(expected_str, ASAN_PCRE_DOTALL "%s.*located %d bytes to the left",
+          is_write ? "WRITE" : "READ", oob_distance);
+  return string(expected_str);
+}
+
+static string LeftOOBWriteMessage(int oob_distance) {
+  return LeftOOBErrorMessage(oob_distance, /*is_write*/true);
+}
+
+static string LeftOOBReadMessage(int oob_distance) {
+  return LeftOOBErrorMessage(oob_distance, /*is_write*/false);
+}
+
+static string LeftOOBAccessMessage(int oob_distance) {
   assert(oob_distance > 0);
   char expected_str[100];
   sprintf(expected_str, "located %d bytes to the left", oob_distance);
@@ -805,44 +869,48 @@ void MemSetOOBTestTemplate(size_t length) {
   T *array = Ident((T*)malloc(size));
   int element = Ident(42);
   int zero = Ident(0);
+  void *(*MEMSET)(void *s, int c, size_t n) = Ident(memset);
   // memset interval inside array
-  memset(array, element, size);
-  memset(array, element, size - 1);
-  memset(array + length - 1, element, sizeof(T));
-  memset(array, element, 1);
+  MEMSET(array, element, size);
+  MEMSET(array, element, size - 1);
+  MEMSET(array + length - 1, element, sizeof(T));
+  MEMSET(array, element, 1);
 
   // memset 0 bytes
-  memset(array - 10, element, zero);
-  memset(array - 1, element, zero);
-  memset(array, element, zero);
-  memset(array + length, 0, zero);
-  memset(array + length + 1, 0, zero);
+  MEMSET(array - 10, element, zero);
+  MEMSET(array - 1, element, zero);
+  MEMSET(array, element, zero);
+  MEMSET(array + length, 0, zero);
+  MEMSET(array + length + 1, 0, zero);
 
   // try to memset bytes to the right of array
-  EXPECT_DEATH(memset(array, 0, size + 1),
-               RightOOBErrorMessage(0));
-  EXPECT_DEATH(memset((char*)(array + length) - 1, element, 6),
-               RightOOBErrorMessage(4));
-  EXPECT_DEATH(memset(array + 1, element, size + sizeof(T)),
-               RightOOBErrorMessage(2 * sizeof(T) - 1));
+  EXPECT_DEATH(MEMSET(array, 0, size + 1),
+               RightOOBWriteMessage(0));
+  EXPECT_DEATH(MEMSET((char*)(array + length) - 1, element, 6),
+               RightOOBWriteMessage(0));
+  EXPECT_DEATH(MEMSET(array + 1, element, size + sizeof(T)),
+               RightOOBWriteMessage(0));
   // whole interval is to the right
-  EXPECT_DEATH(memset(array + length + 1, 0, 10),
-               RightOOBErrorMessage(sizeof(T)));
+  EXPECT_DEATH(MEMSET(array + length + 1, 0, 10),
+               RightOOBWriteMessage(sizeof(T)));
 
   // try to memset bytes to the left of array
-  EXPECT_DEATH(memset((char*)array - 1, element, size),
-               LeftOOBErrorMessage(1));
-  EXPECT_DEATH(memset((char*)array - 5, 0, 6),
-               LeftOOBErrorMessage(5));
-  EXPECT_DEATH(memset(array - 5, element, size + 5 * sizeof(T)),
-               LeftOOBErrorMessage(5 * sizeof(T)));
+  EXPECT_DEATH(MEMSET((char*)array - 1, element, size),
+               LeftOOBWriteMessage(1));
+  EXPECT_DEATH(MEMSET((char*)array - 5, 0, 6),
+               LeftOOBWriteMessage(5));
+  if (length >= 100) {
+    // Large OOB, we find it only if the redzone is large enough.
+    EXPECT_DEATH(memset(array - 5, element, size + 5 * sizeof(T)),
+                 LeftOOBWriteMessage(5 * sizeof(T)));
+  }
   // whole interval is to the left
-  EXPECT_DEATH(memset(array - 2, 0, sizeof(T)),
-               LeftOOBErrorMessage(2 * sizeof(T)));
+  EXPECT_DEATH(MEMSET(array - 2, 0, sizeof(T)),
+               LeftOOBWriteMessage(2 * sizeof(T)));
 
   // try to memset bytes both to the left & to the right
-  EXPECT_DEATH(memset((char*)array - 2, element, size + 4),
-               LeftOOBErrorMessage(2));
+  EXPECT_DEATH(MEMSET((char*)array - 2, element, size + 4),
+               LeftOOBWriteMessage(2));
 
   free(array);
 }
@@ -854,6 +922,51 @@ TEST(AddressSanitizer, MemSetOOBTest) {
   // We can test arrays of structres/classes here, but what for?
 }
 
+// Try to allocate two arrays of 'size' bytes that are near each other.
+// Strictly speaking we are not guaranteed to find such two pointers,
+// but given the structure of asan's allocator we will.
+static bool AllocateTwoAdjacentArrays(char **x1, char **x2, size_t size) {
+  vector<char *> v;
+  bool res = false;
+  for (size_t i = 0; i < 1000U && !res; i++) {
+    v.push_back(new char[size]);
+    if (i == 0) continue;
+    sort(v.begin(), v.end());
+    for (size_t j = 1; j < v.size(); j++) {
+      assert(v[j] > v[j-1]);
+      if ((size_t)(v[j] - v[j-1]) < size * 2) {
+        *x2 = v[j];
+        *x1 = v[j-1];
+        res = true;
+        break;
+      }
+    }
+  }
+
+  for (size_t i = 0; i < v.size(); i++) {
+    if (res && v[i] == *x1) continue;
+    if (res && v[i] == *x2) continue;
+    delete [] v[i];
+  }
+  return res;
+}
+
+TEST(AddressSanitizer, LargeOOBInMemset) {
+  for (size_t size = 200; size < 100000; size += size / 2) {
+    char *x1, *x2;
+    if (!Ident(AllocateTwoAdjacentArrays)(&x1, &x2, size))
+      continue;
+    // fprintf(stderr, "  large oob memset: %p %p %zd\n", x1, x2, size);
+    // Do a memset on x1 with huge out-of-bound access that will end up in x2.
+    EXPECT_DEATH(Ident(memset)(x1, 0, size * 2),
+                 "is located 0 bytes to the right");
+    delete [] x1;
+    delete [] x2;
+    return;
+  }
+  assert(0 && "Did not find two adjacent malloc-ed pointers");
+}
+
 // Same test for memcpy and memmove functions
 template <typename T, class M>
 void MemTransferOOBTestTemplate(size_t length) {
@@ -877,27 +990,27 @@ void MemTransferOOBTestTemplate(size_t length) {
 
   // try to change mem to the right of dest
   EXPECT_DEATH(M::transfer(dest + 1, src, size),
-               RightOOBErrorMessage(sizeof(T) - 1));
+               RightOOBWriteMessage(0));
   EXPECT_DEATH(M::transfer((char*)(dest + length) - 1, src, 5),
-               RightOOBErrorMessage(3));
+               RightOOBWriteMessage(0));
 
   // try to change mem to the left of dest
   EXPECT_DEATH(M::transfer(dest - 2, src, size),
-               LeftOOBErrorMessage(2 * sizeof(T)));
+               LeftOOBWriteMessage(2 * sizeof(T)));
   EXPECT_DEATH(M::transfer((char*)dest - 3, src, 4),
-               LeftOOBErrorMessage(3));
+               LeftOOBWriteMessage(3));
 
   // try to access mem to the right of src
   EXPECT_DEATH(M::transfer(dest, src + 2, size),
-               RightOOBErrorMessage(2 * sizeof(T) - 1));
+               RightOOBReadMessage(0));
   EXPECT_DEATH(M::transfer(dest, (char*)(src + length) - 3, 6),
-               RightOOBErrorMessage(2));
+               RightOOBReadMessage(0));
 
   // try to access mem to the left of src
   EXPECT_DEATH(M::transfer(dest, src - 1, size),
-               LeftOOBErrorMessage(sizeof(T)));
+               LeftOOBReadMessage(sizeof(T)));
   EXPECT_DEATH(M::transfer(dest, (char*)src - 6, 7),
-               LeftOOBErrorMessage(6));
+               LeftOOBReadMessage(6));
 
   // Generally we don't need to test cases where both accessing src and writing
   // to dest address to poisoned memory.
@@ -906,10 +1019,10 @@ void MemTransferOOBTestTemplate(size_t length) {
   T *big_dest = Ident((T*)malloc(size * 2));
   // try to change mem to both sides of dest
   EXPECT_DEATH(M::transfer(dest - 1, big_src, size * 2),
-               LeftOOBErrorMessage(sizeof(T)));
+               LeftOOBWriteMessage(sizeof(T)));
   // try to access mem to both sides of src
   EXPECT_DEATH(M::transfer(big_dest, src - 2, size * 2),
-               LeftOOBErrorMessage(2 * sizeof(T)));
+               LeftOOBReadMessage(2 * sizeof(T)));
 
   free(src);
   free(dest);
@@ -920,7 +1033,7 @@ void MemTransferOOBTestTemplate(size_t length) {
 class MemCpyWrapper {
  public:
   static void* transfer(void *to, const void *from, size_t size) {
-    return memcpy(to, from, size);
+    return Ident(memcpy)(to, from, size);
   }
 };
 TEST(AddressSanitizer, MemCpyOOBTest) {
@@ -931,7 +1044,7 @@ TEST(AddressSanitizer, MemCpyOOBTest) {
 class MemMoveWrapper {
  public:
   static void* transfer(void *to, const void *from, size_t size) {
-    return memmove(to, from, size);
+    return Ident(memmove)(to, from, size);
   }
 };
 TEST(AddressSanitizer, MemMoveOOBTest) {
@@ -958,15 +1071,15 @@ void StrLenOOBTestTemplate(char *str, size_t length, bool is_global) {
   // Arg of strlen is not malloced, OOB access
   if (!is_global) {
     // We don't insert RedZones to the left of global variables
-    EXPECT_DEATH(Ident(strlen(str - 1)), LeftOOBErrorMessage(1));
-    EXPECT_DEATH(Ident(strlen(str - 5)), LeftOOBErrorMessage(5));
+    EXPECT_DEATH(Ident(strlen(str - 1)), LeftOOBReadMessage(1));
+    EXPECT_DEATH(Ident(strlen(str - 5)), LeftOOBReadMessage(5));
   }
-  EXPECT_DEATH(Ident(strlen(str + length + 1)), RightOOBErrorMessage(0));
+  EXPECT_DEATH(Ident(strlen(str + length + 1)), RightOOBReadMessage(0));
   // Overwrite terminator
   str[length] = 'a';
   // String is not zero-terminated, strlen will lead to OOB access
-  EXPECT_DEATH(Ident(strlen(str)), RightOOBErrorMessage(0));
-  EXPECT_DEATH(Ident(strlen(str + length)), RightOOBErrorMessage(0));
+  EXPECT_DEATH(Ident(strlen(str)), RightOOBReadMessage(0));
+  EXPECT_DEATH(Ident(strlen(str + length)), RightOOBReadMessage(0));
   // Restore terminator
   str[length] = 0;
 }
@@ -1010,11 +1123,11 @@ TEST(AddressSanitizer, StrNLenOOBTest) {
   str[size - 1] = '\0';
   Ident(strnlen(str, 2 * size));
   // Argument points to not allocated memory.
-  EXPECT_DEATH(Ident(strnlen(str - 1, 1)), LeftOOBErrorMessage(1));
-  EXPECT_DEATH(Ident(strnlen(str + size, 1)), RightOOBErrorMessage(0));
+  EXPECT_DEATH(Ident(strnlen(str - 1, 1)), LeftOOBReadMessage(1));
+  EXPECT_DEATH(Ident(strnlen(str + size, 1)), RightOOBReadMessage(0));
   // Overwrite the terminating '\0' and hit unallocated memory.
   str[size - 1] = 'z';
-  EXPECT_DEATH(Ident(strnlen(str, size + 1)), RightOOBErrorMessage(0));
+  EXPECT_DEATH(Ident(strnlen(str, size + 1)), RightOOBReadMessage(0));
   free(str);
 }
 #endif
@@ -1030,11 +1143,11 @@ TEST(AddressSanitizer, StrDupOOBTest) {
   new_str = strdup(str + size - 1);
   free(new_str);
   // Argument points to not allocated memory.
-  EXPECT_DEATH(Ident(strdup(str - 1)), LeftOOBErrorMessage(1));
-  EXPECT_DEATH(Ident(strdup(str + size)), RightOOBErrorMessage(0));
+  EXPECT_DEATH(Ident(strdup(str - 1)), LeftOOBReadMessage(1));
+  EXPECT_DEATH(Ident(strdup(str + size)), RightOOBReadMessage(0));
   // Overwrite the terminating '\0' and hit unallocated memory.
   str[size - 1] = 'z';
-  EXPECT_DEATH(Ident(strdup(str)), RightOOBErrorMessage(0));
+  EXPECT_DEATH(Ident(strdup(str)), RightOOBReadMessage(0));
   free(str);
 }
 
@@ -1048,15 +1161,15 @@ TEST(AddressSanitizer, StrCpyOOBTest) {
   strcpy(to, from);
   strcpy(to + to_size - from_size, from);
   // Length of "from" is too small.
-  EXPECT_DEATH(Ident(strcpy(from, "hello2")), RightOOBErrorMessage(0));
+  EXPECT_DEATH(Ident(strcpy(from, "hello2")), RightOOBWriteMessage(0));
   // "to" or "from" points to not allocated memory.
-  EXPECT_DEATH(Ident(strcpy(to - 1, from)), LeftOOBErrorMessage(1));
-  EXPECT_DEATH(Ident(strcpy(to, from - 1)), LeftOOBErrorMessage(1));
-  EXPECT_DEATH(Ident(strcpy(to, from + from_size)), RightOOBErrorMessage(0));
-  EXPECT_DEATH(Ident(strcpy(to + to_size, from)), RightOOBErrorMessage(0));
+  EXPECT_DEATH(Ident(strcpy(to - 1, from)), LeftOOBWriteMessage(1));
+  EXPECT_DEATH(Ident(strcpy(to, from - 1)), LeftOOBReadMessage(1));
+  EXPECT_DEATH(Ident(strcpy(to, from + from_size)), RightOOBReadMessage(0));
+  EXPECT_DEATH(Ident(strcpy(to + to_size, from)), RightOOBWriteMessage(0));
   // Overwrite the terminating '\0' character and hit unallocated memory.
   from[from_size - 1] = '!';
-  EXPECT_DEATH(Ident(strcpy(to, from)), RightOOBErrorMessage(0));
+  EXPECT_DEATH(Ident(strcpy(to, from)), RightOOBReadMessage(0));
   free(to);
   free(from);
 }
@@ -1078,25 +1191,25 @@ TEST(AddressSanitizer, StrNCpyOOBTest) {
   strncpy(to + to_size - 1, from, 1);
   // One of {to, from} points to not allocated memory
   EXPECT_DEATH(Ident(strncpy(to, from - 1, from_size)),
-               LeftOOBErrorMessage(1));
+               LeftOOBReadMessage(1));
   EXPECT_DEATH(Ident(strncpy(to - 1, from, from_size)),
-               LeftOOBErrorMessage(1));
+               LeftOOBWriteMessage(1));
   EXPECT_DEATH(Ident(strncpy(to, from + from_size, 1)),
-               RightOOBErrorMessage(0));
+               RightOOBReadMessage(0));
   EXPECT_DEATH(Ident(strncpy(to + to_size, from, 1)),
-               RightOOBErrorMessage(0));
+               RightOOBWriteMessage(0));
   // Length of "to" is too small
   EXPECT_DEATH(Ident(strncpy(to + to_size - from_size + 1, from, from_size)),
-               RightOOBErrorMessage(0));
+               RightOOBWriteMessage(0));
   EXPECT_DEATH(Ident(strncpy(to + 1, from, to_size)),
-               RightOOBErrorMessage(0));
+               RightOOBWriteMessage(0));
   // Overwrite terminator in from
   from[from_size - 1] = '!';
   // normal strncpy call
   strncpy(to, from, from_size);
   // Length of "from" is too small
   EXPECT_DEATH(Ident(strncpy(to, from, to_size)),
-               RightOOBErrorMessage(0));
+               RightOOBReadMessage(0));
   free(to);
   free(from);
 }
@@ -1117,11 +1230,11 @@ USED static void RunStrChrTest(PointerToStrChr1 StrChr) {
   EXPECT_EQ(str + 10, StrChr(str, 'q'));
   EXPECT_EQ(NULL, StrChr(str, 'a'));
   // StrChr argument points to not allocated memory.
-  EXPECT_DEATH(Ident(StrChr(str - 1, 'z')), LeftOOBErrorMessage(1));
-  EXPECT_DEATH(Ident(StrChr(str + size, 'z')), RightOOBErrorMessage(0));
+  EXPECT_DEATH(Ident(StrChr(str - 1, 'z')), LeftOOBReadMessage(1));
+  EXPECT_DEATH(Ident(StrChr(str + size, 'z')), RightOOBReadMessage(0));
   // Overwrite the terminator and hit not allocated memory.
   str[11] = 'z';
-  EXPECT_DEATH(Ident(StrChr(str, 'a')), RightOOBErrorMessage(0));
+  EXPECT_DEATH(Ident(StrChr(str, 'a')), RightOOBReadMessage(0));
   free(str);
 }
 USED static void RunStrChrTest(PointerToStrChr2 StrChr) {
@@ -1133,11 +1246,11 @@ USED static void RunStrChrTest(PointerToStrChr2 StrChr) {
   EXPECT_EQ(str + 10, StrChr(str, 'q'));
   EXPECT_EQ(NULL, StrChr(str, 'a'));
   // StrChr argument points to not allocated memory.
-  EXPECT_DEATH(Ident(StrChr(str - 1, 'z')), LeftOOBErrorMessage(1));
-  EXPECT_DEATH(Ident(StrChr(str + size, 'z')), RightOOBErrorMessage(0));
+  EXPECT_DEATH(Ident(StrChr(str - 1, 'z')), LeftOOBReadMessage(1));
+  EXPECT_DEATH(Ident(StrChr(str + size, 'z')), RightOOBReadMessage(0));
   // Overwrite the terminator and hit not allocated memory.
   str[11] = 'z';
-  EXPECT_DEATH(Ident(StrChr(str, 'a')), RightOOBErrorMessage(0));
+  EXPECT_DEATH(Ident(StrChr(str, 'a')), RightOOBReadMessage(0));
   free(str);
 }
 
@@ -1198,8 +1311,9 @@ TEST(AddressSanitizer, StrCmpAndFriendsLogicTest) {
 typedef int(*PointerToStrCmp)(const char*, const char*);
 void RunStrCmpTest(PointerToStrCmp StrCmp) {
   size_t size = Ident(100);
-  char *s1 = MallocAndMemsetString(size);
-  char *s2 = MallocAndMemsetString(size);
+  int fill = 'o';
+  char *s1 = MallocAndMemsetString(size, fill);
+  char *s2 = MallocAndMemsetString(size, fill);
   s1[size - 1] = '\0';
   s2[size - 1] = '\0';
   // Normal StrCmp calls
@@ -1210,14 +1324,14 @@ void RunStrCmpTest(PointerToStrCmp StrCmp) {
   s2[size - 1] = 'x';
   Ident(StrCmp(s1, s2));
   // One of arguments points to not allocated memory.
-  EXPECT_DEATH(Ident(StrCmp)(s1 - 1, s2), LeftOOBErrorMessage(1));
-  EXPECT_DEATH(Ident(StrCmp)(s1, s2 - 1), LeftOOBErrorMessage(1));
-  EXPECT_DEATH(Ident(StrCmp)(s1 + size, s2), RightOOBErrorMessage(0));
-  EXPECT_DEATH(Ident(StrCmp)(s1, s2 + size), RightOOBErrorMessage(0));
+  EXPECT_DEATH(Ident(StrCmp)(s1 - 1, s2), LeftOOBReadMessage(1));
+  EXPECT_DEATH(Ident(StrCmp)(s1, s2 - 1), LeftOOBReadMessage(1));
+  EXPECT_DEATH(Ident(StrCmp)(s1 + size, s2), RightOOBReadMessage(0));
+  EXPECT_DEATH(Ident(StrCmp)(s1, s2 + size), RightOOBReadMessage(0));
   // Hit unallocated memory and die.
-  s2[size - 1] = 'z';
-  EXPECT_DEATH(Ident(StrCmp)(s1, s1), RightOOBErrorMessage(0));
-  EXPECT_DEATH(Ident(StrCmp)(s1 + size - 1, s2), RightOOBErrorMessage(0));
+  s1[size - 1] = fill;
+  EXPECT_DEATH(Ident(StrCmp)(s1, s1), RightOOBReadMessage(0));
+  EXPECT_DEATH(Ident(StrCmp)(s1 + size - 1, s2), RightOOBReadMessage(0));
   free(s1);
   free(s2);
 }
@@ -1246,13 +1360,13 @@ void RunStrNCmpTest(PointerToStrNCmp StrNCmp) {
   Ident(StrNCmp(s1 - 1, s2 - 1, 0));
   Ident(StrNCmp(s1 + size - 1, s2 + size - 1, 1));
   // One of arguments points to not allocated memory.
-  EXPECT_DEATH(Ident(StrNCmp)(s1 - 1, s2, 1), LeftOOBErrorMessage(1));
-  EXPECT_DEATH(Ident(StrNCmp)(s1, s2 - 1, 1), LeftOOBErrorMessage(1));
-  EXPECT_DEATH(Ident(StrNCmp)(s1 + size, s2, 1), RightOOBErrorMessage(0));
-  EXPECT_DEATH(Ident(StrNCmp)(s1, s2 + size, 1), RightOOBErrorMessage(0));
+  EXPECT_DEATH(Ident(StrNCmp)(s1 - 1, s2, 1), LeftOOBReadMessage(1));
+  EXPECT_DEATH(Ident(StrNCmp)(s1, s2 - 1, 1), LeftOOBReadMessage(1));
+  EXPECT_DEATH(Ident(StrNCmp)(s1 + size, s2, 1), RightOOBReadMessage(0));
+  EXPECT_DEATH(Ident(StrNCmp)(s1, s2 + size, 1), RightOOBReadMessage(0));
   // Hit unallocated memory and die.
-  EXPECT_DEATH(Ident(StrNCmp)(s1 + 1, s2 + 1, size), RightOOBErrorMessage(0));
-  EXPECT_DEATH(Ident(StrNCmp)(s1 + size - 1, s2, 2), RightOOBErrorMessage(0));
+  EXPECT_DEATH(Ident(StrNCmp)(s1 + 1, s2 + 1, size), RightOOBReadMessage(0));
+  EXPECT_DEATH(Ident(StrNCmp)(s1 + size - 1, s2, 2), RightOOBReadMessage(0));
   free(s1);
   free(s2);
 }
@@ -1274,22 +1388,23 @@ TEST(AddressSanitizer, MemCmpOOBTest) {
   Ident(memcmp(s1 + size - 1, s2 + size - 1, 1));
   Ident(memcmp(s1 - 1, s2 - 1, 0));
   // One of arguments points to not allocated memory.
-  EXPECT_DEATH(Ident(memcmp)(s1 - 1, s2, 1), LeftOOBErrorMessage(1));
-  EXPECT_DEATH(Ident(memcmp)(s1, s2 - 1, 1), LeftOOBErrorMessage(1));
-  EXPECT_DEATH(Ident(memcmp)(s1 + size, s2, 1), RightOOBErrorMessage(0));
-  EXPECT_DEATH(Ident(memcmp)(s1, s2 + size, 1), RightOOBErrorMessage(0));
+  EXPECT_DEATH(Ident(memcmp)(s1 - 1, s2, 1), LeftOOBReadMessage(1));
+  EXPECT_DEATH(Ident(memcmp)(s1, s2 - 1, 1), LeftOOBReadMessage(1));
+  EXPECT_DEATH(Ident(memcmp)(s1 + size, s2, 1), RightOOBReadMessage(0));
+  EXPECT_DEATH(Ident(memcmp)(s1, s2 + size, 1), RightOOBReadMessage(0));
   // Hit unallocated memory and die.
-  EXPECT_DEATH(Ident(memcmp)(s1 + 1, s2 + 1, size), RightOOBErrorMessage(0));
-  EXPECT_DEATH(Ident(memcmp)(s1 + size - 1, s2, 2), RightOOBErrorMessage(0));
+  EXPECT_DEATH(Ident(memcmp)(s1 + 1, s2 + 1, size), RightOOBReadMessage(0));
+  EXPECT_DEATH(Ident(memcmp)(s1 + size - 1, s2, 2), RightOOBReadMessage(0));
   // Zero bytes are not terminators and don't prevent from OOB.
   s1[size - 1] = '\0';
   s2[size - 1] = '\0';
-  EXPECT_DEATH(Ident(memcmp)(s1, s2, size + 1), RightOOBErrorMessage(0));
+  EXPECT_DEATH(Ident(memcmp)(s1, s2, size + 1), RightOOBReadMessage(0));
   free(s1);
   free(s2);
 }
 
 TEST(AddressSanitizer, StrCatOOBTest) {
+  // strcat() reads strlen(to) bytes from |to| before concatenating.
   size_t to_size = Ident(100);
   char *to = MallocAndMemsetString(to_size);
   to[0] = '\0';
@@ -1302,23 +1417,23 @@ TEST(AddressSanitizer, StrCatOOBTest) {
   strcat(to + from_size, from + from_size - 2);
   // Passing an invalid pointer is an error even when concatenating an empty
   // string.
-  EXPECT_DEATH(strcat(to - 1, from + from_size - 1), LeftOOBErrorMessage(1));
+  EXPECT_DEATH(strcat(to - 1, from + from_size - 1), LeftOOBAccessMessage(1));
   // One of arguments points to not allocated memory.
-  EXPECT_DEATH(strcat(to - 1, from), LeftOOBErrorMessage(1));
-  EXPECT_DEATH(strcat(to, from - 1), LeftOOBErrorMessage(1));
-  EXPECT_DEATH(strcat(to + to_size, from), RightOOBErrorMessage(0));
-  EXPECT_DEATH(strcat(to, from + from_size), RightOOBErrorMessage(0));
+  EXPECT_DEATH(strcat(to - 1, from), LeftOOBAccessMessage(1));
+  EXPECT_DEATH(strcat(to, from - 1), LeftOOBReadMessage(1));
+  EXPECT_DEATH(strcat(to + to_size, from), RightOOBWriteMessage(0));
+  EXPECT_DEATH(strcat(to, from + from_size), RightOOBReadMessage(0));
 
   // "from" is not zero-terminated.
   from[from_size - 1] = 'z';
-  EXPECT_DEATH(strcat(to, from), RightOOBErrorMessage(0));
+  EXPECT_DEATH(strcat(to, from), RightOOBReadMessage(0));
   from[from_size - 1] = '\0';
   // "to" is not zero-terminated.
   memset(to, 'z', to_size);
-  EXPECT_DEATH(strcat(to, from), RightOOBErrorMessage(0));
+  EXPECT_DEATH(strcat(to, from), RightOOBWriteMessage(0));
   // "to" is too short to fit "from".
   to[to_size - from_size + 1] = '\0';
-  EXPECT_DEATH(strcat(to, from), RightOOBErrorMessage(0));
+  EXPECT_DEATH(strcat(to, from), RightOOBWriteMessage(0));
   // length of "to" is just enough.
   strcat(to, from + 1);
 
@@ -1327,6 +1442,7 @@ TEST(AddressSanitizer, StrCatOOBTest) {
 }
 
 TEST(AddressSanitizer, StrNCatOOBTest) {
+  // strncat() reads strlen(to) bytes from |to| before concatenating.
   size_t to_size = Ident(100);
   char *to = MallocAndMemsetString(to_size);
   to[0] = '\0';
@@ -1338,25 +1454,25 @@ TEST(AddressSanitizer, StrNCatOOBTest) {
   from[from_size - 1] = '\0';
   strncat(to, from, 2 * from_size);
   // Catenating empty string with an invalid string is still an error.
-  EXPECT_DEATH(strncat(to - 1, from, 0), LeftOOBErrorMessage(1));
+  EXPECT_DEATH(strncat(to - 1, from, 0), LeftOOBAccessMessage(1));
   strncat(to, from + from_size - 1, 10);
   // One of arguments points to not allocated memory.
-  EXPECT_DEATH(strncat(to - 1, from, 2), LeftOOBErrorMessage(1));
-  EXPECT_DEATH(strncat(to, from - 1, 2), LeftOOBErrorMessage(1));
-  EXPECT_DEATH(strncat(to + to_size, from, 2), RightOOBErrorMessage(0));
-  EXPECT_DEATH(strncat(to, from + from_size, 2), RightOOBErrorMessage(0));
+  EXPECT_DEATH(strncat(to - 1, from, 2), LeftOOBAccessMessage(1));
+  EXPECT_DEATH(strncat(to, from - 1, 2), LeftOOBReadMessage(1));
+  EXPECT_DEATH(strncat(to + to_size, from, 2), RightOOBWriteMessage(0));
+  EXPECT_DEATH(strncat(to, from + from_size, 2), RightOOBReadMessage(0));
 
   memset(from, 'z', from_size);
   memset(to, 'z', to_size);
   to[0] = '\0';
   // "from" is too short.
-  EXPECT_DEATH(strncat(to, from, from_size + 1), RightOOBErrorMessage(0));
+  EXPECT_DEATH(strncat(to, from, from_size + 1), RightOOBReadMessage(0));
   // "to" is not zero-terminated.
-  EXPECT_DEATH(strncat(to + 1, from, 1), RightOOBErrorMessage(0));
+  EXPECT_DEATH(strncat(to + 1, from, 1), RightOOBWriteMessage(0));
   // "to" is too short to fit "from".
   to[0] = 'z';
   to[to_size - from_size + 1] = '\0';
-  EXPECT_DEATH(strncat(to, from, from_size - 1), RightOOBErrorMessage(0));
+  EXPECT_DEATH(strncat(to, from, from_size - 1), RightOOBWriteMessage(0));
   // "to" is just enough.
   strncat(to, from, from_size - 2);
 
@@ -1447,10 +1563,10 @@ typedef void(*PointerToCallAtoi)(const char*);
 void RunAtoiOOBTest(PointerToCallAtoi Atoi) {
   char *array = MallocAndMemsetString(10, '1');
   // Invalid pointer to the string.
-  EXPECT_DEATH(Atoi(array + 11), RightOOBErrorMessage(1));
-  EXPECT_DEATH(Atoi(array - 1), LeftOOBErrorMessage(1));
+  EXPECT_DEATH(Atoi(array + 11), RightOOBReadMessage(1));
+  EXPECT_DEATH(Atoi(array - 1), LeftOOBReadMessage(1));
   // Die if a buffer doesn't have terminating NULL.
-  EXPECT_DEATH(Atoi(array), RightOOBErrorMessage(0));
+  EXPECT_DEATH(Atoi(array), RightOOBReadMessage(0));
   // Make last symbol a terminating NULL or other non-digit.
   array[9] = '\0';
   Atoi(array);
@@ -1459,13 +1575,13 @@ void RunAtoiOOBTest(PointerToCallAtoi Atoi) {
   Atoi(array + 9);
   // Sometimes we need to detect overflow if no digits are found.
   memset(array, ' ', 10);
-  EXPECT_DEATH(Atoi(array), RightOOBErrorMessage(0));
+  EXPECT_DEATH(Atoi(array), RightOOBReadMessage(0));
   array[9] = '-';
-  EXPECT_DEATH(Atoi(array), RightOOBErrorMessage(0));
-  EXPECT_DEATH(Atoi(array + 9), RightOOBErrorMessage(0));
+  EXPECT_DEATH(Atoi(array), RightOOBReadMessage(0));
+  EXPECT_DEATH(Atoi(array + 9), RightOOBReadMessage(0));
   array[8] = '-';
   Atoi(array);
-  delete array;
+  free(array);
 }
 
 TEST(AddressSanitizer, AtoiAndFriendsOOBTest) {
@@ -1489,16 +1605,16 @@ void RunStrtolOOBTest(PointerToCallStrtol Strtol) {
   array[1] = '2';
   array[2] = '3';
   // Invalid pointer to the string.
-  EXPECT_DEATH(Strtol(array + 3, NULL, 0), RightOOBErrorMessage(0));
-  EXPECT_DEATH(Strtol(array - 1, NULL, 0), LeftOOBErrorMessage(1));
+  EXPECT_DEATH(Strtol(array + 3, NULL, 0), RightOOBReadMessage(0));
+  EXPECT_DEATH(Strtol(array - 1, NULL, 0), LeftOOBReadMessage(1));
   // Buffer overflow if there is no terminating null (depends on base).
   Strtol(array, &endptr, 3);
   EXPECT_EQ(array + 2, endptr);
-  EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBErrorMessage(0));
+  EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0));
   array[2] = 'z';
   Strtol(array, &endptr, 35);
   EXPECT_EQ(array + 2, endptr);
-  EXPECT_DEATH(Strtol(array, NULL, 36), RightOOBErrorMessage(0));
+  EXPECT_DEATH(Strtol(array, NULL, 36), RightOOBReadMessage(0));
   // Add terminating zero to get rid of overflow.
   array[2] = '\0';
   Strtol(array, NULL, 36);
@@ -1507,11 +1623,11 @@ void RunStrtolOOBTest(PointerToCallStrtol Strtol) {
   Strtol(array + 3, NULL, 1);
   // Sometimes we need to detect overflow if no digits are found.
   array[0] = array[1] = array[2] = ' ';
-  EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBErrorMessage(0));
+  EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0));
   array[2] = '+';
-  EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBErrorMessage(0));
+  EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0));
   array[2] = '-';
-  EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBErrorMessage(0));
+  EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0));
   array[1] = '+';
   Strtol(array, NULL, 0);
   array[1] = array[2] = 'z';
@@ -1519,7 +1635,7 @@ void RunStrtolOOBTest(PointerToCallStrtol Strtol) {
   EXPECT_EQ(array, endptr);
   Strtol(array + 2, NULL, 0);
   EXPECT_EQ(array, endptr);
-  delete array;
+  free(array);
 }
 
 TEST(AddressSanitizer, StrtollOOBTest) {
@@ -1538,7 +1654,7 @@ typedef void*(*PointerToMemSet)(void*, int, size_t);
 void CallMemSetByPointer(PointerToMemSet MemSet) {
   size_t size = Ident(100);
   char *array = Ident((char*)malloc(size));
-  EXPECT_DEATH(MemSet(array, 0, 101), RightOOBErrorMessage(0));
+  EXPECT_DEATH(MemSet(array, 0, 101), RightOOBWriteMessage(0));
   free(array);
 }
 
@@ -1546,7 +1662,7 @@ void CallMemTransferByPointer(PointerToMemTransfer MemTransfer) {
   size_t size = Ident(100);
   char *src = Ident((char*)malloc(size));
   char *dst = Ident((char*)malloc(size));
-  EXPECT_DEATH(MemTransfer(dst, src, 101), RightOOBErrorMessage(0));
+  EXPECT_DEATH(MemTransfer(dst, src, 101), RightOOBWriteMessage(0));
   free(src);
   free(dst);
 }
@@ -1557,12 +1673,51 @@ TEST(AddressSanitizer, DISABLED_MemIntrinsicCallByPointerTest) {
   CallMemTransferByPointer(&memmove);
 }
 
+#if defined(__linux__) && !defined(ANDROID) && !defined(__ANDROID__)
+TEST(AddressSanitizer, pread) {
+  char *x = new char[10];
+  int fd = open("/proc/self/stat", O_RDONLY);
+  ASSERT_GT(fd, 0);
+  EXPECT_DEATH(pread(fd, x, 15, 0),
+               ASAN_PCRE_DOTALL
+               "AddressSanitizer: heap-buffer-overflow"
+               ".* is located 0 bytes to the right of 10-byte region");
+  close(fd);
+  delete [] x;
+}
+
+TEST(AddressSanitizer, pread64) {
+  char *x = new char[10];
+  int fd = open("/proc/self/stat", O_RDONLY);
+  ASSERT_GT(fd, 0);
+  EXPECT_DEATH(pread64(fd, x, 15, 0),
+               ASAN_PCRE_DOTALL
+               "AddressSanitizer: heap-buffer-overflow"
+               ".* is located 0 bytes to the right of 10-byte region");
+  close(fd);
+  delete [] x;
+}
+
+TEST(AddressSanitizer, read) {
+  char *x = new char[10];
+  int fd = open("/proc/self/stat", O_RDONLY);
+  ASSERT_GT(fd, 0);
+  EXPECT_DEATH(read(fd, x, 15),
+               ASAN_PCRE_DOTALL
+               "AddressSanitizer: heap-buffer-overflow"
+               ".* is located 0 bytes to the right of 10-byte region");
+  close(fd);
+  delete [] x;
+}
+
+#endif  // defined(__linux__) && !defined(ANDROID) && !defined(__ANDROID__)
+
 // This test case fails
 // Clang optimizes memcpy/memset calls which lead to unaligned access
 TEST(AddressSanitizer, DISABLED_MemIntrinsicUnalignedAccessTest) {
   int size = Ident(4096);
   char *s = Ident((char*)malloc(size));
-  EXPECT_DEATH(memset(s + size - 1, 0, 2), RightOOBErrorMessage(0));
+  EXPECT_DEATH(memset(s + size - 1, 0, 2), RightOOBWriteMessage(0));
   free(s);
 }
 
@@ -1617,19 +1772,30 @@ TEST(AddressSanitizer, DISABLED_MallocFreeUnwindAndSymbolizeTest) {
                "malloc_fff.*malloc_eee.*malloc_ddd");
 }
 
+static bool TryToSetThreadName(const char *name) {
+#if defined(__linux__) && defined(PR_SET_NAME)
+  return 0 == prctl(PR_SET_NAME, (unsigned long)name, 0, 0, 0);
+#else
+  return false;
+#endif
+}
+
 void *ThreadedTestAlloc(void *a) {
+  EXPECT_EQ(true, TryToSetThreadName("AllocThr"));
   int **p = (int**)a;
   *p = new int;
   return 0;
 }
 
 void *ThreadedTestFree(void *a) {
+  EXPECT_EQ(true, TryToSetThreadName("FreeThr"));
   int **p = (int**)a;
   delete *p;
   return 0;
 }
 
 void *ThreadedTestUse(void *a) {
+  EXPECT_EQ(true, TryToSetThreadName("UseThr"));
   int **p = (int**)a;
   **p = 1;
   return 0;
@@ -1654,6 +1820,30 @@ TEST(AddressSanitizer, ThreadedTest) {
                ".*Thread T.*created");
 }
 
+void *ThreadedTestFunc(void *unused) {
+  // Check if prctl(PR_SET_NAME) is supported. Return if not.
+  if (!TryToSetThreadName("TestFunc"))
+    return 0;
+  EXPECT_DEATH(ThreadedTestSpawn(),
+               ASAN_PCRE_DOTALL
+               "WRITE .*thread T. .UseThr."
+               ".*freed by thread T. .FreeThr. here:"
+               ".*previously allocated by thread T. .AllocThr. here:"
+               ".*Thread T. .UseThr. created by T.*TestFunc"
+               ".*Thread T. .FreeThr. created by T"
+               ".*Thread T. .AllocThr. created by T"
+               "");
+  return 0;
+}
+
+TEST(AddressSanitizer, ThreadNamesTest) {
+  // Run ThreadedTestFunc in a separate thread because it tries to set a
+  // thread name and we don't want to change the main thread's name.
+  pthread_t t;
+  PTHREAD_CREATE(&t, 0, ThreadedTestFunc, 0);
+  PTHREAD_JOIN(t, 0);
+}
+
 #if ASAN_NEEDS_SEGV
 TEST(AddressSanitizer, ShadowGapTest) {
 #if SANITIZER_WORDSIZE == 32
@@ -1868,6 +2058,27 @@ TEST(AddressSanitizer, AttributeNoAddressSafetyTest) {
   Ident(NoAddressSafety)();
 }
 
+static string MismatchStr(const string &str) {
+  return string("AddressSanitizer: alloc-dealloc-mismatch \\(") + str;
+}
+
+// This test is disabled until we enable alloc_dealloc_mismatch by default.
+// The feature is also tested by lit tests.
+TEST(AddressSanitizer, DISABLED_AllocDeallocMismatch) {
+  EXPECT_DEATH(free(Ident(new int)),
+               MismatchStr("operator new vs free"));
+  EXPECT_DEATH(free(Ident(new int[2])),
+               MismatchStr("operator new \\[\\] vs free"));
+  EXPECT_DEATH(delete (Ident(new int[2])),
+               MismatchStr("operator new \\[\\] vs operator delete"));
+  EXPECT_DEATH(delete (Ident((int*)malloc(2 * sizeof(int)))),
+               MismatchStr("malloc vs operator delete"));
+  EXPECT_DEATH(delete [] (Ident(new int)),
+               MismatchStr("operator new vs operator delete \\[\\]"));
+  EXPECT_DEATH(delete [] (Ident((int*)malloc(2 * sizeof(int)))),
+               MismatchStr("malloc vs operator delete \\[\\]"));
+}
+
 // ------------------ demo tests; run each one-by-one -------------
 // e.g. --gtest_filter=*DemoOOBLeftHigh --gtest_also_run_disabled_tests
 TEST(AddressSanitizer, DISABLED_DemoThreadedTest) {
@@ -2033,53 +2244,56 @@ TEST(AddressSanitizerMac, DISABLED_CFAllocatorMallocZoneDoubleFree) {
   EXPECT_DEATH(CFAllocatorMallocZoneDoubleFree(), "attempting double-free");
 }
 
+// For libdispatch tests below we check that ASan got to the shadow byte
+// legend, i.e. managed to print the thread stacks (this almost certainly
+// means that the libdispatch task creation has been intercepted correctly).
 TEST(AddressSanitizerMac, GCDDispatchAsync) {
   // Make sure the whole ASan report is printed, i.e. that we don't die
   // on a CHECK.
-  EXPECT_DEATH(TestGCDDispatchAsync(), "Shadow byte and word");
+  EXPECT_DEATH(TestGCDDispatchAsync(), "Shadow byte legend");
 }
 
 TEST(AddressSanitizerMac, GCDDispatchSync) {
   // Make sure the whole ASan report is printed, i.e. that we don't die
   // on a CHECK.
-  EXPECT_DEATH(TestGCDDispatchSync(), "Shadow byte and word");
+  EXPECT_DEATH(TestGCDDispatchSync(), "Shadow byte legend");
 }
 
 
 TEST(AddressSanitizerMac, GCDReuseWqthreadsAsync) {
   // Make sure the whole ASan report is printed, i.e. that we don't die
   // on a CHECK.
-  EXPECT_DEATH(TestGCDReuseWqthreadsAsync(), "Shadow byte and word");
+  EXPECT_DEATH(TestGCDReuseWqthreadsAsync(), "Shadow byte legend");
 }
 
 TEST(AddressSanitizerMac, GCDReuseWqthreadsSync) {
   // Make sure the whole ASan report is printed, i.e. that we don't die
   // on a CHECK.
-  EXPECT_DEATH(TestGCDReuseWqthreadsSync(), "Shadow byte and word");
+  EXPECT_DEATH(TestGCDReuseWqthreadsSync(), "Shadow byte legend");
 }
 
 TEST(AddressSanitizerMac, GCDDispatchAfter) {
   // Make sure the whole ASan report is printed, i.e. that we don't die
   // on a CHECK.
-  EXPECT_DEATH(TestGCDDispatchAfter(), "Shadow byte and word");
+  EXPECT_DEATH(TestGCDDispatchAfter(), "Shadow byte legend");
 }
 
 TEST(AddressSanitizerMac, GCDSourceEvent) {
   // Make sure the whole ASan report is printed, i.e. that we don't die
   // on a CHECK.
-  EXPECT_DEATH(TestGCDSourceEvent(), "Shadow byte and word");
+  EXPECT_DEATH(TestGCDSourceEvent(), "Shadow byte legend");
 }
 
 TEST(AddressSanitizerMac, GCDSourceCancel) {
   // Make sure the whole ASan report is printed, i.e. that we don't die
   // on a CHECK.
-  EXPECT_DEATH(TestGCDSourceCancel(), "Shadow byte and word");
+  EXPECT_DEATH(TestGCDSourceCancel(), "Shadow byte legend");
 }
 
 TEST(AddressSanitizerMac, GCDGroupAsync) {
   // Make sure the whole ASan report is printed, i.e. that we don't die
   // on a CHECK.
-  EXPECT_DEATH(TestGCDGroupAsync(), "Shadow byte and word");
+  EXPECT_DEATH(TestGCDGroupAsync(), "Shadow byte legend");
 }
 
 void *MallocIntrospectionLockWorker(void *_) {
@@ -2172,7 +2386,7 @@ TEST(AddressSanitizerMac, NSURLDeallocation) {
 TEST(AddressSanitizerMac, Mstats) {
   malloc_statistics_t stats1, stats2;
   malloc_zone_statistics(/*all zones*/NULL, &stats1);
-  const int kMallocSize = 100000;
+  const size_t kMallocSize = 100000;
   void *alloc = Ident(malloc(kMallocSize));
   malloc_zone_statistics(/*all zones*/NULL, &stats2);
   EXPECT_GT(stats2.blocks_in_use, stats1.blocks_in_use);
index ec9176fd1221b86f1dfbfd5daf8d5b687c452fea..e0e39c3e72c8564de393cd8c71949bd742f83c85 100644 (file)
@@ -1,3 +1,13 @@
+2013-01-10  Kostya Serebryany  <kcc@google.com>
+
+       * All source files: Merge from upstream r171973.
+       * sanitizer_common/Makefile.am: Added new files.
+       * asan/Makefile.am: Likewise.
+       * tsan/Makefile.am: Likewise.
+       * sanitizer_common/Makefile.in: Regenerated.
+       * asan/Makefile.in: Likewise.
+       * tsan/Makefile.in: Likewise.
+
 2013-01-07  H.J. Lu  <hongjiu.lu@intel.com>
 
        * asan/Makefile.am (libasan_la_LIBADD): Replace
index 071edae3249b5c9c579aefda9e4bd58e0fca4239..ff637c242ba7bcbe26635cb45e5f8abd12f86835 100644 (file)
@@ -1,4 +1,4 @@
-169392
+171973
 
 The first line of this file holds the svn revision number of the
 last merge done from the master library sources.
index 04a621fd0feb0a54fc1578984bd1f74397b15f0d..7d675001a37f522e374444cf4de624609876e9c9 100644 (file)
@@ -15,6 +15,7 @@ toolexeclib_LTLIBRARIES = libasan.la
 
 asan_files = \
        asan_allocator.cc \
+       asan_allocator2.cc \
        asan_interceptors.cc \
        asan_mac.cc \
        asan_malloc_mac.cc \
@@ -23,6 +24,7 @@ asan_files = \
        asan_rtl.cc \
        asan_stats.cc \
        asan_thread_registry.cc \
+       asan_fake_stack.cc \
        asan_globals.cc \
        asan_linux.cc \
        asan_malloc_linux.cc \
index 7c8e321a1cd56c543d9002c93105bb0cc3ed8717..4578391b3c08b7605cb5560bf628702eba036f90 100644 (file)
@@ -84,19 +84,20 @@ am__DEPENDENCIES_1 =
 @USING_MAC_INTERPOSE_FALSE@    $(am__DEPENDENCIES_1)
 @USING_MAC_INTERPOSE_TRUE@libasan_la_DEPENDENCIES = $(top_builddir)/sanitizer_common/libsanitizer_common.la \
 @USING_MAC_INTERPOSE_TRUE@     $(am__DEPENDENCIES_1)
-am__libasan_la_SOURCES_DIST = asan_allocator.cc asan_interceptors.cc \
-       asan_mac.cc asan_malloc_mac.cc asan_new_delete.cc \
-       asan_posix.cc asan_rtl.cc asan_stats.cc \
-       asan_thread_registry.cc asan_globals.cc asan_linux.cc \
-       asan_malloc_linux.cc asan_malloc_win.cc asan_poisoning.cc \
-       asan_report.cc asan_stack.cc asan_thread.cc asan_win.cc \
-       dynamic/asan_interceptors_dynamic.cc
-am__objects_1 = asan_allocator.lo asan_interceptors.lo asan_mac.lo \
-       asan_malloc_mac.lo asan_new_delete.lo asan_posix.lo \
-       asan_rtl.lo asan_stats.lo asan_thread_registry.lo \
-       asan_globals.lo asan_linux.lo asan_malloc_linux.lo \
-       asan_malloc_win.lo asan_poisoning.lo asan_report.lo \
-       asan_stack.lo asan_thread.lo asan_win.lo
+am__libasan_la_SOURCES_DIST = asan_allocator.cc asan_allocator2.cc \
+       asan_interceptors.cc asan_mac.cc asan_malloc_mac.cc \
+       asan_new_delete.cc asan_posix.cc asan_rtl.cc asan_stats.cc \
+       asan_thread_registry.cc asan_fake_stack.cc asan_globals.cc \
+       asan_linux.cc asan_malloc_linux.cc asan_malloc_win.cc \
+       asan_poisoning.cc asan_report.cc asan_stack.cc asan_thread.cc \
+       asan_win.cc dynamic/asan_interceptors_dynamic.cc
+am__objects_1 = asan_allocator.lo asan_allocator2.lo \
+       asan_interceptors.lo asan_mac.lo asan_malloc_mac.lo \
+       asan_new_delete.lo asan_posix.lo asan_rtl.lo asan_stats.lo \
+       asan_thread_registry.lo asan_fake_stack.lo asan_globals.lo \
+       asan_linux.lo asan_malloc_linux.lo asan_malloc_win.lo \
+       asan_poisoning.lo asan_report.lo asan_stack.lo asan_thread.lo \
+       asan_win.lo
 @USING_MAC_INTERPOSE_TRUE@am__objects_2 =  \
 @USING_MAC_INTERPOSE_TRUE@     asan_interceptors_dynamic.lo
 am_libasan_la_OBJECTS = $(am__objects_1) $(am__objects_2)
@@ -269,6 +270,7 @@ ACLOCAL_AMFLAGS = -I $(top_srcdir) -I $(top_srcdir)/config
 toolexeclib_LTLIBRARIES = libasan.la
 asan_files = \
        asan_allocator.cc \
+       asan_allocator2.cc \
        asan_interceptors.cc \
        asan_mac.cc \
        asan_malloc_mac.cc \
@@ -277,6 +279,7 @@ asan_files = \
        asan_rtl.cc \
        asan_stats.cc \
        asan_thread_registry.cc \
+       asan_fake_stack.cc \
        asan_globals.cc \
        asan_linux.cc \
        asan_malloc_linux.cc \
@@ -409,6 +412,8 @@ distclean-compile:
        -rm -f *.tab.c
 
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_allocator.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_allocator2.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_fake_stack.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_globals.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_interceptors.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_interceptors_dynamic.Plo@am__quote@
index 03d5bbd3eb5b66bb4621724673eceea520861d31..b170fe723d2d3882b471745482926aa065b0c672 100644 (file)
@@ -22,8 +22,9 @@
 // Once freed, the body of the chunk contains the stack trace of the free call.
 //
 //===----------------------------------------------------------------------===//
-
 #include "asan_allocator.h"
+
+#if ASAN_ALLOCATOR_VERSION == 1
 #include "asan_interceptors.h"
 #include "asan_internal.h"
 #include "asan_lock.h"
 #include "sanitizer/asan_interface.h"
 #include "sanitizer_common/sanitizer_atomic.h"
 
-#if defined(_WIN32) && !defined(__clang__)
-#include <intrin.h>
-#endif
-
 namespace __asan {
 
 #define REDZONE ((uptr)(flags()->redzone))
@@ -58,42 +55,6 @@ static const uptr kMallocSizeClassStep = 1UL << kMallocSizeClassStepLog;
 static const uptr kMaxAllowedMallocSize =
     (SANITIZER_WORDSIZE == 32) ? 3UL << 30 : 8UL << 30;
 
-static inline bool IsAligned(uptr a, uptr alignment) {
-  return (a & (alignment - 1)) == 0;
-}
-
-static inline uptr Log2(uptr x) {
-  CHECK(IsPowerOfTwo(x));
-#if !defined(_WIN32) || defined(__clang__)
-  return __builtin_ctzl(x);
-#elif defined(_WIN64)
-  unsigned long ret;  // NOLINT
-  _BitScanForward64(&ret, x);
-  return ret;
-#else
-  unsigned long ret;  // NOLINT
-  _BitScanForward(&ret, x);
-  return ret;
-#endif
-}
-
-static inline uptr RoundUpToPowerOfTwo(uptr size) {
-  CHECK(size);
-  if (IsPowerOfTwo(size)) return size;
-
-  unsigned long up;  // NOLINT
-#if !defined(_WIN32) || defined(__clang__)
-  up = SANITIZER_WORDSIZE - 1 - __builtin_clzl(size);
-#elif defined(_WIN64)
-  _BitScanReverse64(&up, size);
-#else
-  _BitScanReverse(&up, size);
-#endif
-  CHECK(size < (1ULL << (up + 1)));
-  CHECK(size > (1ULL << up));
-  return 1UL << (up + 1);
-}
-
 static inline uptr SizeClassToSize(u8 size_class) {
   CHECK(size_class < kNumberOfSizeClasses);
   if (size_class <= kMallocSizeClassStepLog) {
@@ -165,7 +126,8 @@ struct ChunkBase {
 
   // Second 8 bytes.
   uptr alignment_log : 8;
-  uptr used_size : FIRST_32_SECOND_64(32, 56);  // Size requested by the user.
+  uptr alloc_type    : 2;
+  uptr used_size : FIRST_32_SECOND_64(32, 54);  // Size requested by the user.
 
   // This field may overlap with the user area and thus should not
   // be used while the chunk is in CHUNK_ALLOCATED state.
@@ -215,33 +177,6 @@ void AsanChunkView::GetFreeStack(StackTrace *stack) {
                               chunk_->compressed_free_stack_size());
 }
 
-bool AsanChunkView::AddrIsInside(uptr addr, uptr access_size, uptr *offset) {
-  if (addr >= Beg() && (addr + access_size) <= End()) {
-    *offset = addr - Beg();
-    return true;
-  }
-  return false;
-}
-
-bool AsanChunkView::AddrIsAtLeft(uptr addr, uptr access_size, uptr *offset) {
-  if (addr < Beg()) {
-    *offset = Beg() - addr;
-    return true;
-  }
-  return false;
-}
-
-bool AsanChunkView::AddrIsAtRight(uptr addr, uptr access_size, uptr *offset) {
-  if (addr + access_size >= End()) {
-    if (addr <= End())
-      *offset = 0;
-    else
-      *offset = addr - End();
-    return true;
-  }
-  return false;
-}
-
 static AsanChunk *PtrToChunk(uptr ptr) {
   AsanChunk *m = (AsanChunk*)(ptr - REDZONE);
   if (m->chunk_state == CHUNK_MEMALIGN) {
@@ -252,34 +187,13 @@ static AsanChunk *PtrToChunk(uptr ptr) {
 
 void AsanChunkFifoList::PushList(AsanChunkFifoList *q) {
   CHECK(q->size() > 0);
-  if (last_) {
-    CHECK(first_);
-    CHECK(!last_->next);
-    last_->next = q->first_;
-    last_ = q->last_;
-  } else {
-    CHECK(!first_);
-    last_ = q->last_;
-    first_ = q->first_;
-    CHECK(first_);
-  }
-  CHECK(last_);
-  CHECK(!last_->next);
   size_ += q->size();
+  append_back(q);
   q->clear();
 }
 
 void AsanChunkFifoList::Push(AsanChunk *n) {
-  CHECK(n->next == 0);
-  if (last_) {
-    CHECK(first_);
-    CHECK(!last_->next);
-    last_->next = n;
-    last_ = n;
-  } else {
-    CHECK(!first_);
-    last_ = first_ = n;
-  }
+  push_back(n);
   size_ += n->Size();
 }
 
@@ -288,15 +202,9 @@ void AsanChunkFifoList::Push(AsanChunk *n) {
 // ago. Not sure if we can or want to do anything with this.
 AsanChunk *AsanChunkFifoList::Pop() {
   CHECK(first_);
-  AsanChunk *res = first_;
-  first_ = first_->next;
-  if (first_ == 0)
-    last_ = 0;
-  CHECK(size_ >= res->Size());
+  AsanChunk *res = front();
   size_ -= res->Size();
-  if (last_) {
-    CHECK(!last_->next);
-  }
+  pop_front();
   return res;
 }
 
@@ -588,7 +496,8 @@ AsanChunkView FindHeapChunkByAddress(uptr address) {
   return AsanChunkView(malloc_info.FindChunkByAddr(address));
 }
 
-static u8 *Allocate(uptr alignment, uptr size, StackTrace *stack) {
+static u8 *Allocate(uptr alignment, uptr size, StackTrace *stack,
+                    AllocType alloc_type) {
   __asan_init();
   CHECK(stack);
   if (size == 0) {
@@ -645,6 +554,7 @@ static u8 *Allocate(uptr alignment, uptr size, StackTrace *stack) {
   CHECK(m);
   CHECK(m->chunk_state == CHUNK_AVAILABLE);
   m->chunk_state = CHUNK_ALLOCATED;
+  m->alloc_type = alloc_type;
   m->next = 0;
   CHECK(m->Size() == size_to_allocate);
   uptr addr = (uptr)m + REDZONE;
@@ -679,7 +589,7 @@ static u8 *Allocate(uptr alignment, uptr size, StackTrace *stack) {
   return (u8*)addr;
 }
 
-static void Deallocate(u8 *ptr, StackTrace *stack) {
+static void Deallocate(u8 *ptr, StackTrace *stack, AllocType alloc_type) {
   if (!ptr) return;
   CHECK(stack);
 
@@ -700,6 +610,9 @@ static void Deallocate(u8 *ptr, StackTrace *stack) {
     ReportFreeNotMalloced((uptr)ptr, stack);
   }
   CHECK(old_chunk_state == CHUNK_ALLOCATED);
+  if (m->alloc_type != alloc_type && flags()->alloc_dealloc_mismatch)
+    ReportAllocTypeMismatch((uptr)ptr, stack,
+                            (AllocType)m->alloc_type, (AllocType)alloc_type);
   // With REDZONE==16 m->next is in the user area, otherwise it should be 0.
   CHECK(REDZONE <= 16 || !m->next);
   CHECK(m->free_tid == kInvalidTid);
@@ -744,18 +657,19 @@ static u8 *Reallocate(u8 *old_ptr, uptr new_size,
   CHECK(m->chunk_state == CHUNK_ALLOCATED);
   uptr old_size = m->used_size;
   uptr memcpy_size = Min(new_size, old_size);
-  u8 *new_ptr = Allocate(0, new_size, stack);
+  u8 *new_ptr = Allocate(0, new_size, stack, FROM_MALLOC);
   if (new_ptr) {
     CHECK(REAL(memcpy) != 0);
     REAL(memcpy)(new_ptr, old_ptr, memcpy_size);
-    Deallocate(old_ptr, stack);
+    Deallocate(old_ptr, stack, FROM_MALLOC);
   }
   return new_ptr;
 }
 
 }  // namespace __asan
 
-// Default (no-op) implementation of malloc hooks.
+#if !SANITIZER_SUPPORTS_WEAK_HOOKS
+// Provide default (no-op) implementation of malloc hooks.
 extern "C" {
 SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
 void __asan_malloc_hook(void *ptr, uptr size) {
@@ -767,53 +681,58 @@ void __asan_free_hook(void *ptr) {
   (void)ptr;
 }
 }  // extern "C"
+#endif
 
 namespace __asan {
 
+void PrintInternalAllocatorStats() {
+}
+
 SANITIZER_INTERFACE_ATTRIBUTE
-void *asan_memalign(uptr alignment, uptr size, StackTrace *stack) {
-  void *ptr = (void*)Allocate(alignment, size, stack);
-  __asan_malloc_hook(ptr, size);
+void *asan_memalign(uptr alignment, uptr size, StackTrace *stack,
+                    AllocType alloc_type) {
+  void *ptr = (void*)Allocate(alignment, size, stack, alloc_type);
+  ASAN_MALLOC_HOOK(ptr, size);
   return ptr;
 }
 
 SANITIZER_INTERFACE_ATTRIBUTE
-void asan_free(void *ptr, StackTrace *stack) {
-  __asan_free_hook(ptr);
-  Deallocate((u8*)ptr, stack);
+void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type) {
+  ASAN_FREE_HOOK(ptr);
+  Deallocate((u8*)ptr, stack, alloc_type);
 }
 
 SANITIZER_INTERFACE_ATTRIBUTE
 void *asan_malloc(uptr size, StackTrace *stack) {
-  void *ptr = (void*)Allocate(0, size, stack);
-  __asan_malloc_hook(ptr, size);
+  void *ptr = (void*)Allocate(0, size, stack, FROM_MALLOC);
+  ASAN_MALLOC_HOOK(ptr, size);
   return ptr;
 }
 
 void *asan_calloc(uptr nmemb, uptr size, StackTrace *stack) {
-  void *ptr = (void*)Allocate(0, nmemb * size, stack);
+  void *ptr = (void*)Allocate(0, nmemb * size, stack, FROM_MALLOC);
   if (ptr)
     REAL(memset)(ptr, 0, nmemb * size);
-  __asan_malloc_hook(ptr, nmemb * size);
+  ASAN_MALLOC_HOOK(ptr, size);
   return ptr;
 }
 
 void *asan_realloc(void *p, uptr size, StackTrace *stack) {
   if (p == 0) {
-    void *ptr = (void*)Allocate(0, size, stack);
-    __asan_malloc_hook(ptr, size);
+    void *ptr = (void*)Allocate(0, size, stack, FROM_MALLOC);
+    ASAN_MALLOC_HOOK(ptr, size);
     return ptr;
   } else if (size == 0) {
-    __asan_free_hook(p);
-    Deallocate((u8*)p, stack);
+    ASAN_FREE_HOOK(p);
+    Deallocate((u8*)p, stack, FROM_MALLOC);
     return 0;
   }
   return Reallocate((u8*)p, size, stack);
 }
 
 void *asan_valloc(uptr size, StackTrace *stack) {
-  void *ptr = (void*)Allocate(GetPageSizeCached(), size, stack);
-  __asan_malloc_hook(ptr, size);
+  void *ptr = (void*)Allocate(GetPageSizeCached(), size, stack, FROM_MALLOC);
+  ASAN_MALLOC_HOOK(ptr, size);
   return ptr;
 }
 
@@ -824,16 +743,16 @@ void *asan_pvalloc(uptr size, StackTrace *stack) {
     // pvalloc(0) should allocate one page.
     size = PageSize;
   }
-  void *ptr = (void*)Allocate(PageSize, size, stack);
-  __asan_malloc_hook(ptr, size);
+  void *ptr = (void*)Allocate(PageSize, size, stack, FROM_MALLOC);
+  ASAN_MALLOC_HOOK(ptr, size);
   return ptr;
 }
 
 int asan_posix_memalign(void **memptr, uptr alignment, uptr size,
                           StackTrace *stack) {
-  void *ptr = Allocate(alignment, size, stack);
+  void *ptr = Allocate(alignment, size, stack, FROM_MALLOC);
   CHECK(IsAligned((uptr)ptr, alignment));
-  __asan_malloc_hook(ptr, size);
+  ASAN_MALLOC_HOOK(ptr, size);
   *memptr = ptr;
   return 0;
 }
@@ -860,170 +779,11 @@ void asan_mz_force_unlock() {
   malloc_info.ForceUnlock();
 }
 
-// ---------------------- Fake stack-------------------- {{{1
-FakeStack::FakeStack() {
-  CHECK(REAL(memset) != 0);
-  REAL(memset)(this, 0, sizeof(*this));
-}
-
-bool FakeStack::AddrIsInSizeClass(uptr addr, uptr size_class) {
-  uptr mem = allocated_size_classes_[size_class];
-  uptr size = ClassMmapSize(size_class);
-  bool res = mem && addr >= mem && addr < mem + size;
-  return res;
-}
-
-uptr FakeStack::AddrIsInFakeStack(uptr addr) {
-  for (uptr i = 0; i < kNumberOfSizeClasses; i++) {
-    if (AddrIsInSizeClass(addr, i)) return allocated_size_classes_[i];
-  }
-  return 0;
-}
-
-// We may want to compute this during compilation.
-inline uptr FakeStack::ComputeSizeClass(uptr alloc_size) {
-  uptr rounded_size = RoundUpToPowerOfTwo(alloc_size);
-  uptr log = Log2(rounded_size);
-  CHECK(alloc_size <= (1UL << log));
-  if (!(alloc_size > (1UL << (log-1)))) {
-    Printf("alloc_size %zu log %zu\n", alloc_size, log);
-  }
-  CHECK(alloc_size > (1UL << (log-1)));
-  uptr res = log < kMinStackFrameSizeLog ? 0 : log - kMinStackFrameSizeLog;
-  CHECK(res < kNumberOfSizeClasses);
-  CHECK(ClassSize(res) >= rounded_size);
-  return res;
-}
-
-void FakeFrameFifo::FifoPush(FakeFrame *node) {
-  CHECK(node);
-  node->next = 0;
-  if (first_ == 0 && last_ == 0) {
-    first_ = last_ = node;
-  } else {
-    CHECK(first_);
-    CHECK(last_);
-    last_->next = node;
-    last_ = node;
-  }
-}
-
-FakeFrame *FakeFrameFifo::FifoPop() {
-  CHECK(first_ && last_ && "Exhausted fake stack");
-  FakeFrame *res = 0;
-  if (first_ == last_) {
-    res = first_;
-    first_ = last_ = 0;
-  } else {
-    res = first_;
-    first_ = first_->next;
-  }
-  return res;
-}
-
-void FakeStack::Init(uptr stack_size) {
-  stack_size_ = stack_size;
-  alive_ = true;
-}
-
-void FakeStack::Cleanup() {
-  alive_ = false;
-  for (uptr i = 0; i < kNumberOfSizeClasses; i++) {
-    uptr mem = allocated_size_classes_[i];
-    if (mem) {
-      PoisonShadow(mem, ClassMmapSize(i), 0);
-      allocated_size_classes_[i] = 0;
-      UnmapOrDie((void*)mem, ClassMmapSize(i));
-    }
-  }
-}
-
-uptr FakeStack::ClassMmapSize(uptr size_class) {
-  return RoundUpToPowerOfTwo(stack_size_);
-}
-
-void FakeStack::AllocateOneSizeClass(uptr size_class) {
-  CHECK(ClassMmapSize(size_class) >= GetPageSizeCached());
-  uptr new_mem = (uptr)MmapOrDie(
-      ClassMmapSize(size_class), __FUNCTION__);
-  // Printf("T%d new_mem[%zu]: %p-%p mmap %zu\n",
-  //       asanThreadRegistry().GetCurrent()->tid(),
-  //       size_class, new_mem, new_mem + ClassMmapSize(size_class),
-  //       ClassMmapSize(size_class));
-  uptr i;
-  for (i = 0; i < ClassMmapSize(size_class);
-       i += ClassSize(size_class)) {
-    size_classes_[size_class].FifoPush((FakeFrame*)(new_mem + i));
-  }
-  CHECK(i == ClassMmapSize(size_class));
-  allocated_size_classes_[size_class] = new_mem;
-}
-
-uptr FakeStack::AllocateStack(uptr size, uptr real_stack) {
-  if (!alive_) return real_stack;
-  CHECK(size <= kMaxStackMallocSize && size > 1);
-  uptr size_class = ComputeSizeClass(size);
-  if (!allocated_size_classes_[size_class]) {
-    AllocateOneSizeClass(size_class);
-  }
-  FakeFrame *fake_frame = size_classes_[size_class].FifoPop();
-  CHECK(fake_frame);
-  fake_frame->size_minus_one = size - 1;
-  fake_frame->real_stack = real_stack;
-  while (FakeFrame *top = call_stack_.top()) {
-    if (top->real_stack > real_stack) break;
-    call_stack_.LifoPop();
-    DeallocateFrame(top);
-  }
-  call_stack_.LifoPush(fake_frame);
-  uptr ptr = (uptr)fake_frame;
-  PoisonShadow(ptr, size, 0);
-  return ptr;
-}
-
-void FakeStack::DeallocateFrame(FakeFrame *fake_frame) {
-  CHECK(alive_);
-  uptr size = fake_frame->size_minus_one + 1;
-  uptr size_class = ComputeSizeClass(size);
-  CHECK(allocated_size_classes_[size_class]);
-  uptr ptr = (uptr)fake_frame;
-  CHECK(AddrIsInSizeClass(ptr, size_class));
-  CHECK(AddrIsInSizeClass(ptr + size - 1, size_class));
-  size_classes_[size_class].FifoPush(fake_frame);
-}
-
-void FakeStack::OnFree(uptr ptr, uptr size, uptr real_stack) {
-  FakeFrame *fake_frame = (FakeFrame*)ptr;
-  CHECK(fake_frame->magic = kRetiredStackFrameMagic);
-  CHECK(fake_frame->descr != 0);
-  CHECK(fake_frame->size_minus_one == size - 1);
-  PoisonShadow(ptr, size, kAsanStackAfterReturnMagic);
-}
-
 }  // namespace __asan
 
 // ---------------------- Interface ---------------- {{{1
 using namespace __asan;  // NOLINT
 
-uptr __asan_stack_malloc(uptr size, uptr real_stack) {
-  if (!flags()->use_fake_stack) return real_stack;
-  AsanThread *t = asanThreadRegistry().GetCurrent();
-  if (!t) {
-    // TSD is gone, use the real stack.
-    return real_stack;
-  }
-  uptr ptr = t->fake_stack().AllocateStack(size, real_stack);
-  // Printf("__asan_stack_malloc %p %zu %p\n", ptr, size, real_stack);
-  return ptr;
-}
-
-void __asan_stack_free(uptr ptr, uptr size, uptr real_stack) {
-  if (!flags()->use_fake_stack) return;
-  if (ptr != real_stack) {
-    FakeStack::OnFree(ptr, size, real_stack);
-  }
-}
-
 // ASan allocator doesn't reserve extra bytes, so normally we would
 // just return "size".
 uptr __asan_get_estimated_allocated_size(uptr size) {
@@ -1040,8 +800,9 @@ uptr __asan_get_allocated_size(const void *p) {
   uptr allocated_size = malloc_info.AllocationSize((uptr)p);
   // Die if p is not malloced or if it is already freed.
   if (allocated_size == 0) {
-    GET_STACK_TRACE_HERE(kStackTraceMax);
+    GET_STACK_TRACE_FATAL_HERE;
     ReportAsanGetAllocatedSizeNotOwned((uptr)p, &stack);
   }
   return allocated_size;
 }
+#endif  // ASAN_ALLOCATOR_VERSION
index 372ca0635ed43db9bb00b56be305ca6560b7f51b..4ade352a3e57d818b18bef736803182a839dc006 100644 (file)
 
 #include "asan_internal.h"
 #include "asan_interceptors.h"
+#include "sanitizer_common/sanitizer_list.h"
+
+// We are in the process of transitioning from the old allocator (version 1)
+// to a new one (version 2). The change is quite intrusive so both allocators
+// will co-exist in the source base for a while. The actual allocator is chosen
+// at build time by redefining this macrozz.
+#define ASAN_ALLOCATOR_VERSION 1
 
 namespace __asan {
 
+enum AllocType {
+  FROM_MALLOC = 1,  // Memory block came from malloc, calloc, realloc, etc.
+  FROM_NEW = 2,     // Memory block came from operator new.
+  FROM_NEW_BR = 3   // Memory block came from operator new [ ]
+};
+
 static const uptr kNumberOfSizeClasses = 255;
 struct AsanChunk;
 
@@ -32,16 +45,40 @@ class AsanChunkView {
   uptr FreeTid();
   void GetAllocStack(StackTrace *stack);
   void GetFreeStack(StackTrace *stack);
-  bool AddrIsInside(uptr addr, uptr access_size, uptr *offset);
-  bool AddrIsAtLeft(uptr addr, uptr access_size, uptr *offset);
-  bool AddrIsAtRight(uptr addr, uptr access_size, uptr *offset);
+  bool AddrIsInside(uptr addr, uptr access_size, uptr *offset) {
+    if (addr >= Beg() && (addr + access_size) <= End()) {
+      *offset = addr - Beg();
+      return true;
+    }
+    return false;
+  }
+  bool AddrIsAtLeft(uptr addr, uptr access_size, uptr *offset) {
+    (void)access_size;
+    if (addr < Beg()) {
+      *offset = Beg() - addr;
+      return true;
+    }
+    return false;
+  }
+  bool AddrIsAtRight(uptr addr, uptr access_size, uptr *offset) {
+    if (addr + access_size >= End()) {
+      if (addr <= End())
+        *offset = 0;
+      else
+        *offset = addr - End();
+      return true;
+    }
+    return false;
+  }
+
  private:
   AsanChunk *const chunk_;
 };
 
 AsanChunkView FindHeapChunkByAddress(uptr address);
 
-class AsanChunkFifoList {
+// List of AsanChunks with total size.
+class AsanChunkFifoList: public IntrusiveList<AsanChunk> {
  public:
   explicit AsanChunkFifoList(LinkerInitialized) { }
   AsanChunkFifoList() { clear(); }
@@ -50,12 +87,10 @@ class AsanChunkFifoList {
   AsanChunk *Pop();
   uptr size() { return size_; }
   void clear() {
-    first_ = last_ = 0;
+    IntrusiveList<AsanChunk>::clear();
     size_ = 0;
   }
  private:
-  AsanChunk *first_;
-  AsanChunk *last_;
   uptr size_;
 };
 
@@ -68,7 +103,11 @@ struct AsanThreadLocalMallocStorage {
   }
 
   AsanChunkFifoList quarantine_;
+#if ASAN_ALLOCATOR_VERSION == 1
   AsanChunk *free_lists_[kNumberOfSizeClasses];
+#else
+  uptr allocator2_cache[1024];  // Opaque.
+#endif
   void CommitBack();
 };
 
@@ -156,8 +195,9 @@ class FakeStack {
   FakeFrameLifo call_stack_;
 };
 
-void *asan_memalign(uptr alignment, uptr size, StackTrace *stack);
-void asan_free(void *ptr, StackTrace *stack);
+void *asan_memalign(uptr alignment, uptr size, StackTrace *stack,
+                    AllocType alloc_type);
+void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type);
 
 void *asan_malloc(uptr size, StackTrace *stack);
 void *asan_calloc(uptr nmemb, uptr size, StackTrace *stack);
@@ -173,5 +213,52 @@ uptr asan_mz_size(const void *ptr);
 void asan_mz_force_lock();
 void asan_mz_force_unlock();
 
+void PrintInternalAllocatorStats();
+
+// Log2 and RoundUpToPowerOfTwo should be inlined for performance.
+#if defined(_WIN32) && !defined(__clang__)
+extern "C" {
+unsigned char _BitScanForward(unsigned long *index, unsigned long mask);  // NOLINT
+unsigned char _BitScanReverse(unsigned long *index, unsigned long mask);  // NOLINT
+#if defined(_WIN64)
+unsigned char _BitScanForward64(unsigned long *index, unsigned __int64 mask);  // NOLINT
+unsigned char _BitScanReverse64(unsigned long *index, unsigned __int64 mask);  // NOLINT
+#endif
+}
+#endif
+
+static inline uptr Log2(uptr x) {
+  CHECK(IsPowerOfTwo(x));
+#if !defined(_WIN32) || defined(__clang__)
+  return __builtin_ctzl(x);
+#elif defined(_WIN64)
+  unsigned long ret;  // NOLINT
+  _BitScanForward64(&ret, x);
+  return ret;
+#else
+  unsigned long ret;  // NOLINT
+  _BitScanForward(&ret, x);
+  return ret;
+#endif
+}
+
+static inline uptr RoundUpToPowerOfTwo(uptr size) {
+  CHECK(size);
+  if (IsPowerOfTwo(size)) return size;
+
+  unsigned long up;  // NOLINT
+#if !defined(_WIN32) || defined(__clang__)
+  up = SANITIZER_WORDSIZE - 1 - __builtin_clzl(size);
+#elif defined(_WIN64)
+  _BitScanReverse64(&up, size);
+#else
+  _BitScanReverse(&up, size);
+#endif
+  CHECK(size < (1ULL << (up + 1)));
+  CHECK(size > (1ULL << up));
+  return 1UL << (up + 1);
+}
+
+
 }  // namespace __asan
 #endif  // ASAN_ALLOCATOR_H
diff --git a/libsanitizer/asan/asan_allocator2.cc b/libsanitizer/asan/asan_allocator2.cc
new file mode 100644 (file)
index 0000000..d12ccb7
--- /dev/null
@@ -0,0 +1,714 @@
+//===-- asan_allocator2.cc ------------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Implementation of ASan's memory allocator, 2-nd version.
+// This variant uses the allocator from sanitizer_common, i.e. the one shared
+// with ThreadSanitizer and MemorySanitizer.
+//
+// Status: under development, not enabled by default yet.
+//===----------------------------------------------------------------------===//
+#include "asan_allocator.h"
+#if ASAN_ALLOCATOR_VERSION == 2
+
+#include "asan_mapping.h"
+#include "asan_report.h"
+#include "asan_thread.h"
+#include "asan_thread_registry.h"
+#include "sanitizer/asan_interface.h"
+#include "sanitizer_common/sanitizer_allocator.h"
+#include "sanitizer_common/sanitizer_internal_defs.h"
+#include "sanitizer_common/sanitizer_list.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
+
+namespace __asan {
+
+struct AsanMapUnmapCallback {
+  void OnMap(uptr p, uptr size) const {
+    PoisonShadow(p, size, kAsanHeapLeftRedzoneMagic);
+    // Statistics.
+    AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats();
+    thread_stats.mmaps++;
+    thread_stats.mmaped += size;
+  }
+  void OnUnmap(uptr p, uptr size) const {
+    PoisonShadow(p, size, 0);
+    // We are about to unmap a chunk of user memory.
+    // Mark the corresponding shadow memory as not needed.
+    // Since asan's mapping is compacting, the shadow chunk may be
+    // not page-aligned, so we only flush the page-aligned portion.
+    uptr page_size = GetPageSizeCached();
+    uptr shadow_beg = RoundUpTo(MemToShadow(p), page_size);
+    uptr shadow_end = RoundDownTo(MemToShadow(p + size), page_size);
+    FlushUnneededShadowMemory(shadow_beg, shadow_end - shadow_beg);
+    // Statistics.
+    AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats();
+    thread_stats.munmaps++;
+    thread_stats.munmaped += size;
+  }
+};
+
+#if SANITIZER_WORDSIZE == 64
+const uptr kAllocatorSpace = 0x600000000000ULL;
+const uptr kAllocatorSize  =  0x10000000000ULL;  // 1T.
+typedef DefaultSizeClassMap SizeClassMap;
+typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, 0 /*metadata*/,
+    SizeClassMap, AsanMapUnmapCallback> PrimaryAllocator;
+#elif SANITIZER_WORDSIZE == 32
+static const u64 kAddressSpaceSize = 1ULL << 32;
+typedef CompactSizeClassMap SizeClassMap;
+typedef SizeClassAllocator32<0, kAddressSpaceSize, 16,
+  SizeClassMap, AsanMapUnmapCallback> PrimaryAllocator;
+#endif
+
+typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache;
+typedef LargeMmapAllocator<AsanMapUnmapCallback> SecondaryAllocator;
+typedef CombinedAllocator<PrimaryAllocator, AllocatorCache,
+    SecondaryAllocator> Allocator;
+
+// We can not use THREADLOCAL because it is not supported on some of the
+// platforms we care about (OSX 10.6, Android).
+// static THREADLOCAL AllocatorCache cache;
+AllocatorCache *GetAllocatorCache(AsanThreadLocalMallocStorage *ms) {
+  CHECK(ms);
+  CHECK_LE(sizeof(AllocatorCache), sizeof(ms->allocator2_cache));
+  return reinterpret_cast<AllocatorCache *>(ms->allocator2_cache);
+}
+
+static Allocator allocator;
+
+static const uptr kMaxAllowedMallocSize =
+  FIRST_32_SECOND_64(3UL << 30, 8UL << 30);
+
+static const uptr kMaxThreadLocalQuarantine =
+  FIRST_32_SECOND_64(1 << 18, 1 << 20);
+
+static const uptr kReturnOnZeroMalloc = 2048;  // Zero page is protected.
+
+static int inited = 0;
+
+static void Init() {
+  if (inited) return;
+  __asan_init();
+  inited = true;  // this must happen before any threads are created.
+  allocator.Init();
+}
+
+// Every chunk of memory allocated by this allocator can be in one of 3 states:
+// CHUNK_AVAILABLE: the chunk is in the free list and ready to be allocated.
+// CHUNK_ALLOCATED: the chunk is allocated and not yet freed.
+// CHUNK_QUARANTINE: the chunk was freed and put into quarantine zone.
+enum {
+  CHUNK_AVAILABLE  = 0,  // 0 is the default value even if we didn't set it.
+  CHUNK_ALLOCATED  = 2,
+  CHUNK_QUARANTINE = 3
+};
+
+// Valid redzone sizes are 16, 32, 64, ... 2048, so we encode them in 3 bits.
+// We use adaptive redzones: for larger allocation larger redzones are used.
+static u32 RZLog2Size(u32 rz_log) {
+  CHECK_LT(rz_log, 8);
+  return 16 << rz_log;
+}
+
+static u32 RZSize2Log(u32 rz_size) {
+  CHECK_GE(rz_size, 16);
+  CHECK_LE(rz_size, 2048);
+  CHECK(IsPowerOfTwo(rz_size));
+  u32 res = __builtin_ctz(rz_size) - 4;
+  CHECK_EQ(rz_size, RZLog2Size(res));
+  return res;
+}
+
+static uptr ComputeRZLog(uptr user_requested_size) {
+  u32 rz_log =
+    user_requested_size <= 64        - 16   ? 0 :
+    user_requested_size <= 128       - 32   ? 1 :
+    user_requested_size <= 512       - 64   ? 2 :
+    user_requested_size <= 4096      - 128  ? 3 :
+    user_requested_size <= (1 << 14) - 256  ? 4 :
+    user_requested_size <= (1 << 15) - 512  ? 5 :
+    user_requested_size <= (1 << 16) - 1024 ? 6 : 7;
+  return Max(rz_log, RZSize2Log(flags()->redzone));
+}
+
+// The memory chunk allocated from the underlying allocator looks like this:
+// L L L L L L H H U U U U U U R R
+//   L -- left redzone words (0 or more bytes)
+//   H -- ChunkHeader (16 bytes), which is also a part of the left redzone.
+//   U -- user memory.
+//   R -- right redzone (0 or more bytes)
+// ChunkBase consists of ChunkHeader and other bytes that overlap with user
+// memory.
+
+// If a memory chunk is allocated by memalign and we had to increase the
+// allocation size to achieve the proper alignment, then we store this magic
+// value in the first uptr word of the memory block and store the address of
+// ChunkBase in the next uptr.
+// M B ? ? ? L L L L L L  H H U U U U U U
+//   M -- magic value kMemalignMagic
+//   B -- address of ChunkHeader pointing to the first 'H'
+static const uptr kMemalignMagic = 0xCC6E96B9;
+
+struct ChunkHeader {
+  // 1-st 8 bytes.
+  u32 chunk_state       : 8;  // Must be first.
+  u32 alloc_tid         : 24;
+
+  u32 free_tid          : 24;
+  u32 from_memalign     : 1;
+  u32 alloc_type        : 2;
+  u32 rz_log            : 3;
+  // 2-nd 8 bytes
+  // This field is used for small sizes. For large sizes it is equal to
+  // SizeClassMap::kMaxSize and the actual size is stored in the
+  // SecondaryAllocator's metadata.
+  u32 user_requested_size;
+  u32 alloc_context_id;
+};
+
+struct ChunkBase : ChunkHeader {
+  // Header2, intersects with user memory.
+  AsanChunk *next;
+  u32 free_context_id;
+};
+
+static const uptr kChunkHeaderSize = sizeof(ChunkHeader);
+static const uptr kChunkHeader2Size = sizeof(ChunkBase) - kChunkHeaderSize;
+COMPILER_CHECK(kChunkHeaderSize == 16);
+COMPILER_CHECK(kChunkHeader2Size <= 16);
+
+struct AsanChunk: ChunkBase {
+  uptr Beg() { return reinterpret_cast<uptr>(this) + kChunkHeaderSize; }
+  uptr UsedSize() {
+    if (user_requested_size != SizeClassMap::kMaxSize)
+      return user_requested_size;
+    return *reinterpret_cast<uptr *>(allocator.GetMetaData(AllocBeg()));
+  }
+  void *AllocBeg() {
+    if (from_memalign)
+      return allocator.GetBlockBegin(reinterpret_cast<void *>(this));
+    return reinterpret_cast<void*>(Beg() - RZLog2Size(rz_log));
+  }
+  // We store the alloc/free stack traces in the chunk itself.
+  u32 *AllocStackBeg() {
+    return (u32*)(Beg() - RZLog2Size(rz_log));
+  }
+  uptr AllocStackSize() {
+    CHECK_LE(RZLog2Size(rz_log), kChunkHeaderSize);
+    return (RZLog2Size(rz_log) - kChunkHeaderSize) / sizeof(u32);
+  }
+  u32 *FreeStackBeg() {
+    return (u32*)(Beg() + kChunkHeader2Size);
+  }
+  uptr FreeStackSize() {
+    if (user_requested_size < kChunkHeader2Size) return 0;
+    uptr available = RoundUpTo(user_requested_size, SHADOW_GRANULARITY);
+    return (available - kChunkHeader2Size) / sizeof(u32);
+  }
+};
+
+uptr AsanChunkView::Beg() { return chunk_->Beg(); }
+uptr AsanChunkView::End() { return Beg() + UsedSize(); }
+uptr AsanChunkView::UsedSize() { return chunk_->UsedSize(); }
+uptr AsanChunkView::AllocTid() { return chunk_->alloc_tid; }
+uptr AsanChunkView::FreeTid() { return chunk_->free_tid; }
+
+static void GetStackTraceFromId(u32 id, StackTrace *stack) {
+  CHECK(id);
+  uptr size = 0;
+  const uptr *trace = StackDepotGet(id, &size);
+  CHECK_LT(size, kStackTraceMax);
+  internal_memcpy(stack->trace, trace, sizeof(uptr) * size);
+  stack->size = size;
+}
+
+void AsanChunkView::GetAllocStack(StackTrace *stack) {
+  if (flags()->use_stack_depot)
+    GetStackTraceFromId(chunk_->alloc_context_id, stack);
+  else
+    StackTrace::UncompressStack(stack, chunk_->AllocStackBeg(),
+                                chunk_->AllocStackSize());
+}
+
+void AsanChunkView::GetFreeStack(StackTrace *stack) {
+  if (flags()->use_stack_depot)
+    GetStackTraceFromId(chunk_->free_context_id, stack);
+  else
+    StackTrace::UncompressStack(stack, chunk_->FreeStackBeg(),
+                                chunk_->FreeStackSize());
+}
+
+class Quarantine: public AsanChunkFifoList {
+ public:
+  void SwallowThreadLocalQuarantine(AsanThreadLocalMallocStorage *ms) {
+    AsanChunkFifoList *q = &ms->quarantine_;
+    if (!q->size()) return;
+    SpinMutexLock l(&mutex_);
+    PushList(q);
+    PopAndDeallocateLoop(ms);
+  }
+
+  void BypassThreadLocalQuarantine(AsanChunk *m) {
+    SpinMutexLock l(&mutex_);
+    Push(m);
+  }
+
+ private:
+  void PopAndDeallocateLoop(AsanThreadLocalMallocStorage *ms) {
+    while (size() > (uptr)flags()->quarantine_size) {
+      PopAndDeallocate(ms);
+    }
+  }
+  void PopAndDeallocate(AsanThreadLocalMallocStorage *ms) {
+    CHECK_GT(size(), 0);
+    AsanChunk *m = Pop();
+    CHECK(m);
+    CHECK(m->chunk_state == CHUNK_QUARANTINE);
+    m->chunk_state = CHUNK_AVAILABLE;
+    CHECK_NE(m->alloc_tid, kInvalidTid);
+    CHECK_NE(m->free_tid, kInvalidTid);
+    PoisonShadow(m->Beg(),
+                 RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY),
+                 kAsanHeapLeftRedzoneMagic);
+    void *p = reinterpret_cast<void *>(m->AllocBeg());
+    if (m->from_memalign) {
+      uptr *memalign_magic = reinterpret_cast<uptr *>(p);
+      CHECK_EQ(memalign_magic[0], kMemalignMagic);
+      CHECK_EQ(memalign_magic[1], reinterpret_cast<uptr>(m));
+    }
+
+    // Statistics.
+    AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats();
+    thread_stats.real_frees++;
+    thread_stats.really_freed += m->UsedSize();
+
+    allocator.Deallocate(GetAllocatorCache(ms), p);
+  }
+  SpinMutex mutex_;
+};
+
+static Quarantine quarantine;
+
+void AsanChunkFifoList::PushList(AsanChunkFifoList *q) {
+  CHECK(q->size() > 0);
+  size_ += q->size();
+  append_back(q);
+  q->clear();
+}
+
+void AsanChunkFifoList::Push(AsanChunk *n) {
+  push_back(n);
+  size_ += n->UsedSize();
+}
+
+// Interesting performance observation: this function takes up to 15% of overal
+// allocator time. That's because *first_ has been evicted from cache long time
+// ago. Not sure if we can or want to do anything with this.
+AsanChunk *AsanChunkFifoList::Pop() {
+  CHECK(first_);
+  AsanChunk *res = front();
+  size_ -= res->UsedSize();
+  pop_front();
+  return res;
+}
+
+static void *Allocate(uptr size, uptr alignment, StackTrace *stack,
+                      AllocType alloc_type) {
+  Init();
+  CHECK(stack);
+  const uptr min_alignment = SHADOW_GRANULARITY;
+  if (alignment < min_alignment)
+    alignment = min_alignment;
+  if (size == 0) {
+    if (alignment <= kReturnOnZeroMalloc)
+      return reinterpret_cast<void *>(kReturnOnZeroMalloc);
+    else
+      return 0;  // 0 bytes with large alignment requested. Just return 0.
+  }
+  CHECK(IsPowerOfTwo(alignment));
+  uptr rz_log = ComputeRZLog(size);
+  uptr rz_size = RZLog2Size(rz_log);
+  uptr rounded_size = RoundUpTo(size, alignment);
+  if (rounded_size < kChunkHeader2Size)
+    rounded_size = kChunkHeader2Size;
+  uptr needed_size = rounded_size + rz_size;
+  if (alignment > min_alignment)
+    needed_size += alignment;
+  bool using_primary_allocator = true;
+  // If we are allocating from the secondary allocator, there will be no
+  // automatic right redzone, so add the right redzone manually.
+  if (!PrimaryAllocator::CanAllocate(needed_size, alignment)) {
+    needed_size += rz_size;
+    using_primary_allocator = false;
+  }
+  CHECK(IsAligned(needed_size, min_alignment));
+  if (size > kMaxAllowedMallocSize || needed_size > kMaxAllowedMallocSize) {
+    Report("WARNING: AddressSanitizer failed to allocate %p bytes\n",
+           (void*)size);
+    return 0;
+  }
+
+  AsanThread *t = asanThreadRegistry().GetCurrent();
+  AllocatorCache *cache = t ? GetAllocatorCache(&t->malloc_storage()) : 0;
+  void *allocated = allocator.Allocate(cache, needed_size, 8, false);
+  uptr alloc_beg = reinterpret_cast<uptr>(allocated);
+  uptr alloc_end = alloc_beg + needed_size;
+  uptr beg_plus_redzone = alloc_beg + rz_size;
+  uptr user_beg = beg_plus_redzone;
+  if (!IsAligned(user_beg, alignment))
+    user_beg = RoundUpTo(user_beg, alignment);
+  uptr user_end = user_beg + size;
+  CHECK_LE(user_end, alloc_end);
+  uptr chunk_beg = user_beg - kChunkHeaderSize;
+  AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg);
+  m->chunk_state = CHUNK_ALLOCATED;
+  m->alloc_type = alloc_type;
+  m->rz_log = rz_log;
+  u32 alloc_tid = t ? t->tid() : 0;
+  m->alloc_tid = alloc_tid;
+  CHECK_EQ(alloc_tid, m->alloc_tid);  // Does alloc_tid fit into the bitfield?
+  m->free_tid = kInvalidTid;
+  m->from_memalign = user_beg != beg_plus_redzone;
+  if (m->from_memalign) {
+    CHECK_LE(beg_plus_redzone + 2 * sizeof(uptr), user_beg);
+    uptr *memalign_magic = reinterpret_cast<uptr *>(alloc_beg);
+    memalign_magic[0] = kMemalignMagic;
+    memalign_magic[1] = chunk_beg;
+  }
+  if (using_primary_allocator) {
+    CHECK(size);
+    m->user_requested_size = size;
+    CHECK(allocator.FromPrimary(allocated));
+  } else {
+    CHECK(!allocator.FromPrimary(allocated));
+    m->user_requested_size = SizeClassMap::kMaxSize;
+    uptr *meta = reinterpret_cast<uptr *>(allocator.GetMetaData(allocated));
+    meta[0] = size;
+    meta[1] = chunk_beg;
+  }
+
+  if (flags()->use_stack_depot) {
+    m->alloc_context_id = StackDepotPut(stack->trace, stack->size);
+  } else {
+    m->alloc_context_id = 0;
+    StackTrace::CompressStack(stack, m->AllocStackBeg(), m->AllocStackSize());
+  }
+
+  uptr size_rounded_down_to_granularity = RoundDownTo(size, SHADOW_GRANULARITY);
+  // Unpoison the bulk of the memory region.
+  if (size_rounded_down_to_granularity)
+    PoisonShadow(user_beg, size_rounded_down_to_granularity, 0);
+  // Deal with the end of the region if size is not aligned to granularity.
+  if (size != size_rounded_down_to_granularity && flags()->poison_heap) {
+    u8 *shadow = (u8*)MemToShadow(user_beg + size_rounded_down_to_granularity);
+    *shadow = size & (SHADOW_GRANULARITY - 1);
+  }
+
+  AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats();
+  thread_stats.mallocs++;
+  thread_stats.malloced += size;
+  thread_stats.malloced_redzones += needed_size - size;
+  uptr class_id = Min(kNumberOfSizeClasses, SizeClassMap::ClassID(needed_size));
+  thread_stats.malloced_by_size[class_id]++;
+  if (needed_size > SizeClassMap::kMaxSize)
+    thread_stats.malloc_large++;
+
+  void *res = reinterpret_cast<void *>(user_beg);
+  ASAN_MALLOC_HOOK(res, size);
+  return res;
+}
+
+static void Deallocate(void *ptr, StackTrace *stack, AllocType alloc_type) {
+  uptr p = reinterpret_cast<uptr>(ptr);
+  if (p == 0 || p == kReturnOnZeroMalloc) return;
+  uptr chunk_beg = p - kChunkHeaderSize;
+  AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg);
+
+  // Flip the chunk_state atomically to avoid race on double-free.
+  u8 old_chunk_state = atomic_exchange((atomic_uint8_t*)m, CHUNK_QUARANTINE,
+                                       memory_order_acq_rel);
+
+  if (old_chunk_state == CHUNK_QUARANTINE)
+    ReportDoubleFree((uptr)ptr, stack);
+  else if (old_chunk_state != CHUNK_ALLOCATED)
+    ReportFreeNotMalloced((uptr)ptr, stack);
+  CHECK(old_chunk_state == CHUNK_ALLOCATED);
+  if (m->alloc_type != alloc_type && flags()->alloc_dealloc_mismatch)
+    ReportAllocTypeMismatch((uptr)ptr, stack,
+                            (AllocType)m->alloc_type, (AllocType)alloc_type);
+
+  CHECK_GE(m->alloc_tid, 0);
+  if (SANITIZER_WORDSIZE == 64)  // On 32-bits this resides in user area.
+    CHECK_EQ(m->free_tid, kInvalidTid);
+  AsanThread *t = asanThreadRegistry().GetCurrent();
+  m->free_tid = t ? t->tid() : 0;
+  if (flags()->use_stack_depot) {
+    m->free_context_id = StackDepotPut(stack->trace, stack->size);
+  } else {
+    m->free_context_id = 0;
+    StackTrace::CompressStack(stack, m->FreeStackBeg(), m->FreeStackSize());
+  }
+  CHECK(m->chunk_state == CHUNK_QUARANTINE);
+  // Poison the region.
+  PoisonShadow(m->Beg(),
+               RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY),
+               kAsanHeapFreeMagic);
+
+  AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats();
+  thread_stats.frees++;
+  thread_stats.freed += m->UsedSize();
+
+  // Push into quarantine.
+  if (t) {
+    AsanChunkFifoList &q = t->malloc_storage().quarantine_;
+    q.Push(m);
+
+    if (q.size() > kMaxThreadLocalQuarantine)
+      quarantine.SwallowThreadLocalQuarantine(&t->malloc_storage());
+  } else {
+    quarantine.BypassThreadLocalQuarantine(m);
+  }
+
+  ASAN_FREE_HOOK(ptr);
+}
+
+static void *Reallocate(void *old_ptr, uptr new_size, StackTrace *stack) {
+  CHECK(old_ptr && new_size);
+  uptr p = reinterpret_cast<uptr>(old_ptr);
+  uptr chunk_beg = p - kChunkHeaderSize;
+  AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg);
+
+  AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats();
+  thread_stats.reallocs++;
+  thread_stats.realloced += new_size;
+
+  CHECK(m->chunk_state == CHUNK_ALLOCATED);
+  uptr old_size = m->UsedSize();
+  uptr memcpy_size = Min(new_size, old_size);
+  void *new_ptr = Allocate(new_size, 8, stack, FROM_MALLOC);
+  if (new_ptr) {
+    CHECK(REAL(memcpy) != 0);
+    REAL(memcpy)(new_ptr, old_ptr, memcpy_size);
+    Deallocate(old_ptr, stack, FROM_MALLOC);
+  }
+  return new_ptr;
+}
+
+static AsanChunk *GetAsanChunkByAddr(uptr p) {
+  void *ptr = reinterpret_cast<void *>(p);
+  uptr alloc_beg = reinterpret_cast<uptr>(allocator.GetBlockBegin(ptr));
+  if (!alloc_beg) return 0;
+  uptr *memalign_magic = reinterpret_cast<uptr *>(alloc_beg);
+  if (memalign_magic[0] == kMemalignMagic) {
+    AsanChunk *m = reinterpret_cast<AsanChunk *>(memalign_magic[1]);
+    CHECK(m->from_memalign);
+    return m;
+  }
+  if (!allocator.FromPrimary(ptr)) {
+    uptr *meta = reinterpret_cast<uptr *>(
+        allocator.GetMetaData(reinterpret_cast<void *>(alloc_beg)));
+    AsanChunk *m = reinterpret_cast<AsanChunk *>(meta[1]);
+    return m;
+  }
+  uptr actual_size = allocator.GetActuallyAllocatedSize(ptr);
+  CHECK_LE(actual_size, SizeClassMap::kMaxSize);
+  // We know the actually allocted size, but we don't know the redzone size.
+  // Just try all possible redzone sizes.
+  for (u32 rz_log = 0; rz_log < 8; rz_log++) {
+    u32 rz_size = RZLog2Size(rz_log);
+    uptr max_possible_size = actual_size - rz_size;
+    if (ComputeRZLog(max_possible_size) != rz_log)
+      continue;
+    return reinterpret_cast<AsanChunk *>(
+        alloc_beg + rz_size - kChunkHeaderSize);
+  }
+  return 0;
+}
+
+static uptr AllocationSize(uptr p) {
+  AsanChunk *m = GetAsanChunkByAddr(p);
+  if (!m) return 0;
+  if (m->chunk_state != CHUNK_ALLOCATED) return 0;
+  if (m->Beg() != p) return 0;
+  return m->UsedSize();
+}
+
+// We have an address between two chunks, and we want to report just one.
+AsanChunk *ChooseChunk(uptr addr,
+                       AsanChunk *left_chunk, AsanChunk *right_chunk) {
+  // Prefer an allocated chunk over freed chunk and freed chunk
+  // over available chunk.
+  if (left_chunk->chunk_state != right_chunk->chunk_state) {
+    if (left_chunk->chunk_state == CHUNK_ALLOCATED)
+      return left_chunk;
+    if (right_chunk->chunk_state == CHUNK_ALLOCATED)
+      return right_chunk;
+    if (left_chunk->chunk_state == CHUNK_QUARANTINE)
+      return left_chunk;
+    if (right_chunk->chunk_state == CHUNK_QUARANTINE)
+      return right_chunk;
+  }
+  // Same chunk_state: choose based on offset.
+  uptr l_offset = 0, r_offset = 0;
+  CHECK(AsanChunkView(left_chunk).AddrIsAtRight(addr, 1, &l_offset));
+  CHECK(AsanChunkView(right_chunk).AddrIsAtLeft(addr, 1, &r_offset));
+  if (l_offset < r_offset)
+    return left_chunk;
+  return right_chunk;
+}
+
+AsanChunkView FindHeapChunkByAddress(uptr addr) {
+  AsanChunk *m1 = GetAsanChunkByAddr(addr);
+  if (!m1) return AsanChunkView(m1);
+  uptr offset = 0;
+  if (AsanChunkView(m1).AddrIsAtLeft(addr, 1, &offset)) {
+    // The address is in the chunk's left redzone, so maybe it is actually
+    // a right buffer overflow from the other chunk to the left.
+    // Search a bit to the left to see if there is another chunk.
+    AsanChunk *m2 = 0;
+    for (uptr l = 1; l < GetPageSizeCached(); l++) {
+      m2 = GetAsanChunkByAddr(addr - l);
+      if (m2 == m1) continue;  // Still the same chunk.
+      break;
+    }
+    if (m2 && AsanChunkView(m2).AddrIsAtRight(addr, 1, &offset))
+      m1 = ChooseChunk(addr, m2, m1);
+  }
+  return AsanChunkView(m1);
+}
+
+void AsanThreadLocalMallocStorage::CommitBack() {
+  quarantine.SwallowThreadLocalQuarantine(this);
+  allocator.SwallowCache(GetAllocatorCache(this));
+}
+
+void PrintInternalAllocatorStats() {
+  allocator.PrintStats();
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void *asan_memalign(uptr alignment, uptr size, StackTrace *stack,
+                    AllocType alloc_type) {
+  return Allocate(size, alignment, stack, alloc_type);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type) {
+  Deallocate(ptr, stack, alloc_type);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void *asan_malloc(uptr size, StackTrace *stack) {
+  return Allocate(size, 8, stack, FROM_MALLOC);
+}
+
+void *asan_calloc(uptr nmemb, uptr size, StackTrace *stack) {
+  void *ptr = Allocate(nmemb * size, 8, stack, FROM_MALLOC);
+  if (ptr)
+    REAL(memset)(ptr, 0, nmemb * size);
+  return ptr;
+}
+
+void *asan_realloc(void *p, uptr size, StackTrace *stack) {
+  if (p == 0)
+    return Allocate(size, 8, stack, FROM_MALLOC);
+  if (size == 0) {
+    Deallocate(p, stack, FROM_MALLOC);
+    return 0;
+  }
+  return Reallocate(p, size, stack);
+}
+
+void *asan_valloc(uptr size, StackTrace *stack) {
+  return Allocate(size, GetPageSizeCached(), stack, FROM_MALLOC);
+}
+
+void *asan_pvalloc(uptr size, StackTrace *stack) {
+  uptr PageSize = GetPageSizeCached();
+  size = RoundUpTo(size, PageSize);
+  if (size == 0) {
+    // pvalloc(0) should allocate one page.
+    size = PageSize;
+  }
+  return Allocate(size, PageSize, stack, FROM_MALLOC);
+}
+
+int asan_posix_memalign(void **memptr, uptr alignment, uptr size,
+                        StackTrace *stack) {
+  void *ptr = Allocate(size, alignment, stack, FROM_MALLOC);
+  CHECK(IsAligned((uptr)ptr, alignment));
+  *memptr = ptr;
+  return 0;
+}
+
+uptr asan_malloc_usable_size(void *ptr, StackTrace *stack) {
+  CHECK(stack);
+  if (ptr == 0) return 0;
+  uptr usable_size = AllocationSize(reinterpret_cast<uptr>(ptr));
+  if (flags()->check_malloc_usable_size && (usable_size == 0))
+    ReportMallocUsableSizeNotOwned((uptr)ptr, stack);
+  return usable_size;
+}
+
+uptr asan_mz_size(const void *ptr) {
+  UNIMPLEMENTED();
+  return 0;
+}
+
+void asan_mz_force_lock() {
+  UNIMPLEMENTED();
+}
+
+void asan_mz_force_unlock() {
+  UNIMPLEMENTED();
+}
+
+}  // namespace __asan
+
+// ---------------------- Interface ---------------- {{{1
+using namespace __asan;  // NOLINT
+
+// ASan allocator doesn't reserve extra bytes, so normally we would
+// just return "size". We don't want to expose our redzone sizes, etc here.
+uptr __asan_get_estimated_allocated_size(uptr size) {
+  return size;
+}
+
+bool __asan_get_ownership(const void *p) {
+  return AllocationSize(reinterpret_cast<uptr>(p)) > 0;
+}
+
+uptr __asan_get_allocated_size(const void *p) {
+  if (p == 0) return 0;
+  uptr allocated_size = AllocationSize(reinterpret_cast<uptr>(p));
+  // Die if p is not malloced or if it is already freed.
+  if (allocated_size == 0) {
+    GET_STACK_TRACE_FATAL_HERE;
+    ReportAsanGetAllocatedSizeNotOwned(reinterpret_cast<uptr>(p), &stack);
+  }
+  return allocated_size;
+}
+
+#if !SANITIZER_SUPPORTS_WEAK_HOOKS
+// Provide default (no-op) implementation of malloc hooks.
+extern "C" {
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_malloc_hook(void *ptr, uptr size) {
+  (void)ptr;
+  (void)size;
+}
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_free_hook(void *ptr) {
+  (void)ptr;
+}
+}  // extern "C"
+#endif
+
+
+#endif  // ASAN_ALLOCATOR_VERSION
diff --git a/libsanitizer/asan/asan_fake_stack.cc b/libsanitizer/asan/asan_fake_stack.cc
new file mode 100644 (file)
index 0000000..e8d1e78
--- /dev/null
@@ -0,0 +1,180 @@
+//===-- asan_fake_stack.cc ------------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// FakeStack is used to detect use-after-return bugs.
+//===----------------------------------------------------------------------===//
+#include "asan_allocator.h"
+#include "asan_thread.h"
+#include "asan_thread_registry.h"
+#include "sanitizer/asan_interface.h"
+
+namespace __asan {
+
+FakeStack::FakeStack() {
+  CHECK(REAL(memset) != 0);
+  REAL(memset)(this, 0, sizeof(*this));
+}
+
+bool FakeStack::AddrIsInSizeClass(uptr addr, uptr size_class) {
+  uptr mem = allocated_size_classes_[size_class];
+  uptr size = ClassMmapSize(size_class);
+  bool res = mem && addr >= mem && addr < mem + size;
+  return res;
+}
+
+uptr FakeStack::AddrIsInFakeStack(uptr addr) {
+  for (uptr i = 0; i < kNumberOfSizeClasses; i++) {
+    if (AddrIsInSizeClass(addr, i)) return allocated_size_classes_[i];
+  }
+  return 0;
+}
+
+// We may want to compute this during compilation.
+inline uptr FakeStack::ComputeSizeClass(uptr alloc_size) {
+  uptr rounded_size = RoundUpToPowerOfTwo(alloc_size);
+  uptr log = Log2(rounded_size);
+  CHECK(alloc_size <= (1UL << log));
+  if (!(alloc_size > (1UL << (log-1)))) {
+    Printf("alloc_size %zu log %zu\n", alloc_size, log);
+  }
+  CHECK(alloc_size > (1UL << (log-1)));
+  uptr res = log < kMinStackFrameSizeLog ? 0 : log - kMinStackFrameSizeLog;
+  CHECK(res < kNumberOfSizeClasses);
+  CHECK(ClassSize(res) >= rounded_size);
+  return res;
+}
+
+void FakeFrameFifo::FifoPush(FakeFrame *node) {
+  CHECK(node);
+  node->next = 0;
+  if (first_ == 0 && last_ == 0) {
+    first_ = last_ = node;
+  } else {
+    CHECK(first_);
+    CHECK(last_);
+    last_->next = node;
+    last_ = node;
+  }
+}
+
+FakeFrame *FakeFrameFifo::FifoPop() {
+  CHECK(first_ && last_ && "Exhausted fake stack");
+  FakeFrame *res = 0;
+  if (first_ == last_) {
+    res = first_;
+    first_ = last_ = 0;
+  } else {
+    res = first_;
+    first_ = first_->next;
+  }
+  return res;
+}
+
+void FakeStack::Init(uptr stack_size) {
+  stack_size_ = stack_size;
+  alive_ = true;
+}
+
+void FakeStack::Cleanup() {
+  alive_ = false;
+  for (uptr i = 0; i < kNumberOfSizeClasses; i++) {
+    uptr mem = allocated_size_classes_[i];
+    if (mem) {
+      PoisonShadow(mem, ClassMmapSize(i), 0);
+      allocated_size_classes_[i] = 0;
+      UnmapOrDie((void*)mem, ClassMmapSize(i));
+    }
+  }
+}
+
+uptr FakeStack::ClassMmapSize(uptr size_class) {
+  return RoundUpToPowerOfTwo(stack_size_);
+}
+
+void FakeStack::AllocateOneSizeClass(uptr size_class) {
+  CHECK(ClassMmapSize(size_class) >= GetPageSizeCached());
+  uptr new_mem = (uptr)MmapOrDie(
+      ClassMmapSize(size_class), __FUNCTION__);
+  // Printf("T%d new_mem[%zu]: %p-%p mmap %zu\n",
+  //       asanThreadRegistry().GetCurrent()->tid(),
+  //       size_class, new_mem, new_mem + ClassMmapSize(size_class),
+  //       ClassMmapSize(size_class));
+  uptr i;
+  for (i = 0; i < ClassMmapSize(size_class);
+       i += ClassSize(size_class)) {
+    size_classes_[size_class].FifoPush((FakeFrame*)(new_mem + i));
+  }
+  CHECK(i == ClassMmapSize(size_class));
+  allocated_size_classes_[size_class] = new_mem;
+}
+
+uptr FakeStack::AllocateStack(uptr size, uptr real_stack) {
+  if (!alive_) return real_stack;
+  CHECK(size <= kMaxStackMallocSize && size > 1);
+  uptr size_class = ComputeSizeClass(size);
+  if (!allocated_size_classes_[size_class]) {
+    AllocateOneSizeClass(size_class);
+  }
+  FakeFrame *fake_frame = size_classes_[size_class].FifoPop();
+  CHECK(fake_frame);
+  fake_frame->size_minus_one = size - 1;
+  fake_frame->real_stack = real_stack;
+  while (FakeFrame *top = call_stack_.top()) {
+    if (top->real_stack > real_stack) break;
+    call_stack_.LifoPop();
+    DeallocateFrame(top);
+  }
+  call_stack_.LifoPush(fake_frame);
+  uptr ptr = (uptr)fake_frame;
+  PoisonShadow(ptr, size, 0);
+  return ptr;
+}
+
+void FakeStack::DeallocateFrame(FakeFrame *fake_frame) {
+  CHECK(alive_);
+  uptr size = fake_frame->size_minus_one + 1;
+  uptr size_class = ComputeSizeClass(size);
+  CHECK(allocated_size_classes_[size_class]);
+  uptr ptr = (uptr)fake_frame;
+  CHECK(AddrIsInSizeClass(ptr, size_class));
+  CHECK(AddrIsInSizeClass(ptr + size - 1, size_class));
+  size_classes_[size_class].FifoPush(fake_frame);
+}
+
+void FakeStack::OnFree(uptr ptr, uptr size, uptr real_stack) {
+  FakeFrame *fake_frame = (FakeFrame*)ptr;
+  CHECK(fake_frame->magic = kRetiredStackFrameMagic);
+  CHECK(fake_frame->descr != 0);
+  CHECK(fake_frame->size_minus_one == size - 1);
+  PoisonShadow(ptr, size, kAsanStackAfterReturnMagic);
+}
+
+}  // namespace __asan
+
+// ---------------------- Interface ---------------- {{{1
+using namespace __asan;  // NOLINT
+
+uptr __asan_stack_malloc(uptr size, uptr real_stack) {
+  if (!flags()->use_fake_stack) return real_stack;
+  AsanThread *t = asanThreadRegistry().GetCurrent();
+  if (!t) {
+    // TSD is gone, use the real stack.
+    return real_stack;
+  }
+  uptr ptr = t->fake_stack().AllocateStack(size, real_stack);
+  // Printf("__asan_stack_malloc %p %zu %p\n", ptr, size, real_stack);
+  return ptr;
+}
+
+void __asan_stack_free(uptr ptr, uptr size, uptr real_stack) {
+  if (!flags()->use_fake_stack) return;
+  if (ptr != real_stack) {
+    FakeStack::OnFree(ptr, size, real_stack);
+  }
+}
index a0dcf3e8a571fb11c925fff0344aac6580a30f27..49a907300843fe961a9e261537ae8dadc563c3b0 100644 (file)
@@ -43,7 +43,7 @@ struct Flags {
   int  report_globals;
   // If set, attempts to catch initialization order issues.
   bool check_initialization_order;
-  // Max number of stack frames kept for each allocation.
+  // Max number of stack frames kept for each allocation/deallocation.
   int  malloc_context_size;
   // If set, uses custom wrappers and replacements for libc string functions
   // to find more errors.
@@ -93,6 +93,17 @@ struct Flags {
   bool print_full_thread_history;
   // ASan will write logs to "log_path.pid" instead of stderr.
   const char *log_path;
+  // Use fast (frame-pointer-based) unwinder on fatal errors (if available).
+  bool fast_unwind_on_fatal;
+  // Use fast (frame-pointer-based) unwinder on malloc/free (if available).
+  bool fast_unwind_on_malloc;
+  // Poison (or not) the heap memory on [de]allocation. Zero value is useful
+  // for benchmarking the allocator or instrumentator.
+  bool poison_heap;
+  // Report errors on malloc/delete, new/free, new/delete[], etc.
+  bool alloc_dealloc_mismatch;
+  // Use stack depot instead of storing stacks in the redzones.
+  bool use_stack_depot;
 };
 
 Flags *flags();
index 89f91ad6a31f8b436b9dbbe318c5c9aefbd67c0d..8341bc65745569f801506390d88c96d9f3dbbfc0 100644 (file)
@@ -14,6 +14,7 @@
 
 #include "asan_internal.h"
 #include "interception/interception.h"
+#include "sanitizer_common/sanitizer_platform_interceptors.h"
 
 using __sanitizer::uptr;
 
@@ -39,8 +40,10 @@ using __sanitizer::uptr;
 
 #if defined(__linux__)
 # define ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX 1
+# define ASAN_INTERCEPT_PRCTL 1
 #else
 # define ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX 0
+# define ASAN_INTERCEPT_PRCTL 0
 #endif
 
 #if !defined(__APPLE__)
@@ -149,10 +152,23 @@ DECLARE_FUNCTION_AND_WRAPPER(long long, atoll, const char *nptr);  // NOLINT
 DECLARE_FUNCTION_AND_WRAPPER(long long, strtoll, const char *nptr, char **endptr, int base);  // NOLINT
 # endif
 
+// unistd.h
+# if SANITIZER_INTERCEPT_READ
+DECLARE_FUNCTION_AND_WRAPPER(SSIZE_T, read, int fd, void *buf, SIZE_T count);
+# endif
+# if SANITIZER_INTERCEPT_PREAD
+DECLARE_FUNCTION_AND_WRAPPER(SSIZE_T, pread, int fd, void *buf,
+                             SIZE_T count, OFF_T offset);
+# endif
+# if SANITIZER_INTERCEPT_PREAD64
+DECLARE_FUNCTION_AND_WRAPPER(SSIZE_T, pread64, int fd, void *buf,
+                             SIZE_T count, OFF64_T offset);
+# endif
+
 # if ASAN_INTERCEPT_MLOCKX
 // mlock/munlock
-DECLARE_FUNCTION_AND_WRAPPER(int, mlock, const void *addr, size_t len);
-DECLARE_FUNCTION_AND_WRAPPER(int, munlock, const void *addr, size_t len);
+DECLARE_FUNCTION_AND_WRAPPER(int, mlock, const void *addr, SIZE_T len);
+DECLARE_FUNCTION_AND_WRAPPER(int, munlock, const void *addr, SIZE_T len);
 DECLARE_FUNCTION_AND_WRAPPER(int, mlockall, int flags);
 DECLARE_FUNCTION_AND_WRAPPER(int, munlockall, void);
 # endif
index 5b544c87fcb85adcae8f5ecfa31c14498545ff10..26daee1727cf47a2dba3c2de4505b39b3af866be 100644 (file)
 
 namespace __asan {
 
-// Instruments read/write access to a single byte in memory.
-// On error calls __asan_report_error, which aborts the program.
-#define ACCESS_ADDRESS(address, isWrite)   do {         \
-  if (!AddrIsInMem(address) || AddressIsPoisoned(address)) {                \
-    GET_CURRENT_PC_BP_SP;                               \
-    __asan_report_error(pc, bp, sp, address, isWrite, /* access_size */ 1); \
-  } \
-} while (0)
-
 // We implement ACCESS_MEMORY_RANGE, ASAN_READ_RANGE,
 // and ASAN_WRITE_RANGE as macro instead of function so
 // that no extra frames are created, and stack trace contains
 // relevant information only.
-
-// Instruments read/write access to a memory range.
-// More complex implementation is possible, for now just
-// checking the first and the last byte of a range.
-#define ACCESS_MEMORY_RANGE(offset, size, isWrite) do { \
-  if (size > 0) { \
-    uptr ptr = (uptr)(offset); \
-    ACCESS_ADDRESS(ptr, isWrite); \
-    ACCESS_ADDRESS(ptr + (size) - 1, isWrite); \
-  } \
+// We check all shadow bytes.
+#define ACCESS_MEMORY_RANGE(offset, size, isWrite) do {                  \
+  if (uptr __ptr = __asan_region_is_poisoned((uptr)(offset), size)) {    \
+    GET_CURRENT_PC_BP_SP;                                                \
+    __asan_report_error(pc, bp, sp, __ptr, isWrite, /* access_size */1); \
+  }                                                                      \
 } while (0)
 
-#define ASAN_READ_RANGE(offset, size) do { \
-  ACCESS_MEMORY_RANGE(offset, size, false); \
-} while (0)
-
-#define ASAN_WRITE_RANGE(offset, size) do { \
-  ACCESS_MEMORY_RANGE(offset, size, true); \
-} while (0)
+#define ASAN_READ_RANGE(offset, size) ACCESS_MEMORY_RANGE(offset, size, false)
+#define ASAN_WRITE_RANGE(offset, size) ACCESS_MEMORY_RANGE(offset, size, true);
 
 // Behavior of functions like "memcpy" or "strcpy" is undefined
 // if memory intervals overlap. We report error in this case.
@@ -69,7 +51,7 @@ static inline bool RangesOverlap(const char *offset1, uptr length1,
   const char *offset1 = (const char*)_offset1; \
   const char *offset2 = (const char*)_offset2; \
   if (RangesOverlap(offset1, length1, offset2, length2)) { \
-    GET_STACK_TRACE_HERE(kStackTraceMax); \
+    GET_STACK_TRACE_FATAL_HERE; \
     ReportStringFunctionMemoryRangesOverlap(name, offset1, length1, \
                                             offset2, length2, &stack); \
   } \
@@ -96,6 +78,11 @@ static inline uptr MaybeRealStrnlen(const char *s, uptr maxlen) {
 // ---------------------- Wrappers ---------------- {{{1
 using namespace __asan;  // NOLINT
 
+#define COMMON_INTERCEPTOR_WRITE_RANGE(ptr, size) ASAN_WRITE_RANGE(ptr, size)
+#define COMMON_INTERCEPTOR_READ_RANGE(ptr, size) ASAN_READ_RANGE(ptr, size)
+#define COMMON_INTERCEPTOR_ENTER(func, ...) ENSURE_ASAN_INITED()
+#include "sanitizer_common/sanitizer_common_interceptors.h"
+
 static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) {
   AsanThread *t = (AsanThread*)arg;
   asanThreadRegistry().SetCurrent(t);
@@ -105,7 +92,7 @@ static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) {
 #if ASAN_INTERCEPT_PTHREAD_CREATE
 INTERCEPTOR(int, pthread_create, void *thread,
     void *attr, void *(*start_routine)(void*), void *arg) {
-  GET_STACK_TRACE_HERE(kStackTraceMax);
+  GET_STACK_TRACE_THREAD;
   u32 current_tid = asanThreadRegistry().GetCurrentTidOrInvalid();
   AsanThread *t = AsanThread::Create(current_tid, start_routine, arg, &stack);
   asanThreadRegistry().RegisterThread(t);
@@ -175,6 +162,25 @@ INTERCEPTOR(void, siglongjmp, void *env, int val) {
 }
 #endif
 
+#if ASAN_INTERCEPT_PRCTL
+#define PR_SET_NAME 15
+INTERCEPTOR(int, prctl, int option,
+            unsigned long arg2, unsigned long arg3,  // NOLINT
+            unsigned long arg4, unsigned long arg5) {  // NOLINT
+  int res = REAL(prctl(option, arg2, arg3, arg4, arg5));
+  if (option == PR_SET_NAME) {
+    AsanThread *t = asanThreadRegistry().GetCurrent();
+    if (t) {
+      char buff[17];
+      internal_strncpy(buff, (char*)arg2, 16);
+      buff[16] = 0;
+      t->summary()->set_name(buff);
+    }
+  }
+  return res;
+}
+#endif
+
 #if ASAN_INTERCEPT___CXA_THROW
 INTERCEPTOR(void, __cxa_throw, void *a, void *b, void *c) {
   CHECK(REAL(__cxa_throw));
@@ -256,8 +262,8 @@ INTERCEPTOR(void*, memcpy, void *to, const void *from, uptr size) {
       // See http://llvm.org/bugs/show_bug.cgi?id=11763.
       CHECK_RANGES_OVERLAP("memcpy", to, size, from, size);
     }
-    ASAN_WRITE_RANGE(from, size);
-    ASAN_READ_RANGE(to, size);
+    ASAN_READ_RANGE(from, size);
+    ASAN_WRITE_RANGE(to, size);
   }
 #if MAC_INTERPOSE_FUNCTIONS
   // Interposing of resolver functions is broken on Mac OS 10.7 and 10.8.
@@ -275,8 +281,8 @@ INTERCEPTOR(void*, memmove, void *to, const void *from, uptr size) {
   }
   ENSURE_ASAN_INITED();
   if (flags()->replace_intrin) {
-    ASAN_WRITE_RANGE(from, size);
-    ASAN_READ_RANGE(to, size);
+    ASAN_READ_RANGE(from, size);
+    ASAN_WRITE_RANGE(to, size);
   }
 #if MAC_INTERPOSE_FUNCTIONS
   // Interposing of resolver functions is broken on Mac OS 10.7 and 10.8.
@@ -621,7 +627,7 @@ INTERCEPTOR_WINAPI(DWORD, CreateThread,
                    void* security, uptr stack_size,
                    DWORD (__stdcall *start_routine)(void*), void* arg,
                    DWORD flags, void* tid) {
-  GET_STACK_TRACE_HERE(kStackTraceMax);
+  GET_STACK_TRACE_THREAD;
   u32 current_tid = asanThreadRegistry().GetCurrentTidOrInvalid();
   AsanThread *t = AsanThread::Create(current_tid, start_routine, arg, &stack);
   asanThreadRegistry().RegisterThread(t);
@@ -646,6 +652,9 @@ void InitializeAsanInterceptors() {
 #if MAC_INTERPOSE_FUNCTIONS
   return;
 #endif
+
+  SANITIZER_COMMON_INTERCEPTORS_INIT;
+
   // Intercept mem* functions.
   ASAN_INTERCEPT_FUNC(memcmp);
   ASAN_INTERCEPT_FUNC(memmove);
@@ -718,6 +727,9 @@ void InitializeAsanInterceptors() {
 #if ASAN_INTERCEPT_SIGLONGJMP
   ASAN_INTERCEPT_FUNC(siglongjmp);
 #endif
+#if ASAN_INTERCEPT_PRCTL
+  ASAN_INTERCEPT_FUNC(prctl);
+#endif
 
   // Intercept exception handling functions.
 #if ASAN_INTERCEPT___CXA_THROW
index ac1ee7de700f3d010b44f498d0b4edbc57751057..1717fce66fb5f094abce3078e94c1e1745480d47 100644 (file)
@@ -81,9 +81,9 @@
 // If set, values like allocator chunk size, as well as defaults for some flags
 // will be changed towards less memory overhead.
 #ifndef ASAN_LOW_MEMORY
-# ifdef ASAN_ANDROID
+#if SANITIZER_WORDSIZE == 32
 #  define ASAN_LOW_MEMORY 1
-# else
+#else
 #  define ASAN_LOW_MEMORY 0
 # endif
 #endif
@@ -143,6 +143,15 @@ bool PlatformHasDifferentMemcpyAndMemmove();
 # define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE true
 #endif  // __APPLE__
 
+// Add convenient macro for interface functions that may be represented as
+// weak hooks.
+#define ASAN_MALLOC_HOOK(ptr, size) \
+  if (&__asan_malloc_hook) __asan_malloc_hook(ptr, size)
+#define ASAN_FREE_HOOK(ptr) \
+  if (&__asan_free_hook) __asan_free_hook(ptr)
+#define ASAN_ON_ERROR() \
+  if (&__asan_on_error) __asan_on_error()
+
 extern int asan_inited;
 // Used to avoid infinite recursion in __asan_init().
 extern bool asan_init_is_running;
index 447999446c8927ea318e4328505d32ad2e2b014e..0e6c6280f0b553a35d97e93829b87db73ac8aa38 100644 (file)
@@ -120,53 +120,21 @@ void AsanLock::Unlock() {
   pthread_mutex_unlock((pthread_mutex_t*)&opaque_storage_);
 }
 
-#ifdef __arm__
-#define UNWIND_STOP _URC_END_OF_STACK
-#define UNWIND_CONTINUE _URC_NO_REASON
-#else
-#define UNWIND_STOP _URC_NORMAL_STOP
-#define UNWIND_CONTINUE _URC_NO_REASON
-#endif
-
-uptr Unwind_GetIP(struct _Unwind_Context *ctx) {
-#ifdef __arm__
-  uptr val;
-  _Unwind_VRS_Result res = _Unwind_VRS_Get(ctx, _UVRSC_CORE,
-      15 /* r15 = PC */, _UVRSD_UINT32, &val);
-  CHECK(res == _UVRSR_OK && "_Unwind_VRS_Get failed");
-  // Clear the Thumb bit.
-  return val & ~(uptr)1;
-#else
-  return _Unwind_GetIP(ctx);
+void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, bool fast) {
+#if defined(__arm__) || \
+    defined(__powerpc__) || defined(__powerpc64__) || \
+    defined(__sparc__)
+  fast = false;
 #endif
-}
-
-_Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx,
-    void *param) {
-  StackTrace *b = (StackTrace*)param;
-  CHECK(b->size < b->max_size);
-  uptr pc = Unwind_GetIP(ctx);
-  b->trace[b->size++] = pc;
-  if (b->size == b->max_size) return UNWIND_STOP;
-  return UNWIND_CONTINUE;
-}
-
-void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp) {
+  if (!fast)
+    return stack->SlowUnwindStack(pc, max_s);
   stack->size = 0;
   stack->trace[0] = pc;
-  if ((max_s) > 1) {
+  if (max_s > 1) {
     stack->max_size = max_s;
-#if defined(__arm__) || \
-    defined(__powerpc__) || defined(__powerpc64__) || \
-    defined(__sparc__)
-    _Unwind_Backtrace(Unwind_Trace, stack);
-    // Pop off the two ASAN functions from the backtrace.
-    stack->PopStackFrames(2);
-#else
     if (!asan_inited) return;
     if (AsanThread *t = asanThreadRegistry().GetCurrent())
       stack->FastUnwindStack(pc, bp, t->stack_top(), t->stack_bottom());
-#endif
   }
 }
 
index db0b6d30a8be9907ad41296a27f1c13a12ef7e06..094c69ff6a26c00046aef7f8351191a8bf4eca5e 100644 (file)
@@ -158,7 +158,8 @@ void AsanLock::Unlock() {
   OSSpinLockUnlock((OSSpinLock*)&opaque_storage_);
 }
 
-void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp) {
+void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, bool fast) {
+  (void)fast;
   stack->size = 0;
   stack->trace[0] = pc;
   if ((max_s) > 1) {
@@ -306,7 +307,7 @@ void asan_register_worker_thread(int parent_tid, StackTrace *stack) {
 // alloc_asan_context().
 extern "C"
 void asan_dispatch_call_block_and_release(void *block) {
-  GET_STACK_TRACE_HERE(kStackTraceMax);
+  GET_STACK_TRACE_THREAD;
   asan_block_context_t *context = (asan_block_context_t*)block;
   if (flags()->verbosity >= 2) {
     Report("asan_dispatch_call_block_and_release(): "
@@ -316,7 +317,7 @@ void asan_dispatch_call_block_and_release(void *block) {
   asan_register_worker_thread(context->parent_tid, &stack);
   // Call the original dispatcher for the block.
   context->func(context->block);
-  asan_free(context, &stack);
+  asan_free(context, &stack, FROM_MALLOC);
 }
 
 }  // namespace __asan
@@ -341,7 +342,7 @@ asan_block_context_t *alloc_asan_context(void *ctxt, dispatch_function_t func,
 #define INTERCEPT_DISPATCH_X_F_3(dispatch_x_f)                                \
   INTERCEPTOR(void, dispatch_x_f, dispatch_queue_t dq, void *ctxt,            \
                                   dispatch_function_t func) {                 \
-    GET_STACK_TRACE_HERE(kStackTraceMax);                                     \
+    GET_STACK_TRACE_THREAD;                                                   \
     asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); \
     if (flags()->verbosity >= 2) {                                            \
       Report(#dispatch_x_f "(): context: %p, pthread_self: %p\n",             \
@@ -359,7 +360,7 @@ INTERCEPT_DISPATCH_X_F_3(dispatch_barrier_async_f)
 INTERCEPTOR(void, dispatch_after_f, dispatch_time_t when,
                                     dispatch_queue_t dq, void *ctxt,
                                     dispatch_function_t func) {
-  GET_STACK_TRACE_HERE(kStackTraceMax);
+  GET_STACK_TRACE_THREAD;
   asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
   if (flags()->verbosity >= 2) {
     Report("dispatch_after_f: %p\n", asan_ctxt);
@@ -372,7 +373,7 @@ INTERCEPTOR(void, dispatch_after_f, dispatch_time_t when,
 INTERCEPTOR(void, dispatch_group_async_f, dispatch_group_t group,
                                           dispatch_queue_t dq, void *ctxt,
                                           dispatch_function_t func) {
-  GET_STACK_TRACE_HERE(kStackTraceMax);
+  GET_STACK_TRACE_THREAD;
   asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
   if (flags()->verbosity >= 2) {
     Report("dispatch_group_async_f(): context: %p, pthread_self: %p\n",
@@ -407,7 +408,7 @@ void dispatch_source_set_event_handler(dispatch_source_t ds, void(^work)(void));
   void (^asan_block)(void);  \
   int parent_tid = asanThreadRegistry().GetCurrentTidOrInvalid(); \
   asan_block = ^(void) { \
-    GET_STACK_TRACE_HERE(kStackTraceMax); \
+    GET_STACK_TRACE_THREAD; \
     asan_register_worker_thread(parent_tid, &stack); \
     work(); \
   }
@@ -457,15 +458,15 @@ void *wrap_workitem_func(void *arg) {
   asan_block_context_t *ctxt = (asan_block_context_t*)arg;
   worker_t fn = (worker_t)(ctxt->func);
   void *result =  fn(ctxt->block);
-  GET_STACK_TRACE_HERE(kStackTraceMax);
-  asan_free(arg, &stack);
+  GET_STACK_TRACE_THREAD;
+  asan_free(arg, &stack, FROM_MALLOC);
   return result;
 }
 
 INTERCEPTOR(int, pthread_workqueue_additem_np, pthread_workqueue_t workq,
     void *(*workitem_func)(void *), void * workitem_arg,
     pthread_workitem_handle_t * itemhandlep, unsigned int *gencountp) {
-  GET_STACK_TRACE_HERE(kStackTraceMax);
+  GET_STACK_TRACE_THREAD;
   asan_block_context_t *asan_ctxt =
       (asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), &stack);
   asan_ctxt->block = workitem_arg;
index b00bbe5deca13843aa68d79635c24a61ca637209..e33d0c0d4d33b2144b311dd140ce568a5a071d1f 100644 (file)
@@ -17,6 +17,8 @@
 #include "asan_interceptors.h"
 #include "asan_internal.h"
 #include "asan_stack.h"
+#include "asan_thread_registry.h"
+#include "sanitizer/asan_interface.h"
 
 #if ASAN_ANDROID
 DECLARE_REAL_AND_INTERCEPTOR(void*, malloc, uptr size)
@@ -57,17 +59,17 @@ void ReplaceSystemMalloc() {
 using namespace __asan;  // NOLINT
 
 INTERCEPTOR(void, free, void *ptr) {
-  GET_STACK_TRACE_HERE_FOR_FREE(ptr);
-  asan_free(ptr, &stack);
+  GET_STACK_TRACE_FREE;
+  asan_free(ptr, &stack, FROM_MALLOC);
 }
 
 INTERCEPTOR(void, cfree, void *ptr) {
-  GET_STACK_TRACE_HERE_FOR_FREE(ptr);
-  asan_free(ptr, &stack);
+  GET_STACK_TRACE_FREE;
+  asan_free(ptr, &stack, FROM_MALLOC);
 }
 
 INTERCEPTOR(void*, malloc, uptr size) {
-  GET_STACK_TRACE_HERE_FOR_MALLOC;
+  GET_STACK_TRACE_MALLOC;
   return asan_malloc(size, &stack);
 }
 
@@ -83,25 +85,25 @@ INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) {
     CHECK(allocated < kCallocPoolSize);
     return mem;
   }
-  GET_STACK_TRACE_HERE_FOR_MALLOC;
+  GET_STACK_TRACE_MALLOC;
   return asan_calloc(nmemb, size, &stack);
 }
 
 INTERCEPTOR(void*, realloc, void *ptr, uptr size) {
-  GET_STACK_TRACE_HERE_FOR_MALLOC;
+  GET_STACK_TRACE_MALLOC;
   return asan_realloc(ptr, size, &stack);
 }
 
 INTERCEPTOR(void*, memalign, uptr boundary, uptr size) {
-  GET_STACK_TRACE_HERE_FOR_MALLOC;
-  return asan_memalign(boundary, size, &stack);
+  GET_STACK_TRACE_MALLOC;
+  return asan_memalign(boundary, size, &stack, FROM_MALLOC);
 }
 
 INTERCEPTOR(void*, __libc_memalign, uptr align, uptr s)
   ALIAS("memalign");
 
 INTERCEPTOR(uptr, malloc_usable_size, void *ptr) {
-  GET_STACK_TRACE_HERE_FOR_MALLOC;
+  GET_STACK_TRACE_MALLOC;
   return asan_malloc_usable_size(ptr, &stack);
 }
 
@@ -124,19 +126,23 @@ INTERCEPTOR(int, mallopt, int cmd, int value) {
 }
 
 INTERCEPTOR(int, posix_memalign, void **memptr, uptr alignment, uptr size) {
-  GET_STACK_TRACE_HERE_FOR_MALLOC;
+  GET_STACK_TRACE_MALLOC;
   // Printf("posix_memalign: %zx %zu\n", alignment, size);
   return asan_posix_memalign(memptr, alignment, size, &stack);
 }
 
 INTERCEPTOR(void*, valloc, uptr size) {
-  GET_STACK_TRACE_HERE_FOR_MALLOC;
+  GET_STACK_TRACE_MALLOC;
   return asan_valloc(size, &stack);
 }
 
 INTERCEPTOR(void*, pvalloc, uptr size) {
-  GET_STACK_TRACE_HERE_FOR_MALLOC;
+  GET_STACK_TRACE_MALLOC;
   return asan_pvalloc(size, &stack);
 }
 
+INTERCEPTOR(void, malloc_stats, void) {
+  __asan_print_accumulated_stats();
+}
+
 #endif  // __linux__
index b56b620a1e3d4833818571516ba0e61fca87d17f..97aa4424d33364775ed96bc20243584be1f132b6 100644 (file)
@@ -90,8 +90,8 @@ INTERCEPTOR(void, free, void *ptr) {
 #endif
   } else {
     if (!asan_mz_size(ptr)) ptr = get_saved_cfallocator_ref(ptr);
-    GET_STACK_TRACE_HERE_FOR_FREE(ptr);
-    asan_free(ptr, &stack);
+    GET_STACK_TRACE_FREE;
+    asan_free(ptr, &stack, FROM_MALLOC);
   }
 }
 
@@ -128,7 +128,7 @@ void *mz_malloc(malloc_zone_t *zone, size_t size) {
     CHECK(system_malloc_zone);
     return malloc_zone_malloc(system_malloc_zone, size);
   }
-  GET_STACK_TRACE_HERE_FOR_MALLOC;
+  GET_STACK_TRACE_MALLOC;
   return asan_malloc(size, &stack);
 }
 
@@ -137,7 +137,7 @@ void *cf_malloc(CFIndex size, CFOptionFlags hint, void *info) {
     CHECK(system_malloc_zone);
     return malloc_zone_malloc(system_malloc_zone, size);
   }
-  GET_STACK_TRACE_HERE_FOR_MALLOC;
+  GET_STACK_TRACE_MALLOC;
   return asan_malloc(size, &stack);
 }
 
@@ -153,7 +153,7 @@ void *mz_calloc(malloc_zone_t *zone, size_t nmemb, size_t size) {
     CHECK(allocated < kCallocPoolSize);
     return mem;
   }
-  GET_STACK_TRACE_HERE_FOR_MALLOC;
+  GET_STACK_TRACE_MALLOC;
   return asan_calloc(nmemb, size, &stack);
 }
 
@@ -162,8 +162,8 @@ void *mz_valloc(malloc_zone_t *zone, size_t size) {
     CHECK(system_malloc_zone);
     return malloc_zone_valloc(system_malloc_zone, size);
   }
-  GET_STACK_TRACE_HERE_FOR_MALLOC;
-  return asan_memalign(GetPageSizeCached(), size, &stack);
+  GET_STACK_TRACE_MALLOC;
+  return asan_memalign(GetPageSizeCached(), size, &stack, FROM_MALLOC);
 }
 
 #define GET_ZONE_FOR_PTR(ptr) \
@@ -173,8 +173,8 @@ void *mz_valloc(malloc_zone_t *zone, size_t size) {
 void ALWAYS_INLINE free_common(void *context, void *ptr) {
   if (!ptr) return;
   if (asan_mz_size(ptr)) {
-    GET_STACK_TRACE_HERE_FOR_FREE(ptr);
-    asan_free(ptr, &stack);
+    GET_STACK_TRACE_FREE;
+    asan_free(ptr, &stack, FROM_MALLOC);
   } else {
     // If the pointer does not belong to any of the zones, use one of the
     // fallback methods to free memory.
@@ -188,9 +188,9 @@ void ALWAYS_INLINE free_common(void *context, void *ptr) {
       // If the memory chunk pointer was moved to store additional
       // CFAllocatorRef, fix it back.
       ptr = get_saved_cfallocator_ref(ptr);
-      GET_STACK_TRACE_HERE_FOR_FREE(ptr);
+      GET_STACK_TRACE_FREE;
       if (!flags()->mac_ignore_invalid_free) {
-        asan_free(ptr, &stack);
+        asan_free(ptr, &stack, FROM_MALLOC);
       } else {
         GET_ZONE_FOR_PTR(ptr);
         WarnMacFreeUnallocated((uptr)ptr, (uptr)zone_ptr, zone_name, &stack);
@@ -211,17 +211,17 @@ void cf_free(void *ptr, void *info) {
 
 void *mz_realloc(malloc_zone_t *zone, void *ptr, size_t size) {
   if (!ptr) {
-    GET_STACK_TRACE_HERE_FOR_MALLOC;
+    GET_STACK_TRACE_MALLOC;
     return asan_malloc(size, &stack);
   } else {
     if (asan_mz_size(ptr)) {
-      GET_STACK_TRACE_HERE_FOR_MALLOC;
+      GET_STACK_TRACE_MALLOC;
       return asan_realloc(ptr, size, &stack);
     } else {
       // We can't recover from reallocating an unknown address, because
       // this would require reading at most |size| bytes from
       // potentially unaccessible memory.
-      GET_STACK_TRACE_HERE_FOR_FREE(ptr);
+      GET_STACK_TRACE_FREE;
       GET_ZONE_FOR_PTR(ptr);
       ReportMacMzReallocUnknown((uptr)ptr, (uptr)zone_ptr, zone_name, &stack);
     }
@@ -230,17 +230,17 @@ void *mz_realloc(malloc_zone_t *zone, void *ptr, size_t size) {
 
 void *cf_realloc(void *ptr, CFIndex size, CFOptionFlags hint, void *info) {
   if (!ptr) {
-    GET_STACK_TRACE_HERE_FOR_MALLOC;
+    GET_STACK_TRACE_MALLOC;
     return asan_malloc(size, &stack);
   } else {
     if (asan_mz_size(ptr)) {
-      GET_STACK_TRACE_HERE_FOR_MALLOC;
+      GET_STACK_TRACE_MALLOC;
       return asan_realloc(ptr, size, &stack);
     } else {
       // We can't recover from reallocating an unknown address, because
       // this would require reading at most |size| bytes from
       // potentially unaccessible memory.
-      GET_STACK_TRACE_HERE_FOR_FREE(ptr);
+      GET_STACK_TRACE_FREE;
       GET_ZONE_FOR_PTR(ptr);
       ReportMacCfReallocUnknown((uptr)ptr, (uptr)zone_ptr, zone_name, &stack);
     }
@@ -259,8 +259,8 @@ void *mz_memalign(malloc_zone_t *zone, size_t align, size_t size) {
     CHECK(system_malloc_zone);
     return malloc_zone_memalign(system_malloc_zone, align, size);
   }
-  GET_STACK_TRACE_HERE_FOR_MALLOC;
-  return asan_memalign(align, size, &stack);
+  GET_STACK_TRACE_MALLOC;
+  return asan_memalign(align, size, &stack, FROM_MALLOC);
 }
 
 // This function is currently unused, and we build with -Werror.
index 7389a2572487e50570f1aebedd1bbc370c38ed84..437079f5d1dd84819665f3c3a3a0612cc42156b6 100644 (file)
@@ -29,8 +29,8 @@ using namespace __asan;  // NOLINT
 
 extern "C" {
 void free(void *ptr) {
-  GET_STACK_TRACE_HERE_FOR_FREE(ptr);
-  return asan_free(ptr, &stack);
+  GET_STACK_TRACE_FREE;
+  return asan_free(ptr, &stack, FROM_MALLOC);
 }
 
 void _free_dbg(void* ptr, int) {
@@ -42,7 +42,7 @@ void cfree(void *ptr) {
 }
 
 void *malloc(size_t size) {
-  GET_STACK_TRACE_HERE_FOR_MALLOC;
+  GET_STACK_TRACE_MALLOC;
   return asan_malloc(size, &stack);
 }
 
@@ -51,7 +51,7 @@ void* _malloc_dbg(size_t size, int , const char*, int) {
 }
 
 void *calloc(size_t nmemb, size_t size) {
-  GET_STACK_TRACE_HERE_FOR_MALLOC;
+  GET_STACK_TRACE_MALLOC;
   return asan_calloc(nmemb, size, &stack);
 }
 
@@ -64,7 +64,7 @@ void *_calloc_impl(size_t nmemb, size_t size, int *errno_tmp) {
 }
 
 void *realloc(void *ptr, size_t size) {
-  GET_STACK_TRACE_HERE_FOR_MALLOC;
+  GET_STACK_TRACE_MALLOC;
   return asan_realloc(ptr, size, &stack);
 }
 
@@ -83,7 +83,7 @@ void* _recalloc(void* p, size_t n, size_t elem_size) {
 }
 
 size_t _msize(void *ptr) {
-  GET_STACK_TRACE_HERE_FOR_MALLOC;
+  GET_STACK_TRACE_MALLOC;
   return asan_malloc_usable_size(ptr, &stack);
 }
 
index 85d1129dea8d1cc7843fa85c2c53eca6618166d6..54e21b79678cdc2c163b0a1630af8ac2ebe7e679 100644 (file)
@@ -18,8 +18,8 @@
 // http://code.google.com/p/address-sanitizer/wiki/AddressSanitizerAlgorithm
 
 #if ASAN_FLEXIBLE_MAPPING_AND_OFFSET == 1
-extern __attribute__((visibility("default"))) uptr __asan_mapping_scale;
-extern __attribute__((visibility("default"))) uptr __asan_mapping_offset;
+extern SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_mapping_scale;
+extern SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_mapping_offset;
 # define SHADOW_SCALE (__asan_mapping_scale)
 # define SHADOW_OFFSET (__asan_mapping_offset)
 #else
index 8132e58b6e2e7b94ec26f1c311920f54b44aa718..6597b93366f1f4dc1d02f1da60ce869aa09e6b3f 100644 (file)
@@ -33,32 +33,34 @@ namespace std {
 struct nothrow_t {};
 }  // namespace std
 
-#define OPERATOR_NEW_BODY \
-  GET_STACK_TRACE_HERE_FOR_MALLOC;\
-  return asan_memalign(0, size, &stack);
+#define OPERATOR_NEW_BODY(type) \
+  GET_STACK_TRACE_MALLOC;\
+  return asan_memalign(0, size, &stack, type);
 
 INTERCEPTOR_ATTRIBUTE
-void *operator new(size_t size) { OPERATOR_NEW_BODY; }
+void *operator new(size_t size) { OPERATOR_NEW_BODY(FROM_NEW); }
 INTERCEPTOR_ATTRIBUTE
-void *operator new[](size_t size) { OPERATOR_NEW_BODY; }
+void *operator new[](size_t size) { OPERATOR_NEW_BODY(FROM_NEW_BR); }
 INTERCEPTOR_ATTRIBUTE
-void *operator new(size_t size, std::nothrow_t const&) { OPERATOR_NEW_BODY; }
+void *operator new(size_t size, std::nothrow_t const&)
+{ OPERATOR_NEW_BODY(FROM_NEW); }
 INTERCEPTOR_ATTRIBUTE
-void *operator new[](size_t size, std::nothrow_t const&) { OPERATOR_NEW_BODY; }
+void *operator new[](size_t size, std::nothrow_t const&)
+{ OPERATOR_NEW_BODY(FROM_NEW_BR); }
 
-#define OPERATOR_DELETE_BODY \
-  GET_STACK_TRACE_HERE_FOR_FREE(ptr);\
-  asan_free(ptr, &stack);
+#define OPERATOR_DELETE_BODY(type) \
+  GET_STACK_TRACE_FREE;\
+  asan_free(ptr, &stack, type);
 
 INTERCEPTOR_ATTRIBUTE
-void operator delete(void *ptr) { OPERATOR_DELETE_BODY; }
+void operator delete(void *ptr) { OPERATOR_DELETE_BODY(FROM_NEW); }
 INTERCEPTOR_ATTRIBUTE
-void operator delete[](void *ptr) { OPERATOR_DELETE_BODY; }
+void operator delete[](void *ptr) { OPERATOR_DELETE_BODY(FROM_NEW_BR); }
 INTERCEPTOR_ATTRIBUTE
 void operator delete(void *ptr, std::nothrow_t const&)
-{ OPERATOR_DELETE_BODY; }
+{ OPERATOR_DELETE_BODY(FROM_NEW); }
 INTERCEPTOR_ATTRIBUTE
 void operator delete[](void *ptr, std::nothrow_t const&)
-{ OPERATOR_DELETE_BODY; }
+{ OPERATOR_DELETE_BODY(FROM_NEW_BR); }
 
 #endif
index 449299a583669435ac3d4b9ec478a327ae78caa3..a00bafffae0a0d59faf9419af02ab3dde757f3c5 100644 (file)
 #include "asan_internal.h"
 #include "asan_mapping.h"
 #include "sanitizer/asan_interface.h"
+#include "sanitizer_common/sanitizer_libc.h"
 
 namespace __asan {
 
 void PoisonShadow(uptr addr, uptr size, u8 value) {
+  if (!flags()->poison_heap) return;
   CHECK(AddrIsAlignedByGranularity(addr));
   CHECK(AddrIsAlignedByGranularity(addr + size));
   uptr shadow_beg = MemToShadow(addr);
@@ -30,6 +32,7 @@ void PoisonShadowPartialRightRedzone(uptr addr,
                                      uptr size,
                                      uptr redzone_size,
                                      u8 value) {
+  if (!flags()->poison_heap) return;
   CHECK(AddrIsAlignedByGranularity(addr));
   u8 *shadow = (u8*)MemToShadow(addr);
   for (uptr i = 0; i < redzone_size;
@@ -150,6 +153,33 @@ bool __asan_address_is_poisoned(void const volatile *addr) {
   return __asan::AddressIsPoisoned((uptr)addr);
 }
 
+uptr __asan_region_is_poisoned(uptr beg, uptr size) {
+  if (!size) return 0;
+  uptr end = beg + size;
+  if (!AddrIsInMem(beg)) return beg;
+  if (!AddrIsInMem(end)) return end;
+  uptr aligned_b = RoundUpTo(beg, SHADOW_GRANULARITY);
+  uptr aligned_e = RoundDownTo(end, SHADOW_GRANULARITY);
+  uptr shadow_beg = MemToShadow(aligned_b);
+  uptr shadow_end = MemToShadow(aligned_e);
+  // First check the first and the last application bytes,
+  // then check the SHADOW_GRANULARITY-aligned region by calling
+  // mem_is_zero on the corresponding shadow.
+  if (!__asan::AddressIsPoisoned(beg) &&
+      !__asan::AddressIsPoisoned(end - 1) &&
+      (shadow_end <= shadow_beg ||
+       __sanitizer::mem_is_zero((const char *)shadow_beg,
+                                shadow_end - shadow_beg)))
+    return 0;
+  // The fast check failed, so we have a poisoned byte somewhere.
+  // Find it slowly.
+  for (; beg < end; beg++)
+    if (__asan::AddressIsPoisoned(beg))
+      return beg;
+  UNREACHABLE("mem_is_zero returned false, but poisoned byte was not found");
+  return 0;
+}
+
 // This is a simplified version of __asan_(un)poison_memory_region, which
 // assumes that left border of region to be poisoned is properly aligned.
 static void PoisonAlignedStackMemory(uptr addr, uptr size, bool do_poison) {
@@ -166,7 +196,7 @@ static void PoisonAlignedStackMemory(uptr addr, uptr size, bool do_poison) {
     // If possible, mark all the bytes mapping to last shadow byte as
     // unaddressable.
     if (end_value > 0 && end_value <= end_offset)
-      *shadow_end = kAsanStackUseAfterScopeMagic;
+      *shadow_end = (s8)kAsanStackUseAfterScopeMagic;
   } else {
     // If necessary, mark few first bytes mapping to last shadow byte
     // as addressable
index 9804ddd1850c74e08da46922e315887061f145f7..ed18ab2509847143b163aeff18dccfac4d527290 100644 (file)
@@ -16,6 +16,9 @@
 #include "asan_stack.h"
 #include "asan_thread.h"
 #include "asan_thread_registry.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_report_decorator.h"
+#include "sanitizer_common/sanitizer_symbolizer.h"
 
 namespace __asan {
 
@@ -38,14 +41,79 @@ void AppendToErrorMessageBuffer(const char *buffer) {
   }
 }
 
+// ---------------------- Decorator ------------------------------ {{{1
+bool PrintsToTtyCached() {
+  static int cached = 0;
+  static bool prints_to_tty;
+  if (!cached) {  // Ok wrt threads since we are printing only from one thread.
+    prints_to_tty = PrintsToTty();
+    cached = 1;
+  }
+  return prints_to_tty;
+}
+class Decorator: private __sanitizer::AnsiColorDecorator {
+ public:
+  Decorator() : __sanitizer::AnsiColorDecorator(PrintsToTtyCached()) { }
+  const char *Warning()    { return Red(); }
+  const char *EndWarning() { return Default(); }
+  const char *Access()     { return Blue(); }
+  const char *EndAccess()  { return Default(); }
+  const char *Location()   { return Green(); }
+  const char *EndLocation() { return Default(); }
+  const char *Allocation()  { return Magenta(); }
+  const char *EndAllocation()  { return Default(); }
+
+  const char *ShadowByte(u8 byte) {
+    switch (byte) {
+      case kAsanHeapLeftRedzoneMagic:
+      case kAsanHeapRightRedzoneMagic:
+        return Red();
+      case kAsanHeapFreeMagic:
+        return Magenta();
+      case kAsanStackLeftRedzoneMagic:
+      case kAsanStackMidRedzoneMagic:
+      case kAsanStackRightRedzoneMagic:
+      case kAsanStackPartialRedzoneMagic:
+        return Red();
+      case kAsanStackAfterReturnMagic:
+        return Magenta();
+      case kAsanInitializationOrderMagic:
+        return Cyan();
+      case kAsanUserPoisonedMemoryMagic:
+        return Blue();
+      case kAsanStackUseAfterScopeMagic:
+        return Magenta();
+      case kAsanGlobalRedzoneMagic:
+        return Red();
+      case kAsanInternalHeapMagic:
+        return Yellow();
+      default:
+        return Default();
+    }
+  }
+  const char *EndShadowByte() { return Default(); }
+};
+
 // ---------------------- Helper functions ----------------------- {{{1
 
-static void PrintBytes(const char *before, uptr *a) {
-  u8 *bytes = (u8*)a;
-  uptr byte_num = (SANITIZER_WORDSIZE) / 8;
-  Printf("%s%p:", before, (void*)a);
-  for (uptr i = 0; i < byte_num; i++) {
-    Printf(" %x%x", bytes[i] >> 4, bytes[i] & 15);
+static void PrintShadowByte(const char *before, u8 byte,
+                            const char *after = "\n") {
+  Decorator d;
+  Printf("%s%s%x%x%s%s", before,
+         d.ShadowByte(byte), byte >> 4, byte & 15, d.EndShadowByte(), after);
+}
+
+static void PrintShadowBytes(const char *before, u8 *bytes,
+                             u8 *guilty, uptr n) {
+  Decorator d;
+  if (before)
+    Printf("%s%p:", before, bytes);
+  for (uptr i = 0; i < n; i++) {
+    u8 *p = bytes + i;
+    const char *before = p == guilty ? "[" :
+        p - 1 == guilty ? "" : " ";
+    const char *after = p == guilty ? "]" : "";
+    PrintShadowByte(before, *p, after);
   }
   Printf("\n");
 }
@@ -54,15 +122,35 @@ static void PrintShadowMemoryForAddress(uptr addr) {
   if (!AddrIsInMem(addr))
     return;
   uptr shadow_addr = MemToShadow(addr);
-  Printf("Shadow byte and word:\n");
-  Printf("  %p: %x\n", (void*)shadow_addr, *(unsigned char*)shadow_addr);
-  uptr aligned_shadow = shadow_addr & ~(kWordSize - 1);
-  PrintBytes("  ", (uptr*)(aligned_shadow));
-  Printf("More shadow bytes:\n");
-  for (int i = -4; i <= 4; i++) {
+  const uptr n_bytes_per_row = 16;
+  uptr aligned_shadow = shadow_addr & ~(n_bytes_per_row - 1);
+  Printf("Shadow bytes around the buggy address:\n");
+  for (int i = -5; i <= 5; i++) {
     const char *prefix = (i == 0) ? "=>" : "  ";
-    PrintBytes(prefix, (uptr*)(aligned_shadow + i * kWordSize));
+    PrintShadowBytes(prefix,
+                     (u8*)(aligned_shadow + i * n_bytes_per_row),
+                     (u8*)shadow_addr, n_bytes_per_row);
   }
+  Printf("Shadow byte legend (one shadow byte represents %d "
+         "application bytes):\n", (int)SHADOW_GRANULARITY);
+  PrintShadowByte("  Addressable:           ", 0);
+  Printf("  Partially addressable: ");
+  for (uptr i = 1; i < SHADOW_GRANULARITY; i++)
+    PrintShadowByte("", i, " ");
+  Printf("\n");
+  PrintShadowByte("  Heap left redzone:     ", kAsanHeapLeftRedzoneMagic);
+  PrintShadowByte("  Heap righ redzone:     ", kAsanHeapRightRedzoneMagic);
+  PrintShadowByte("  Freed Heap region:     ", kAsanHeapFreeMagic);
+  PrintShadowByte("  Stack left redzone:    ", kAsanStackLeftRedzoneMagic);
+  PrintShadowByte("  Stack mid redzone:     ", kAsanStackMidRedzoneMagic);
+  PrintShadowByte("  Stack right redzone:   ", kAsanStackRightRedzoneMagic);
+  PrintShadowByte("  Stack partial redzone: ", kAsanStackPartialRedzoneMagic);
+  PrintShadowByte("  Stack after return:    ", kAsanStackAfterReturnMagic);
+  PrintShadowByte("  Stack use after scope: ", kAsanStackUseAfterScopeMagic);
+  PrintShadowByte("  Global redzone:        ", kAsanGlobalRedzoneMagic);
+  PrintShadowByte("  Global init order:     ", kAsanInitializationOrderMagic);
+  PrintShadowByte("  Poisoned by user:      ", kAsanUserPoisonedMemoryMagic);
+  PrintShadowByte("  ASan internal:         ", kAsanInternalHeapMagic);
 }
 
 static void PrintZoneForPointer(uptr ptr, uptr zone_ptr,
@@ -98,6 +186,8 @@ static void PrintGlobalNameIfASCII(const __asan_global &g) {
 bool DescribeAddressRelativeToGlobal(uptr addr, const __asan_global &g) {
   if (addr < g.beg - kGlobalAndStackRedzone) return false;
   if (addr >= g.beg + g.size_with_redzone) return false;
+  Decorator d;
+  Printf("%s", d.Location());
   Printf("%p is located ", (void*)addr);
   if (addr < g.beg) {
     Printf("%zd bytes to the left", g.beg - addr);
@@ -108,6 +198,7 @@ bool DescribeAddressRelativeToGlobal(uptr addr, const __asan_global &g) {
   }
   Printf(" of global variable '%s' (0x%zx) of size %zu\n",
              g.name, g.beg, g.size);
+  Printf("%s", d.EndLocation());
   PrintGlobalNameIfASCII(g);
   return true;
 }
@@ -151,9 +242,12 @@ bool DescribeAddressIfStack(uptr addr, uptr access_size) {
   internal_strncat(buf, frame_descr,
                    Min(kBufSize,
                        static_cast<sptr>(name_end - frame_descr)));
+  Decorator d;
+  Printf("%s", d.Location());
   Printf("Address %p is located at offset %zu "
              "in frame <%s> of T%d's stack:\n",
-             (void*)addr, offset, buf, t->tid());
+             (void*)addr, offset, Demangle(buf), t->tid());
+  Printf("%s", d.EndLocation());
   // Report the number of stack objects.
   char *p;
   uptr n_objects = internal_simple_strtoll(name_end, &p, 10);
@@ -187,6 +281,8 @@ bool DescribeAddressIfStack(uptr addr, uptr access_size) {
 static void DescribeAccessToHeapChunk(AsanChunkView chunk, uptr addr,
                                       uptr access_size) {
   uptr offset;
+  Decorator d;
+  Printf("%s", d.Location());
   Printf("%p is located ", (void*)addr);
   if (chunk.AddrIsInside(addr, access_size, &offset)) {
     Printf("%zu bytes inside of", offset);
@@ -199,6 +295,26 @@ static void DescribeAccessToHeapChunk(AsanChunkView chunk, uptr addr,
   }
   Printf(" %zu-byte region [%p,%p)\n", chunk.UsedSize(),
          (void*)(chunk.Beg()), (void*)(chunk.End()));
+  Printf("%s", d.EndLocation());
+}
+
+// Return " (thread_name) " or an empty string if the name is empty.
+const char *ThreadNameWithParenthesis(AsanThreadSummary *t, char buff[],
+                                      uptr buff_len) {
+  const char *name = t->name();
+  if (*name == 0) return "";
+  buff[0] = 0;
+  internal_strncat(buff, " (", 3);
+  internal_strncat(buff, name, buff_len - 4);
+  internal_strncat(buff, ")", 2);
+  return buff;
+}
+
+const char *ThreadNameWithParenthesis(u32 tid, char buff[],
+                                      uptr buff_len) {
+  if (tid == kInvalidTid) return "";
+  AsanThreadSummary *t = asanThreadRegistry().FindByTid(tid);
+  return ThreadNameWithParenthesis(t, buff, buff_len);
 }
 
 void DescribeHeapAddress(uptr addr, uptr access_size) {
@@ -212,20 +328,31 @@ void DescribeHeapAddress(uptr addr, uptr access_size) {
   chunk.GetAllocStack(&alloc_stack);
   AsanThread *t = asanThreadRegistry().GetCurrent();
   CHECK(t);
+  char tname[128];
+  Decorator d;
   if (chunk.FreeTid() != kInvalidTid) {
     AsanThreadSummary *free_thread =
         asanThreadRegistry().FindByTid(chunk.FreeTid());
-    Printf("freed by thread T%d here:\n", free_thread->tid());
+    Printf("%sfreed by thread T%d%s here:%s\n", d.Allocation(),
+           free_thread->tid(),
+           ThreadNameWithParenthesis(free_thread, tname, sizeof(tname)),
+           d.EndAllocation());
     StackTrace free_stack;
     chunk.GetFreeStack(&free_stack);
     PrintStack(&free_stack);
-    Printf("previously allocated by thread T%d here:\n", alloc_thread->tid());
+    Printf("%spreviously allocated by thread T%d%s here:%s\n",
+           d.Allocation(), alloc_thread->tid(),
+           ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)),
+           d.EndAllocation());
     PrintStack(&alloc_stack);
     DescribeThread(t->summary());
     DescribeThread(free_thread);
     DescribeThread(alloc_thread);
   } else {
-    Printf("allocated by thread T%d here:\n", alloc_thread->tid());
+    Printf("%sallocated by thread T%d%s here:%s\n", d.Allocation(),
+           alloc_thread->tid(),
+           ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)),
+           d.EndAllocation());
     PrintStack(&alloc_stack);
     DescribeThread(t->summary());
     DescribeThread(alloc_thread);
@@ -254,8 +381,13 @@ void DescribeThread(AsanThreadSummary *summary) {
     return;
   }
   summary->set_announced(true);
-  Printf("Thread T%d created by T%d here:\n",
-         summary->tid(), summary->parent_tid());
+  char tname[128];
+  Printf("Thread T%d%s", summary->tid(),
+         ThreadNameWithParenthesis(summary->tid(), tname, sizeof(tname)));
+  Printf(" created by T%d%s here:\n",
+         summary->parent_tid(),
+         ThreadNameWithParenthesis(summary->parent_tid(),
+                                   tname, sizeof(tname)));
   PrintStack(summary->stack());
   // Recursively described parent thread if needed.
   if (flags()->print_full_thread_history) {
@@ -291,7 +423,7 @@ class ScopedInErrorReport {
       // Die() to bypass any additional checks.
       Exit(flags()->exitcode);
     }
-    __asan_on_error();
+    ASAN_ON_ERROR();
     reporting_thread_tid = asanThreadRegistry().GetCurrentTidOrInvalid();
     Printf("===================================================="
            "=============\n");
@@ -322,44 +454,79 @@ class ScopedInErrorReport {
 
 void ReportSIGSEGV(uptr pc, uptr sp, uptr bp, uptr addr) {
   ScopedInErrorReport in_report;
+  Decorator d;
+  Printf("%s", d.Warning());
   Report("ERROR: AddressSanitizer: SEGV on unknown address %p"
              " (pc %p sp %p bp %p T%d)\n",
              (void*)addr, (void*)pc, (void*)sp, (void*)bp,
              asanThreadRegistry().GetCurrentTidOrInvalid());
+  Printf("%s", d.EndWarning());
   Printf("AddressSanitizer can not provide additional info.\n");
-  GET_STACK_TRACE_WITH_PC_AND_BP(kStackTraceMax, pc, bp);
+  GET_STACK_TRACE_FATAL(pc, bp);
   PrintStack(&stack);
 }
 
 void ReportDoubleFree(uptr addr, StackTrace *stack) {
   ScopedInErrorReport in_report;
+  Decorator d;
+  Printf("%s", d.Warning());
   Report("ERROR: AddressSanitizer: attempting double-free on %p:\n", addr);
+  Printf("%s", d.EndWarning());
   PrintStack(stack);
   DescribeHeapAddress(addr, 1);
 }
 
 void ReportFreeNotMalloced(uptr addr, StackTrace *stack) {
   ScopedInErrorReport in_report;
+  Decorator d;
+  Printf("%s", d.Warning());
   Report("ERROR: AddressSanitizer: attempting free on address "
              "which was not malloc()-ed: %p\n", addr);
+  Printf("%s", d.EndWarning());
   PrintStack(stack);
   DescribeHeapAddress(addr, 1);
 }
 
+void ReportAllocTypeMismatch(uptr addr, StackTrace *stack,
+                             AllocType alloc_type,
+                             AllocType dealloc_type) {
+  static const char *alloc_names[] =
+    {"INVALID", "malloc", "operator new", "operator new []"};
+  static const char *dealloc_names[] =
+    {"INVALID", "free", "operator delete", "operator delete []"};
+  CHECK_NE(alloc_type, dealloc_type);
+  ScopedInErrorReport in_report;
+  Decorator d;
+  Printf("%s", d.Warning());
+  Report("ERROR: AddressSanitizer: alloc-dealloc-mismatch (%s vs %s) on %p\n",
+        alloc_names[alloc_type], dealloc_names[dealloc_type], addr);
+  Printf("%s", d.EndWarning());
+  PrintStack(stack);
+  DescribeHeapAddress(addr, 1);
+  Report("HINT: if you don't care about these warnings you may set "
+         "ASAN_OPTIONS=alloc_dealloc_mismatch=0\n");
+}
+
 void ReportMallocUsableSizeNotOwned(uptr addr, StackTrace *stack) {
   ScopedInErrorReport in_report;
+  Decorator d;
+  Printf("%s", d.Warning());
   Report("ERROR: AddressSanitizer: attempting to call "
              "malloc_usable_size() for pointer which is "
              "not owned: %p\n", addr);
+  Printf("%s", d.EndWarning());
   PrintStack(stack);
   DescribeHeapAddress(addr, 1);
 }
 
 void ReportAsanGetAllocatedSizeNotOwned(uptr addr, StackTrace *stack) {
   ScopedInErrorReport in_report;
+  Decorator d;
+  Printf("%s", d.Warning());
   Report("ERROR: AddressSanitizer: attempting to call "
              "__asan_get_allocated_size() for pointer which is "
              "not owned: %p\n", addr);
+  Printf("%s", d.EndWarning());
   PrintStack(stack);
   DescribeHeapAddress(addr, 1);
 }
@@ -368,9 +535,12 @@ void ReportStringFunctionMemoryRangesOverlap(
     const char *function, const char *offset1, uptr length1,
     const char *offset2, uptr length2, StackTrace *stack) {
   ScopedInErrorReport in_report;
+  Decorator d;
+  Printf("%s", d.Warning());
   Report("ERROR: AddressSanitizer: %s-param-overlap: "
              "memory ranges [%p,%p) and [%p, %p) overlap\n", \
              function, offset1, offset1 + length1, offset2, offset2 + length2);
+  Printf("%s", d.EndWarning());
   PrintStack(stack);
   DescribeAddress((uptr)offset1, length1);
   DescribeAddress((uptr)offset2, length2);
@@ -463,17 +633,23 @@ void __asan_report_error(uptr pc, uptr bp, uptr sp,
         break;
     }
   }
-
+  Decorator d;
+  Printf("%s", d.Warning());
   Report("ERROR: AddressSanitizer: %s on address "
              "%p at pc 0x%zx bp 0x%zx sp 0x%zx\n",
              bug_descr, (void*)addr, pc, bp, sp);
+  Printf("%s", d.EndWarning());
 
   u32 curr_tid = asanThreadRegistry().GetCurrentTidOrInvalid();
-  Printf("%s of size %zu at %p thread T%d\n",
-             access_size ? (is_write ? "WRITE" : "READ") : "ACCESS",
-             access_size, (void*)addr, curr_tid);
-
-  GET_STACK_TRACE_WITH_PC_AND_BP(kStackTraceMax, pc, bp);
+  char tname[128];
+  Printf("%s%s of size %zu at %p thread T%d%s%s\n",
+         d.Access(),
+         access_size ? (is_write ? "WRITE" : "READ") : "ACCESS",
+         access_size, (void*)addr, curr_tid,
+         ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname)),
+         d.EndAccess());
+
+  GET_STACK_TRACE_FATAL(pc, bp);
   PrintStack(&stack);
 
   DescribeAddress(addr, access_size);
@@ -491,7 +667,13 @@ void NOINLINE __asan_set_error_report_callback(void (*callback)(const char*)) {
   }
 }
 
+void __asan_describe_address(uptr addr) {
+  DescribeAddress(addr, 1);
+}
+
+#if !SANITIZER_SUPPORTS_WEAK_HOOKS
 // Provide default implementation of __asan_on_error that does nothing
 // and may be overriden by user.
 SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE NOINLINE
 void __asan_on_error() {}
+#endif
index 9710bd7968ea11577cefc688a928b41beb5d91ef..a7e0e5816b7eeed0e63a20d5e71b6e3822871e1a 100644 (file)
@@ -10,6 +10,7 @@
 // ASan-private header for error reporting functions.
 //===----------------------------------------------------------------------===//
 
+#include "asan_allocator.h"
 #include "asan_internal.h"
 #include "asan_thread.h"
 #include "sanitizer/asan_interface.h"
@@ -32,6 +33,9 @@ void DescribeThread(AsanThreadSummary *summary);
 void NORETURN ReportSIGSEGV(uptr pc, uptr sp, uptr bp, uptr addr);
 void NORETURN ReportDoubleFree(uptr addr, StackTrace *stack);
 void NORETURN ReportFreeNotMalloced(uptr addr, StackTrace *stack);
+void NORETURN ReportAllocTypeMismatch(uptr addr, StackTrace *stack,
+                                      AllocType alloc_type,
+                                      AllocType dealloc_type);
 void NORETURN ReportMallocUsableSizeNotOwned(uptr addr,
                                              StackTrace *stack);
 void NORETURN ReportAsanGetAllocatedSizeNotOwned(uptr addr,
index cf0025a70efc81c9a53631c76c99ffba66095817..6480bf4cc3591d92377b2490b1b1cf45d9a67fcb 100644 (file)
@@ -52,7 +52,7 @@ static void AsanCheckFailed(const char *file, int line, const char *cond,
              file, line, cond, (uptr)v1, (uptr)v2);
   // FIXME: check for infinite recursion without a thread-local counter here.
   PRINT_CURRENT_STACK();
-  ShowStatsAndAbort();
+  Die();
 }
 
 // -------------------------- Flags ------------------------- {{{1
@@ -64,6 +64,10 @@ Flags *flags() {
   return &asan_flags;
 }
 
+static const char *MaybeCallAsanDefaultOptions() {
+  return (&__asan_default_options) ? __asan_default_options() : "";
+}
+
 static void ParseFlagsFromString(Flags *f, const char *str) {
   ParseFlag(str, &f->quarantine_size, "quarantine_size");
   ParseFlag(str, &f->symbolize, "symbolize");
@@ -98,21 +102,20 @@ static void ParseFlagsFromString(Flags *f, const char *str) {
   ParseFlag(str, &f->allow_reexec, "allow_reexec");
   ParseFlag(str, &f->print_full_thread_history, "print_full_thread_history");
   ParseFlag(str, &f->log_path, "log_path");
+  ParseFlag(str, &f->fast_unwind_on_fatal, "fast_unwind_on_fatal");
+  ParseFlag(str, &f->fast_unwind_on_malloc, "fast_unwind_on_malloc");
+  ParseFlag(str, &f->poison_heap, "poison_heap");
+  ParseFlag(str, &f->alloc_dealloc_mismatch, "alloc_dealloc_mismatch");
+  ParseFlag(str, &f->use_stack_depot, "use_stack_depot");
 }
 
-extern "C" {
-SANITIZER_WEAK_ATTRIBUTE
-SANITIZER_INTERFACE_ATTRIBUTE
-const char* __asan_default_options() { return ""; }
-}  // extern "C"
-
 void InitializeFlags(Flags *f, const char *env) {
   internal_memset(f, 0, sizeof(*f));
 
   f->quarantine_size = (ASAN_LOW_MEMORY) ? 1UL << 26 : 1UL << 28;
   f->symbolize = false;
   f->verbosity = 0;
-  f->redzone = (ASAN_LOW_MEMORY) ? 64 : 128;
+  f->redzone = ASAN_ALLOCATOR_VERSION == 2 ? 16 : (ASAN_LOW_MEMORY) ? 64 : 128;
   f->debug = false;
   f->report_globals = 1;
   f->check_initialization_order = true;
@@ -137,12 +140,17 @@ void InitializeFlags(Flags *f, const char *env) {
   f->allow_reexec = true;
   f->print_full_thread_history = true;
   f->log_path = 0;
+  f->fast_unwind_on_fatal = true;
+  f->fast_unwind_on_malloc = true;
+  f->poison_heap = true;
+  f->alloc_dealloc_mismatch = true;
+  f->use_stack_depot = true;  // Only affects allocator2.
 
   // Override from user-specified string.
-  ParseFlagsFromString(f, __asan_default_options());
+  ParseFlagsFromString(f, MaybeCallAsanDefaultOptions());
   if (flags()->verbosity) {
     Report("Using the defaults from __asan_default_options: %s\n",
-           __asan_default_options());
+           MaybeCallAsanDefaultOptions());
   }
 
   // Override from command line.
@@ -239,15 +247,12 @@ static NOINLINE void force_interface_symbols() {
     case 27: __asan_set_error_exit_code(0); break;
     case 28: __asan_stack_free(0, 0, 0); break;
     case 29: __asan_stack_malloc(0, 0); break;
-    case 30: __asan_on_error(); break;
-    case 31: __asan_default_options(); break;
-    case 32: __asan_before_dynamic_init(0, 0); break;
-    case 33: __asan_after_dynamic_init(); break;
-    case 34: __asan_malloc_hook(0, 0); break;
-    case 35: __asan_free_hook(0); break;
-    case 36: __asan_symbolize(0, 0, 0); break;
-    case 37: __asan_poison_stack_memory(0, 0); break;
-    case 38: __asan_unpoison_stack_memory(0, 0); break;
+    case 30: __asan_before_dynamic_init(0, 0); break;
+    case 31: __asan_after_dynamic_init(); break;
+    case 32: __asan_poison_stack_memory(0, 0); break;
+    case 33: __asan_unpoison_stack_memory(0, 0); break;
+    case 34: __asan_region_is_poisoned(0, 0); break;
+    case 35: __asan_describe_address(0); break;
   }
 }
 
@@ -261,6 +266,13 @@ static void asan_atexit() {
 // ---------------------- Interface ---------------- {{{1
 using namespace __asan;  // NOLINT
 
+#if !SANITIZER_SUPPORTS_WEAK_HOOKS
+extern "C" {
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+const char* __asan_default_options() { return ""; }
+}  // extern "C"
+#endif
+
 int NOINLINE __asan_set_error_exit_code(int exit_code) {
   int old = flags()->exitcode;
   flags()->exitcode = exit_code;
index 2531a7f9df800193ca7c90b6b1094d360e7e77c0..9b6a28e8082ef7cf6885e7dd425b3205cb288df7 100644 (file)
 
 namespace __asan {
 
+static bool MaybeCallAsanSymbolize(const void *pc, char *out_buffer,
+                                   int out_size) {
+  return (&__asan_symbolize) ? __asan_symbolize(pc, out_buffer, out_size)
+                             : false;
+}
+
 void PrintStack(StackTrace *stack) {
   stack->PrintStack(stack->trace, stack->size, flags()->symbolize,
-                    flags()->strip_path_prefix, __asan_symbolize);
+                    flags()->strip_path_prefix, MaybeCallAsanSymbolize);
 }
 
 }  // namespace __asan
@@ -27,7 +33,7 @@ void PrintStack(StackTrace *stack) {
 // Provide default implementation of __asan_symbolize that does nothing
 // and may be overriden by user if he wants to use his own symbolization.
 // ASan on Windows has its own implementation of this.
-#ifndef _WIN32
+#if !defined(_WIN32) && !SANITIZER_SUPPORTS_WEAK_HOOKS
 SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE NOINLINE
 bool __asan_symbolize(const void *pc, char *out_buffer, int out_size) {
   return false;
index da622ed8eec0fb5e97a22679447909806c6a3196..6a5ffc934cca84b22c8f07d860c467a15f1cac60 100644 (file)
 #define ASAN_STACK_H
 
 #include "sanitizer_common/sanitizer_stacktrace.h"
+#include "asan_flags.h"
 
 namespace __asan {
 
-void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp);
+void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, bool fast);
 void PrintStack(StackTrace *stack);
 
 }  // namespace __asan
@@ -25,27 +26,38 @@ void PrintStack(StackTrace *stack);
 // The pc will be in the position 0 of the resulting stack trace.
 // The bp may refer to the current frame or to the caller's frame.
 // fast_unwind is currently unused.
-#define GET_STACK_TRACE_WITH_PC_AND_BP(max_s, pc, bp)               \
+#define GET_STACK_TRACE_WITH_PC_AND_BP(max_s, pc, bp, fast)     \
   StackTrace stack;                                             \
-  GetStackTrace(&stack, max_s, pc, bp)
+  GetStackTrace(&stack, max_s, pc, bp, fast)
 
 // NOTE: A Rule of thumb is to retrieve stack trace in the interceptors
 // as early as possible (in functions exposed to the user), as we generally
 // don't want stack trace to contain functions from ASan internals.
 
-#define GET_STACK_TRACE_HERE(max_size)                        \
+#define GET_STACK_TRACE(max_size, fast)                       \
   GET_STACK_TRACE_WITH_PC_AND_BP(max_size,                    \
-      StackTrace::GetCurrentPc(), GET_CURRENT_FRAME())
+      StackTrace::GetCurrentPc(), GET_CURRENT_FRAME(), fast)
 
-#define GET_STACK_TRACE_HERE_FOR_MALLOC                             \
-  GET_STACK_TRACE_HERE(flags()->malloc_context_size)
+#define GET_STACK_TRACE_FATAL(pc, bp)                                 \
+  GET_STACK_TRACE_WITH_PC_AND_BP(kStackTraceMax, pc, bp,              \
+                                 flags()->fast_unwind_on_fatal)
 
-#define GET_STACK_TRACE_HERE_FOR_FREE(ptr)                          \
-  GET_STACK_TRACE_HERE(flags()->malloc_context_size)
+#define GET_STACK_TRACE_FATAL_HERE                           \
+  GET_STACK_TRACE(kStackTraceMax, flags()->fast_unwind_on_fatal)
+
+#define GET_STACK_TRACE_THREAD                              \
+  GET_STACK_TRACE(kStackTraceMax, true)
+
+#define GET_STACK_TRACE_MALLOC                             \
+  GET_STACK_TRACE(flags()->malloc_context_size,            \
+                  flags()->fast_unwind_on_malloc)
+
+#define GET_STACK_TRACE_FREE GET_STACK_TRACE_MALLOC
 
 #define PRINT_CURRENT_STACK()                    \
   {                                              \
-    GET_STACK_TRACE_HERE(kStackTraceMax);        \
+    GET_STACK_TRACE(kStackTraceMax,              \
+      flags()->fast_unwind_on_fatal);            \
     PrintStack(&stack);                          \
   }
 
index 566e0103e4188d26160014aa4220999ddab42b96..94dd741d3259d4f24f7759253f56b4862b4ba46d 100644 (file)
@@ -15,6 +15,7 @@
 #include "asan_stats.h"
 #include "asan_thread_registry.h"
 #include "sanitizer/asan_interface.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
 
 namespace __asan {
 
@@ -40,8 +41,9 @@ void AsanStats::Print() {
   Printf("Stats: %zuM freed by %zu calls\n", freed>>20, frees);
   Printf("Stats: %zuM really freed by %zu calls\n",
              really_freed>>20, real_frees);
-  Printf("Stats: %zuM (%zu full pages) mmaped in %zu calls\n",
-             mmaped>>20, mmaped / GetPageSizeCached(), mmaps);
+  Printf("Stats: %zuM (%zuM-%zuM) mmaped; %zu maps, %zu unmaps\n",
+             (mmaped-munmaped)>>20, mmaped>>20, munmaped>>20,
+             mmaps, munmaps);
 
   PrintMallocStatsArray("  mmaps   by size class: ", mmaped_by_size);
   PrintMallocStatsArray("  mallocs by size class: ", malloced_by_size);
@@ -59,6 +61,10 @@ static void PrintAccumulatedStats() {
   // Use lock to keep reports from mixing up.
   ScopedLock lock(&print_lock);
   stats.Print();
+  StackDepotStats *stack_depot_stats = StackDepotGetStats();
+  Printf("Stats: StackDepot: %zd ids; %zdM mapped\n",
+         stack_depot_stats->n_uniq_ids, stack_depot_stats->mapped >> 20);
+  PrintInternalAllocatorStats();
 }
 
 }  // namespace __asan
index c2b3298266b9f62729ba6396e11353d55edbd9b0..fd27451aef25699f495d824ebb26a69ec9f7b5da 100644 (file)
@@ -35,6 +35,8 @@ struct AsanStats {
   uptr realloced;
   uptr mmaps;
   uptr mmaped;
+  uptr munmaps;
+  uptr munmaped;
   uptr mmaped_by_size[kNumberOfSizeClasses];
   uptr malloced_by_size[kNumberOfSizeClasses];
   uptr freed_by_size[kNumberOfSizeClasses];
index dff8c88528a11c1a74d183c2c1f5890f53bb1bfb..f385ec35fcda30421b39fbbb859d0b0923ae1794 100644 (file)
@@ -37,6 +37,7 @@ class AsanThreadSummary {
       internal_memcpy(&stack_, stack, sizeof(*stack));
     }
     thread_ = 0;
+    name_[0] = 0;
   }
   u32 tid() { return tid_; }
   void set_tid(u32 tid) { tid_ = tid; }
@@ -47,6 +48,10 @@ class AsanThreadSummary {
   AsanThread *thread() { return thread_; }
   void set_thread(AsanThread *thread) { thread_ = thread; }
   static void TSDDtor(void *tsd);
+  void set_name(const char *name) {
+    internal_strncpy(name_, name, sizeof(name_) - 1);
+  }
+  const char *name() { return name_; }
 
  private:
   u32 tid_;
@@ -54,8 +59,12 @@ class AsanThreadSummary {
   bool announced_;
   StackTrace stack_;
   AsanThread *thread_;
+  char name_[128];
 };
 
+// AsanThreadSummary objects are never freed, so we need many of them.
+COMPILER_CHECK(sizeof(AsanThreadSummary) <= 4094);
+
 // AsanThread are stored in TSD and destroyed when the thread dies.
 class AsanThread {
  public:
index 9858cce22b076e60eefe8774e6187217cbc087b0..8db6a57cdff7cd0b177717323155b97cbe39d92f 100644 (file)
@@ -121,13 +121,14 @@ uptr AsanThreadRegistry::GetCurrentAllocatedBytes() {
 uptr AsanThreadRegistry::GetHeapSize() {
   ScopedLock lock(&mu_);
   UpdateAccumulatedStatsUnlocked();
-  return accumulated_stats_.mmaped;
+  return accumulated_stats_.mmaped - accumulated_stats_.munmaped;
 }
 
 uptr AsanThreadRegistry::GetFreeBytes() {
   ScopedLock lock(&mu_);
   UpdateAccumulatedStatsUnlocked();
   uptr total_free = accumulated_stats_.mmaped
+                  - accumulated_stats_.munmaped
                   + accumulated_stats_.really_freed
                   + accumulated_stats_.really_freed_redzones;
   uptr total_used = accumulated_stats_.malloced
index 8b7f9ef0e38340d73cb1734d22537e3bb7b0acda..02a2e08868ed0971e4117d9f43b4ffed5216ca98 100644 (file)
@@ -30,7 +30,8 @@ static AsanLock dbghelp_lock(LINKER_INITIALIZED);
 static bool dbghelp_initialized = false;
 #pragma comment(lib, "dbghelp.lib")
 
-void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp) {
+void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, bool fast) {
+  (void)fast;
   stack->max_size = max_s;
   void *tmp[kStackTraceMax];
 
index b94b303f3ac1d6a751b8c8bf9f9df313a8d2337b..47f780ceaa39ba2d8005f3311e7d57d9225535ae 100644 (file)
@@ -115,6 +115,15 @@ extern "C" {
   bool __asan_address_is_poisoned(void const volatile *addr)
       SANITIZER_INTERFACE_ATTRIBUTE;
 
+  // If at least on byte in [beg, beg+size) is poisoned, return the address
+  // of the first such byte. Otherwise return 0.
+  uptr __asan_region_is_poisoned(uptr beg, uptr size)
+      SANITIZER_INTERFACE_ATTRIBUTE;
+
+  // Print the description of addr (useful when debugging in gdb).
+  void __asan_describe_address(uptr addr)
+      SANITIZER_INTERFACE_ATTRIBUTE;
+
   // This is an internal function that is called to report an error.
   // However it is still a part of the interface because users may want to
   // set a breakpoint on this function in a debugger.
@@ -138,7 +147,7 @@ extern "C" {
   // User may provide function that would be called right when ASan detects
   // an error. This can be used to notice cases when ASan detects an error, but
   // the program crashes before ASan report is printed.
-  void __asan_on_error()
+  /* OPTIONAL */ void __asan_on_error()
       SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
 
   // User may provide its own implementation for symbolization function.
@@ -146,7 +155,8 @@ extern "C" {
   // "out_buffer". Description should be at most "out_size" bytes long.
   // User-specified function should return true if symbolization was
   // successful.
-  bool __asan_symbolize(const void *pc, char *out_buffer, int out_size)
+  /* OPTIONAL */ bool __asan_symbolize(const void *pc, char *out_buffer,
+                                       int out_size)
       SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
 
   // Returns the estimated number of bytes that will be reserved by allocator
@@ -186,20 +196,19 @@ extern "C" {
   void __asan_print_accumulated_stats()
       SANITIZER_INTERFACE_ATTRIBUTE;
 
-  // This function may be overriden by user to provide a string containing
-  // ASan runtime options. See asan_flags.h for details.
-  const char* __asan_default_options()
+  // This function may be optionally provided by user and should return
+  // a string containing ASan runtime options. See asan_flags.h for details.
+  /* OPTIONAL */ const char* __asan_default_options()
       SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
 
-  // Malloc hooks that may be overriden by user.
+  // Malloc hooks that may be optionally provided by user.
   // __asan_malloc_hook(ptr, size) is called immediately after
   //   allocation of "size" bytes, which returned "ptr".
   // __asan_free_hook(ptr) is called immediately before
   //   deallocation of "ptr".
-  // If user doesn't provide implementations of these hooks, they are no-op.
-  void __asan_malloc_hook(void *ptr, uptr size)
+  /* OPTIONAL */ void __asan_malloc_hook(void *ptr, uptr size)
       SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
-  void __asan_free_hook(void *ptr)
+  /* OPTIONAL */ void __asan_free_hook(void *ptr)
       SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
 }  // extern "C"
 
index f6e814df6f5f352a026d3988bd34e5541c73de1f..9fba976a041154b2b9f49343005ba35f89e2c21f 100644 (file)
 # define SANITIZER_WEAK_ATTRIBUTE  __attribute__((weak))
 #endif
 
+#ifdef __linux__
+# define SANITIZER_SUPPORTS_WEAK_HOOKS 1
+#else
+# define SANITIZER_SUPPORTS_WEAK_HOOKS 0
+#endif
+
 // __has_feature
 #if !defined(__has_feature)
 # define __has_feature(x) 0
@@ -73,6 +79,12 @@ extern "C" {
   // stderr.
   void __sanitizer_set_report_fd(int fd)
       SANITIZER_INTERFACE_ATTRIBUTE;
+
+  // Notify the tools that the sandbox is going to be turned on. The reserved
+  // parameter will be used in the future to hold a structure with functions
+  // that the tools may call to bypass the sandbox.
+  void __sanitizer_sandbox_on_notify(void *reserved)
+      SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
 }  // extern "C"
 
 #endif  // SANITIZER_COMMON_INTERFACE_DEFS_H
index 8094fe5d34512ed35f81b4812409716ddf686597..66bdd8830f3a66be70a892b2d1f5329eee7a2c3c 100644 (file)
 # error "Interception doesn't work on this operating system."
 #endif
 
+#include "sanitizer/common_interface_defs.h"
+
+// These typedefs should be used only in the interceptor definitions to replace
+// the standard system types (e.g. SSIZE_T instead of ssize_t)
+typedef __sanitizer::uptr SIZE_T;
+typedef __sanitizer::sptr SSIZE_T;
+typedef __sanitizer::u64  OFF_T;
+typedef __sanitizer::u64  OFF64_T;
+
 // How to use this library:
 //      1) Include this header to define your own interceptors
 //         (see details below).
index 708b2a4dc285e42bd87647858fd0ffd6644e99b1..cc23dc2425c6538acdb03f6f88e48b62d604bac9 100644 (file)
@@ -22,6 +22,7 @@ sanitizer_common_files = \
         sanitizer_stackdepot.cc \
         sanitizer_stacktrace.cc \
         sanitizer_symbolizer.cc \
+       sanitizer_symbolizer_itanium.cc \
         sanitizer_symbolizer_linux.cc \
         sanitizer_symbolizer_mac.cc \
         sanitizer_symbolizer_win.cc \
index d52f42d26106c9d1e9615e0ff0e40c062e0418c6..77b1f1e215dc0324c0a85cff30cb1bc4a0b1e250 100644 (file)
@@ -59,9 +59,9 @@ am__objects_1 = sanitizer_allocator.lo sanitizer_common.lo \
        sanitizer_flags.lo sanitizer_libc.lo sanitizer_linux.lo \
        sanitizer_mac.lo sanitizer_posix.lo sanitizer_printf.lo \
        sanitizer_stackdepot.lo sanitizer_stacktrace.lo \
-       sanitizer_symbolizer.lo sanitizer_symbolizer_linux.lo \
-       sanitizer_symbolizer_mac.lo sanitizer_symbolizer_win.lo \
-       sanitizer_win.lo
+       sanitizer_symbolizer.lo sanitizer_symbolizer_itanium.lo \
+       sanitizer_symbolizer_linux.lo sanitizer_symbolizer_mac.lo \
+       sanitizer_symbolizer_win.lo sanitizer_win.lo
 am_libsanitizer_common_la_OBJECTS = $(am__objects_1)
 libsanitizer_common_la_OBJECTS = $(am_libsanitizer_common_la_OBJECTS)
 DEFAULT_INCLUDES = -I.@am__isrc@
@@ -236,6 +236,7 @@ sanitizer_common_files = \
         sanitizer_stackdepot.cc \
         sanitizer_stacktrace.cc \
         sanitizer_symbolizer.cc \
+       sanitizer_symbolizer_itanium.cc \
         sanitizer_symbolizer_linux.cc \
         sanitizer_symbolizer_mac.cc \
         sanitizer_symbolizer_win.cc \
@@ -345,6 +346,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_stackdepot.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_stacktrace.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_itanium.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_linux.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_mac.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_win.Plo@am__quote@
index 63107bdbdb021d069f6e0350fa35f4e4be455691..d0fc315b97e1e18c5b823000db22569bd549fa98 100644 (file)
 
 namespace __sanitizer {
 
-// Maps size class id to size and back.
-template <uptr l0, uptr l1, uptr l2, uptr l3, uptr l4, uptr l5,
-          uptr s0, uptr s1, uptr s2, uptr s3, uptr s4,
-          uptr c0, uptr c1, uptr c2, uptr c3, uptr c4>
-class SplineSizeClassMap {
- private:
-  // Here we use a spline composed of 5 polynomials of oder 1.
-  // The first size class is l0, then the classes go with step s0
-  // untill they reach l1, after which they go with step s1 and so on.
-  // Steps should be powers of two for cheap division.
-  // The size of the last size class should be a power of two.
-  // There should be at most 256 size classes.
-  static const uptr u0 = 0  + (l1 - l0) / s0;
-  static const uptr u1 = u0 + (l2 - l1) / s1;
-  static const uptr u2 = u1 + (l3 - l2) / s2;
-  static const uptr u3 = u2 + (l4 - l3) / s3;
-  static const uptr u4 = u3 + (l5 - l4) / s4;
+// SizeClassMap maps allocation sizes into size classes and back.
+// Class 0 corresponds to size 0.
+// Classes 1 - 16 correspond to sizes 8 - 128 (size = class_id * 8).
+// Next 8 classes: 128 + i * 16 (i = 1 to 8).
+// Next 8 classes: 256 + i * 32 (i = 1 to 8).
+// ...
+// Next 8 classes: 2^k + i * 2^(k-3) (i = 1 to 8).
+// Last class corresponds to kMaxSize = 1 << kMaxSizeLog.
+//
+// This structure of the size class map gives us:
+//   - Efficient table-free class-to-size and size-to-class functions.
+//   - Difference between two consequent size classes is betweed 12% and 6%
+//
+// This class also gives a hint to a thread-caching allocator about the amount
+// of chunks that need to be cached per-thread:
+//  - kMaxNumCached is the maximal number of chunks per size class.
+//  - (1 << kMaxBytesCachedLog) is the maximal number of bytes per size class.
+//
+// Part of output of SizeClassMap::Print():
+//    c00 => s: 0 diff: +0 00% l 0 cached: 0 0; id 0
+//    c01 => s: 8 diff: +8 00% l 3 cached: 256 2048; id 1
+//    c02 => s: 16 diff: +8 100% l 4 cached: 256 4096; id 2
+//    ...
+//    c07 => s: 56 diff: +8 16% l 5 cached: 256 14336; id 7
+//
+//    c08 => s: 64 diff: +8 14% l 6 cached: 256 16384; id 8
+//    ...
+//    c15 => s: 120 diff: +8 07% l 6 cached: 256 30720; id 15
+//
+//    c16 => s: 128 diff: +8 06% l 7 cached: 256 32768; id 16
+//    c17 => s: 144 diff: +16 12% l 7 cached: 227 32688; id 17
+//    ...
+//    c23 => s: 240 diff: +16 07% l 7 cached: 136 32640; id 23
+//
+//    c24 => s: 256 diff: +16 06% l 8 cached: 128 32768; id 24
+//    c25 => s: 288 diff: +32 12% l 8 cached: 113 32544; id 25
+//    ...
+//    c31 => s: 480 diff: +32 07% l 8 cached: 68 32640; id 31
+//
+//    c32 => s: 512 diff: +32 06% l 9 cached: 64 32768; id 32
 
- public:
-  // The number of size classes should be a power of two for fast division.
-  static const uptr kNumClasses = u4 + 1;
-  static const uptr kMaxSize = l5;
-  static const uptr kMinSize = l0;
 
-  COMPILER_CHECK(kNumClasses <= 256);
-  COMPILER_CHECK((kNumClasses & (kNumClasses - 1)) == 0);
-  COMPILER_CHECK((kMaxSize & (kMaxSize - 1)) == 0);
+template <uptr kMaxSizeLog, uptr kMaxNumCached, uptr kMaxBytesCachedLog>
+class SizeClassMap {
+  static const uptr kMinSizeLog = 3;
+  static const uptr kMidSizeLog = kMinSizeLog + 4;
+  static const uptr kMinSize = 1 << kMinSizeLog;
+  static const uptr kMidSize = 1 << kMidSizeLog;
+  static const uptr kMidClass = kMidSize / kMinSize;
+  static const uptr S = 3;
+  static const uptr M = (1 << S) - 1;
+
+ public:
+  static const uptr kMaxSize = 1 << kMaxSizeLog;
+  static const uptr kNumClasses =
+      kMidClass + ((kMaxSizeLog - kMidSizeLog) << S) + 1;
+  COMPILER_CHECK(kNumClasses >= 32 && kNumClasses <= 256);
+  static const uptr kNumClassesRounded =
+      kNumClasses == 32  ? 32 :
+      kNumClasses <= 64  ? 64 :
+      kNumClasses <= 128 ? 128 : 256;
 
   static uptr Size(uptr class_id) {
-    if (class_id <= u0) return l0 + s0 * (class_id - 0);
-    if (class_id <= u1) return l1 + s1 * (class_id - u0);
-    if (class_id <= u2) return l2 + s2 * (class_id - u1);
-    if (class_id <= u3) return l3 + s3 * (class_id - u2);
-    if (class_id <= u4) return l4 + s4 * (class_id - u3);
-    return 0;
+    if (class_id <= kMidClass)
+      return kMinSize * class_id;
+    class_id -= kMidClass;
+    uptr t = kMidSize << (class_id >> S);
+    return t + (t >> S) * (class_id & M);
   }
+
   static uptr ClassID(uptr size) {
-    if (size <= l1) return 0  + (size - l0 + s0 - 1) / s0;
-    if (size <= l2) return u0 + (size - l1 + s1 - 1) / s1;
-    if (size <= l3) return u1 + (size - l2 + s2 - 1) / s2;
-    if (size <= l4) return u2 + (size - l3 + s3 - 1) / s3;
-    if (size <= l5) return u3 + (size - l4 + s4 - 1) / s4;
-    return 0;
+    if (size <= kMidSize)
+      return (size + kMinSize - 1) >> kMinSizeLog;
+    if (size > kMaxSize) return 0;
+    uptr l = SANITIZER_WORDSIZE - 1 - __builtin_clzl(size);
+    uptr hbits = (size >> (l - S)) & M;
+    uptr lbits = size & ((1 << (l - S)) - 1);
+    uptr l1 = l - kMidSizeLog;
+    return kMidClass + (l1 << S) + hbits + (lbits > 0);
   }
 
   static uptr MaxCached(uptr class_id) {
-    if (class_id <= u0) return c0;
-    if (class_id <= u1) return c1;
-    if (class_id <= u2) return c2;
-    if (class_id <= u3) return c3;
-    if (class_id <= u4) return c4;
-    return 0;
+    if (class_id == 0) return 0;
+    uptr n = (1UL << kMaxBytesCachedLog) / Size(class_id);
+    return Max(1UL, Min(kMaxNumCached, n));
   }
-};
 
-class DefaultSizeClassMap: public SplineSizeClassMap<
-  /* l: */1 << 4, 1 << 9,  1 << 12, 1 << 15, 1 << 18, 1 << 21,
-  /* s: */1 << 4, 1 << 6,  1 << 9,  1 << 12, 1 << 15,
-  /* c: */256,    64,      16,      4,       1> {
- private:
-  COMPILER_CHECK(kNumClasses == 256);
+  static void Print() {
+    uptr prev_s = 0;
+    uptr total_cached = 0;
+    for (uptr i = 0; i < kNumClasses; i++) {
+      uptr s = Size(i);
+      if (s >= kMidSize / 2 && (s & (s - 1)) == 0)
+        Printf("\n");
+      uptr d = s - prev_s;
+      uptr p = prev_s ? (d * 100 / prev_s) : 0;
+      uptr l = SANITIZER_WORDSIZE - 1 - __builtin_clzl(s);
+      uptr cached = MaxCached(i) * s;
+      Printf("c%02zd => s: %zd diff: +%zd %02zd%% l %zd "
+             "cached: %zd %zd; id %zd\n",
+             i, Size(i), d, p, l, MaxCached(i), cached, ClassID(s));
+      total_cached += cached;
+      prev_s = s;
+    }
+    Printf("Total cached: %zd\n", total_cached);
+  }
+
+  static void Validate() {
+    for (uptr c = 1; c < kNumClasses; c++) {
+      // Printf("Validate: c%zd\n", c);
+      uptr s = Size(c);
+      CHECK_EQ(ClassID(s), c);
+      if (c != kNumClasses - 1)
+        CHECK_EQ(ClassID(s + 1), c + 1);
+      CHECK_EQ(ClassID(s - 1), c);
+      if (c)
+        CHECK_GT(Size(c), Size(c-1));
+    }
+    CHECK_EQ(ClassID(kMaxSize + 1), 0);
+
+    for (uptr s = 1; s <= kMaxSize; s++) {
+      uptr c = ClassID(s);
+      // Printf("s%zd => c%zd\n", s, c);
+      CHECK_LT(c, kNumClasses);
+      CHECK_GE(Size(c), s);
+      if (c > 0)
+        CHECK_LT(Size(c-1), s);
+    }
+  }
 };
 
-class CompactSizeClassMap: public SplineSizeClassMap<
-  /* l: */1 << 3, 1 << 4,  1 << 7, 1 << 8, 1 << 12, 1 << 15,
-  /* s: */1 << 3, 1 << 4,  1 << 7, 1 << 8, 1 << 12,
-  /* c: */256,    64,      16,      4,       1> {
- private:
-  COMPILER_CHECK(kNumClasses <= 32);
-};
+typedef SizeClassMap<15, 256, 16> DefaultSizeClassMap;
+typedef SizeClassMap<15, 64, 14> CompactSizeClassMap;
+
 
 struct AllocatorListNode {
   AllocatorListNode *next;
@@ -97,11 +161,45 @@ struct AllocatorListNode {
 
 typedef IntrusiveList<AllocatorListNode> AllocatorFreeList;
 
+// Move at most max_count chunks from allocate_from to allocate_to.
+// This function is better be a method of AllocatorFreeList, but we can't
+// inherit it from IntrusiveList as the ancient gcc complains about non-PODness.
+static inline uptr BulkMove(uptr max_count,
+                            AllocatorFreeList *allocate_from,
+                            AllocatorFreeList *allocate_to) {
+  CHECK(!allocate_from->empty());
+  CHECK(allocate_to->empty());
+  uptr res = 0;
+  if (allocate_from->size() <= max_count) {
+    res = allocate_from->size();
+    allocate_to->append_front(allocate_from);
+    CHECK(allocate_from->empty());
+  } else {
+    for (uptr i = 0; i < max_count; i++) {
+      AllocatorListNode *node = allocate_from->front();
+      allocate_from->pop_front();
+      allocate_to->push_front(node);
+    }
+    res = max_count;
+    CHECK(!allocate_from->empty());
+  }
+  CHECK(!allocate_to->empty());
+  return res;
+}
+
+// Allocators call these callbacks on mmap/munmap.
+struct NoOpMapUnmapCallback {
+  void OnMap(uptr p, uptr size) const { }
+  void OnUnmap(uptr p, uptr size) const { }
+};
+
 // SizeClassAllocator64 -- allocator for 64-bit address space.
 //
 // Space: a portion of address space of kSpaceSize bytes starting at
 // a fixed address (kSpaceBeg). Both constants are powers of two and
 // kSpaceBeg is kSpaceSize-aligned.
+// At the beginning the entire space is mprotect-ed, then small parts of it
+// are mapped on demand.
 //
 // Region: a part of Space dedicated to a single size class.
 // There are kNumClasses Regions of equal size.
@@ -112,22 +210,35 @@ typedef IntrusiveList<AllocatorListNode> AllocatorFreeList;
 // A Region looks like this:
 // UserChunk1 ... UserChunkN <gap> MetaChunkN ... MetaChunk1
 template <const uptr kSpaceBeg, const uptr kSpaceSize,
-          const uptr kMetadataSize, class SizeClassMap>
+          const uptr kMetadataSize, class SizeClassMap,
+          class MapUnmapCallback = NoOpMapUnmapCallback>
 class SizeClassAllocator64 {
  public:
   void Init() {
-    CHECK_EQ(AllocBeg(), reinterpret_cast<uptr>(MmapFixedNoReserve(
-             AllocBeg(), AllocSize())));
+    CHECK_EQ(kSpaceBeg,
+             reinterpret_cast<uptr>(Mprotect(kSpaceBeg, kSpaceSize)));
+    MapWithCallback(kSpaceEnd, AdditionalSize());
   }
 
-  bool CanAllocate(uptr size, uptr alignment) {
+  void MapWithCallback(uptr beg, uptr size) {
+    CHECK_EQ(beg, reinterpret_cast<uptr>(MmapFixedOrDie(beg, size)));
+    MapUnmapCallback().OnMap(beg, size);
+  }
+
+  void UnmapWithCallback(uptr beg, uptr size) {
+    MapUnmapCallback().OnUnmap(beg, size);
+    UnmapOrDie(reinterpret_cast<void *>(beg), size);
+  }
+
+  static bool CanAllocate(uptr size, uptr alignment) {
     return size <= SizeClassMap::kMaxSize &&
       alignment <= SizeClassMap::kMaxSize;
   }
 
   void *Allocate(uptr size, uptr alignment) {
+    if (size < alignment) size = alignment;
     CHECK(CanAllocate(size, alignment));
-    return AllocateBySizeClass(SizeClassMap::ClassID(size));
+    return AllocateBySizeClass(ClassID(size));
   }
 
   void Deallocate(void *p) {
@@ -143,18 +254,8 @@ class SizeClassAllocator64 {
     if (region->free_list.empty()) {
       PopulateFreeList(class_id, region);
     }
-    CHECK(!region->free_list.empty());
-    uptr count = SizeClassMap::MaxCached(class_id);
-    if (region->free_list.size() <= count) {
-      free_list->append_front(&region->free_list);
-    } else {
-      for (uptr i = 0; i < count; i++) {
-        AllocatorListNode *node = region->free_list.front();
-        region->free_list.pop_front();
-        free_list->push_front(node);
-      }
-    }
-    CHECK(!free_list->empty());
+    region->n_allocated += BulkMove(SizeClassMap::MaxCached(class_id),
+                                    &region->free_list, free_list);
   }
 
   // Swallow the entire free_list for the given class_id.
@@ -162,6 +263,7 @@ class SizeClassAllocator64 {
     CHECK_LT(class_id, kNumClasses);
     RegionInfo *region = GetRegionInfo(class_id);
     SpinMutexLock l(&region->mutex);
+    region->n_freed += free_list->size();
     region->free_list.append_front(free_list);
   }
 
@@ -170,16 +272,20 @@ class SizeClassAllocator64 {
   }
 
   static uptr GetSizeClass(void *p) {
-    return (reinterpret_cast<uptr>(p) / kRegionSize) % kNumClasses;
+    return (reinterpret_cast<uptr>(p) / kRegionSize) % kNumClassesRounded;
   }
 
-  static void *GetBlockBegin(void *p) {
+  void *GetBlockBegin(void *p) {
     uptr class_id = GetSizeClass(p);
     uptr size = SizeClassMap::Size(class_id);
     uptr chunk_idx = GetChunkIdx((uptr)p, size);
     uptr reg_beg = (uptr)p & ~(kRegionSize - 1);
-    uptr begin = reg_beg + chunk_idx * size;
-    return (void*)begin;
+    uptr beg = chunk_idx * size;
+    uptr next_beg = beg + size;
+    RegionInfo *region = GetRegionInfo(class_id);
+    if (region->mapped_user >= next_beg)
+      return reinterpret_cast<void*>(reg_beg + beg);
+    return 0;
   }
 
   static uptr GetActuallyAllocatedSize(void *p) {
@@ -206,39 +312,66 @@ class SizeClassAllocator64 {
 
   // Test-only.
   void TestOnlyUnmap() {
-    UnmapOrDie(reinterpret_cast<void*>(AllocBeg()), AllocSize());
+    UnmapWithCallback(kSpaceBeg, kSpaceSize + AdditionalSize());
+  }
+
+  void PrintStats() {
+    uptr total_mapped = 0;
+    uptr n_allocated = 0;
+    uptr n_freed = 0;
+    for (uptr class_id = 1; class_id < kNumClasses; class_id++) {
+      RegionInfo *region = GetRegionInfo(class_id);
+      total_mapped += region->mapped_user;
+      n_allocated += region->n_allocated;
+      n_freed += region->n_freed;
+    }
+    Printf("Stats: SizeClassAllocator64: %zdM mapped in %zd allocations; "
+           "remains %zd\n",
+           total_mapped >> 20, n_allocated, n_allocated - n_freed);
+    for (uptr class_id = 1; class_id < kNumClasses; class_id++) {
+      RegionInfo *region = GetRegionInfo(class_id);
+      if (region->mapped_user == 0) continue;
+      Printf("  %02zd (%zd): total: %zd K allocs: %zd remains: %zd\n",
+             class_id,
+             SizeClassMap::Size(class_id),
+             region->mapped_user >> 10,
+             region->n_allocated,
+             region->n_allocated - region->n_freed);
+    }
   }
 
-  static uptr AllocBeg()  { return kSpaceBeg; }
-  static uptr AllocSize() { return kSpaceSize + AdditionalSize(); }
-
   typedef SizeClassMap SizeClassMapT;
-  static const uptr kNumClasses = SizeClassMap::kNumClasses;  // 2^k <= 256
+  static const uptr kNumClasses = SizeClassMap::kNumClasses;
+  static const uptr kNumClassesRounded = SizeClassMap::kNumClassesRounded;
 
  private:
-  static const uptr kRegionSize = kSpaceSize / kNumClasses;
+  static const uptr kRegionSize = kSpaceSize / kNumClassesRounded;
+  static const uptr kSpaceEnd = kSpaceBeg + kSpaceSize;
   COMPILER_CHECK(kSpaceBeg % kSpaceSize == 0);
-  COMPILER_CHECK(kNumClasses <= SizeClassMap::kNumClasses);
   // kRegionSize must be >= 2^32.
   COMPILER_CHECK((kRegionSize) >= (1ULL << (SANITIZER_WORDSIZE / 2)));
   // Populate the free list with at most this number of bytes at once
   // or with one element if its size is greater.
-  static const uptr kPopulateSize = 1 << 18;
+  static const uptr kPopulateSize = 1 << 15;
+  // Call mmap for user memory with at least this size.
+  static const uptr kUserMapSize = 1 << 15;
+  // Call mmap for metadata memory with at least this size.
+  static const uptr kMetaMapSize = 1 << 16;
 
   struct RegionInfo {
     SpinMutex mutex;
     AllocatorFreeList free_list;
     uptr allocated_user;  // Bytes allocated for user memory.
     uptr allocated_meta;  // Bytes allocated for metadata.
-    char padding[kCacheLineSize - 3 * sizeof(uptr) - sizeof(AllocatorFreeList)];
+    uptr mapped_user;  // Bytes mapped for user memory.
+    uptr mapped_meta;  // Bytes mapped for metadata.
+    uptr n_allocated, n_freed;  // Just stats.
   };
-  COMPILER_CHECK(sizeof(RegionInfo) == kCacheLineSize);
+  COMPILER_CHECK(sizeof(RegionInfo) >= kCacheLineSize);
 
   static uptr AdditionalSize() {
-    uptr PageSize = GetPageSizeCached();
-    uptr res = Max(sizeof(RegionInfo) * kNumClasses, PageSize);
-    CHECK_EQ(res % PageSize, 0);
-    return res;
+    return RoundUpTo(sizeof(RegionInfo) * kNumClassesRounded,
+                     GetPageSizeCached());
   }
 
   RegionInfo *GetRegionInfo(uptr class_id) {
@@ -256,11 +389,20 @@ class SizeClassAllocator64 {
   }
 
   void PopulateFreeList(uptr class_id, RegionInfo *region) {
+    CHECK(region->free_list.empty());
     uptr size = SizeClassMap::Size(class_id);
     uptr beg_idx = region->allocated_user;
     uptr end_idx = beg_idx + kPopulateSize;
-    region->free_list.clear();
     uptr region_beg = kSpaceBeg + kRegionSize * class_id;
+    if (end_idx + size > region->mapped_user) {
+      // Do the mmap for the user memory.
+      uptr map_size = kUserMapSize;
+      while (end_idx + size > region->mapped_user + map_size)
+        map_size += kUserMapSize;
+      CHECK_GE(region->mapped_user + map_size, end_idx);
+      MapWithCallback(region_beg + region->mapped_user, map_size);
+      region->mapped_user += map_size;
+    }
     uptr idx = beg_idx;
     uptr i = 0;
     do {  // do-while loop because we need to put at least one item.
@@ -270,7 +412,19 @@ class SizeClassAllocator64 {
       i++;
     } while (idx < end_idx);
     region->allocated_user += idx - beg_idx;
+    CHECK_LE(region->allocated_user, region->mapped_user);
     region->allocated_meta += i * kMetadataSize;
+    if (region->allocated_meta > region->mapped_meta) {
+      uptr map_size = kMetaMapSize;
+      while (region->allocated_meta > region->mapped_meta + map_size)
+        map_size += kMetaMapSize;
+      // Do the mmap for the metadata.
+      CHECK_GE(region->mapped_meta + map_size, region->allocated_meta);
+      MapWithCallback(region_beg + kRegionSize -
+                      region->mapped_meta - map_size, map_size);
+      region->mapped_meta += map_size;
+    }
+    CHECK_LE(region->allocated_meta, region->mapped_meta);
     if (region->allocated_user + region->allocated_meta > kRegionSize) {
       Printf("Out of memory. Dying.\n");
       Printf("The process has exhausted %zuMB for size class %zu.\n",
@@ -289,6 +443,7 @@ class SizeClassAllocator64 {
     CHECK(!region->free_list.empty());
     AllocatorListNode *node = region->free_list.front();
     region->free_list.pop_front();
+    region->n_allocated++;
     return reinterpret_cast<void*>(node);
   }
 
@@ -296,7 +451,211 @@ class SizeClassAllocator64 {
     RegionInfo *region = GetRegionInfo(class_id);
     SpinMutexLock l(&region->mutex);
     region->free_list.push_front(reinterpret_cast<AllocatorListNode*>(p));
+    region->n_freed++;
+  }
+};
+
+// SizeClassAllocator32 -- allocator for 32-bit address space.
+// This allocator can theoretically be used on 64-bit arch, but there it is less
+// efficient than SizeClassAllocator64.
+//
+// [kSpaceBeg, kSpaceBeg + kSpaceSize) is the range of addresses which can
+// be returned by MmapOrDie().
+//
+// Region:
+//   a result of a single call to MmapAlignedOrDie(kRegionSize, kRegionSize).
+// Since the regions are aligned by kRegionSize, there are exactly
+// kNumPossibleRegions possible regions in the address space and so we keep
+// an u8 array possible_regions[kNumPossibleRegions] to store the size classes.
+// 0 size class means the region is not used by the allocator.
+//
+// One Region is used to allocate chunks of a single size class.
+// A Region looks like this:
+// UserChunk1 .. UserChunkN <gap> MetaChunkN .. MetaChunk1
+//
+// In order to avoid false sharing the objects of this class should be
+// chache-line aligned.
+template <const uptr kSpaceBeg, const u64 kSpaceSize,
+          const uptr kMetadataSize, class SizeClassMap,
+          class MapUnmapCallback = NoOpMapUnmapCallback>
+class SizeClassAllocator32 {
+ public:
+  void Init() {
+    state_ = reinterpret_cast<State *>(MapWithCallback(sizeof(State)));
+  }
+
+  void *MapWithCallback(uptr size) {
+    size = RoundUpTo(size, GetPageSizeCached());
+    void *res = MmapOrDie(size, "SizeClassAllocator32");
+    MapUnmapCallback().OnMap((uptr)res, size);
+    return res;
+  }
+  void UnmapWithCallback(uptr beg, uptr size) {
+    MapUnmapCallback().OnUnmap(beg, size);
+    UnmapOrDie(reinterpret_cast<void *>(beg), size);
+  }
+
+  static bool CanAllocate(uptr size, uptr alignment) {
+    return size <= SizeClassMap::kMaxSize &&
+      alignment <= SizeClassMap::kMaxSize;
+  }
+
+  void *Allocate(uptr size, uptr alignment) {
+    if (size < alignment) size = alignment;
+    CHECK(CanAllocate(size, alignment));
+    return AllocateBySizeClass(ClassID(size));
+  }
+
+  void Deallocate(void *p) {
+    CHECK(PointerIsMine(p));
+    DeallocateBySizeClass(p, GetSizeClass(p));
+  }
+
+  void *GetMetaData(void *p) {
+    CHECK(PointerIsMine(p));
+    uptr mem = reinterpret_cast<uptr>(p);
+    uptr beg = ComputeRegionBeg(mem);
+    uptr size = SizeClassMap::Size(GetSizeClass(p));
+    u32 offset = mem - beg;
+    uptr n = offset / (u32)size;  // 32-bit division
+    uptr meta = (beg + kRegionSize) - (n + 1) * kMetadataSize;
+    return reinterpret_cast<void*>(meta);
+  }
+
+  // Allocate several chunks of the given class_id.
+  void BulkAllocate(uptr class_id, AllocatorFreeList *free_list) {
+    SizeClassInfo *sci = GetSizeClassInfo(class_id);
+    SpinMutexLock l(&sci->mutex);
+    EnsureSizeClassHasAvailableChunks(sci, class_id);
+    CHECK(!sci->free_list.empty());
+    BulkMove(SizeClassMap::MaxCached(class_id), &sci->free_list, free_list);
+  }
+
+  // Swallow the entire free_list for the given class_id.
+  void BulkDeallocate(uptr class_id, AllocatorFreeList *free_list) {
+    SizeClassInfo *sci = GetSizeClassInfo(class_id);
+    SpinMutexLock l(&sci->mutex);
+    sci->free_list.append_front(free_list);
+  }
+
+  bool PointerIsMine(void *p) {
+    return GetSizeClass(p) != 0;
+  }
+
+  uptr GetSizeClass(void *p) {
+    return state_->possible_regions[ComputeRegionId(reinterpret_cast<uptr>(p))];
+  }
+
+  void *GetBlockBegin(void *p) {
+    CHECK(PointerIsMine(p));
+    uptr mem = reinterpret_cast<uptr>(p);
+    uptr beg = ComputeRegionBeg(mem);
+    uptr size = SizeClassMap::Size(GetSizeClass(p));
+    u32 offset = mem - beg;
+    u32 n = offset / (u32)size;  // 32-bit division
+    uptr res = beg + (n * (u32)size);
+    return reinterpret_cast<void*>(res);
+  }
+
+  uptr GetActuallyAllocatedSize(void *p) {
+    CHECK(PointerIsMine(p));
+    return SizeClassMap::Size(GetSizeClass(p));
+  }
+
+  uptr ClassID(uptr size) { return SizeClassMap::ClassID(size); }
+
+  uptr TotalMemoryUsed() {
+    // No need to lock here.
+    uptr res = 0;
+    for (uptr i = 0; i < kNumPossibleRegions; i++)
+      if (state_->possible_regions[i])
+        res += kRegionSize;
+    return res;
+  }
+
+  void TestOnlyUnmap() {
+    for (uptr i = 0; i < kNumPossibleRegions; i++)
+      if (state_->possible_regions[i])
+        UnmapWithCallback((i * kRegionSize), kRegionSize);
+    UnmapWithCallback(reinterpret_cast<uptr>(state_), sizeof(State));
+  }
+
+  void PrintStats() {
+  }
+
+  typedef SizeClassMap SizeClassMapT;
+  static const uptr kNumClasses = SizeClassMap::kNumClasses;
+
+ private:
+  static const uptr kRegionSizeLog = SANITIZER_WORDSIZE == 64 ? 24 : 20;
+  static const uptr kRegionSize = 1 << kRegionSizeLog;
+  static const uptr kNumPossibleRegions = kSpaceSize / kRegionSize;
+
+  struct SizeClassInfo {
+    SpinMutex mutex;
+    AllocatorFreeList free_list;
+    char padding[kCacheLineSize - sizeof(uptr) - sizeof(AllocatorFreeList)];
+  };
+  COMPILER_CHECK(sizeof(SizeClassInfo) == kCacheLineSize);
+
+  uptr ComputeRegionId(uptr mem) {
+    uptr res = mem >> kRegionSizeLog;
+    CHECK_LT(res, kNumPossibleRegions);
+    return res;
+  }
+
+  uptr ComputeRegionBeg(uptr mem) {
+    return mem & ~(kRegionSize - 1);
+  }
+
+  uptr AllocateRegion(uptr class_id) {
+    CHECK_LT(class_id, kNumClasses);
+    uptr res = reinterpret_cast<uptr>(MmapAlignedOrDie(kRegionSize, kRegionSize,
+                                      "SizeClassAllocator32"));
+    MapUnmapCallback().OnMap(res, kRegionSize);
+    CHECK_EQ(0U, (res & (kRegionSize - 1)));
+    CHECK_EQ(0U, state_->possible_regions[ComputeRegionId(res)]);
+    state_->possible_regions[ComputeRegionId(res)] = class_id;
+    return res;
+  }
+
+  SizeClassInfo *GetSizeClassInfo(uptr class_id) {
+    CHECK_LT(class_id, kNumClasses);
+    return &state_->size_class_info_array[class_id];
+  }
+
+  void EnsureSizeClassHasAvailableChunks(SizeClassInfo *sci, uptr class_id) {
+    if (!sci->free_list.empty()) return;
+    uptr size = SizeClassMap::Size(class_id);
+    uptr reg = AllocateRegion(class_id);
+    uptr n_chunks = kRegionSize / (size + kMetadataSize);
+    for (uptr i = reg; i < reg + n_chunks * size; i += size)
+      sci->free_list.push_back(reinterpret_cast<AllocatorListNode*>(i));
+  }
+
+  void *AllocateBySizeClass(uptr class_id) {
+    CHECK_LT(class_id, kNumClasses);
+    SizeClassInfo *sci = GetSizeClassInfo(class_id);
+    SpinMutexLock l(&sci->mutex);
+    EnsureSizeClassHasAvailableChunks(sci, class_id);
+    CHECK(!sci->free_list.empty());
+    AllocatorListNode *node = sci->free_list.front();
+    sci->free_list.pop_front();
+    return reinterpret_cast<void*>(node);
   }
+
+  void DeallocateBySizeClass(void *p, uptr class_id) {
+    CHECK_LT(class_id, kNumClasses);
+    SizeClassInfo *sci = GetSizeClassInfo(class_id);
+    SpinMutexLock l(&sci->mutex);
+    sci->free_list.push_front(reinterpret_cast<AllocatorListNode*>(p));
+  }
+
+  struct State {
+    u8 possible_regions[kNumPossibleRegions];
+    SizeClassInfo size_class_info_array[kNumClasses];
+  };
+  State *state_;
 };
 
 // Objects of this type should be used as local caches for SizeClassAllocator64.
@@ -312,6 +671,7 @@ struct SizeClassAllocatorLocalCache {
   }
 
   void *Allocate(SizeClassAllocator *allocator, uptr class_id) {
+    CHECK_NE(class_id, 0UL);
     CHECK_LT(class_id, kNumClasses);
     AllocatorFreeList *free_list = &free_lists_[class_id];
     if (free_list->empty())
@@ -323,6 +683,7 @@ struct SizeClassAllocatorLocalCache {
   }
 
   void Deallocate(SizeClassAllocator *allocator, uptr class_id, void *p) {
+    CHECK_NE(class_id, 0UL);
     CHECK_LT(class_id, kNumClasses);
     AllocatorFreeList *free_list = &free_lists_[class_id];
     free_list->push_front(reinterpret_cast<AllocatorListNode*>(p));
@@ -358,6 +719,7 @@ struct SizeClassAllocatorLocalCache {
 // This class can (de)allocate only large chunks of memory using mmap/unmap.
 // The main purpose of this allocator is to cover large and rare allocation
 // sizes not covered by more efficient allocators (e.g. SizeClassAllocator64).
+template <class MapUnmapCallback = NoOpMapUnmapCallback>
 class LargeMmapAllocator {
  public:
   void Init() {
@@ -372,6 +734,7 @@ class LargeMmapAllocator {
     if (map_size < size) return 0;  // Overflow.
     uptr map_beg = reinterpret_cast<uptr>(
         MmapOrDie(map_size, "LargeMmapAllocator"));
+    MapUnmapCallback().OnMap(map_beg, map_size);
     uptr map_end = map_beg + map_size;
     uptr res = map_beg + page_size_;
     if (res & (alignment - 1))  // Align.
@@ -384,11 +747,13 @@ class LargeMmapAllocator {
     h->map_size = map_size;
     {
       SpinMutexLock l(&mutex_);
-      h->next = list_;
-      h->prev = 0;
-      if (list_)
-        list_->prev = h;
-      list_ = h;
+      uptr idx = n_chunks_++;
+      CHECK_LT(idx, kMaxNumChunks);
+      h->chunk_idx = idx;
+      chunks_[idx] = h;
+      stats.n_allocs++;
+      stats.currently_allocated += map_size;
+      stats.max_allocated = Max(stats.max_allocated, stats.currently_allocated);
     }
     return reinterpret_cast<void*>(res);
   }
@@ -397,63 +762,81 @@ class LargeMmapAllocator {
     Header *h = GetHeader(p);
     {
       SpinMutexLock l(&mutex_);
-      Header *prev = h->prev;
-      Header *next = h->next;
-      if (prev)
-        prev->next = next;
-      if (next)
-        next->prev = prev;
-      if (h == list_)
-        list_ = next;
+      uptr idx = h->chunk_idx;
+      CHECK_EQ(chunks_[idx], h);
+      CHECK_LT(idx, n_chunks_);
+      chunks_[idx] = chunks_[n_chunks_ - 1];
+      chunks_[idx]->chunk_idx = idx;
+      n_chunks_--;
+      stats.n_frees++;
+      stats.currently_allocated -= h->map_size;
     }
+    MapUnmapCallback().OnUnmap(h->map_beg, h->map_size);
     UnmapOrDie(reinterpret_cast<void*>(h->map_beg), h->map_size);
   }
 
   uptr TotalMemoryUsed() {
     SpinMutexLock l(&mutex_);
     uptr res = 0;
-    for (Header *l = list_; l; l = l->next) {
-      res += RoundUpMapSize(l->size);
+    for (uptr i = 0; i < n_chunks_; i++) {
+      Header *h = chunks_[i];
+      CHECK_EQ(h->chunk_idx, i);
+      res += RoundUpMapSize(h->size);
     }
     return res;
   }
 
   bool PointerIsMine(void *p) {
-    // Fast check.
-    if ((reinterpret_cast<uptr>(p) & (page_size_ - 1))) return false;
-    SpinMutexLock l(&mutex_);
-    for (Header *l = list_; l; l = l->next) {
-      if (GetUser(l) == p) return true;
-    }
-    return false;
+    return GetBlockBegin(p) != 0;
   }
 
   uptr GetActuallyAllocatedSize(void *p) {
-    return RoundUpMapSize(GetHeader(p)->size) - page_size_;
+    return RoundUpTo(GetHeader(p)->size, page_size_);
   }
 
   // At least page_size_/2 metadata bytes is available.
   void *GetMetaData(void *p) {
+    // Too slow: CHECK_EQ(p, GetBlockBegin(p));
+    CHECK(IsAligned(reinterpret_cast<uptr>(p), page_size_));
     return GetHeader(p) + 1;
   }
 
-  void *GetBlockBegin(void *p) {
+  void *GetBlockBegin(void *ptr) {
+    uptr p = reinterpret_cast<uptr>(ptr);
     SpinMutexLock l(&mutex_);
-    for (Header *l = list_; l; l = l->next) {
-      void *b = GetUser(l);
-      if (p >= b && p < (u8*)b + l->size)
-        return b;
+    uptr nearest_chunk = 0;
+    // Cache-friendly linear search.
+    for (uptr i = 0; i < n_chunks_; i++) {
+      uptr ch = reinterpret_cast<uptr>(chunks_[i]);
+      if (p < ch) continue;  // p is at left to this chunk, skip it.
+      if (p - ch < p - nearest_chunk)
+        nearest_chunk = ch;
     }
-    return 0;
+    if (!nearest_chunk)
+      return 0;
+    Header *h = reinterpret_cast<Header *>(nearest_chunk);
+    CHECK_GE(nearest_chunk, h->map_beg);
+    CHECK_LT(nearest_chunk, h->map_beg + h->map_size);
+    CHECK_LE(nearest_chunk, p);
+    if (h->map_beg + h->map_size < p)
+      return 0;
+    return GetUser(h);
+  }
+
+  void PrintStats() {
+    Printf("Stats: LargeMmapAllocator: allocated %zd times, "
+           "remains %zd (%zd K) max %zd M\n",
+           stats.n_allocs, stats.n_allocs - stats.n_frees,
+           stats.currently_allocated >> 10, stats.max_allocated >> 20);
   }
 
  private:
+  static const int kMaxNumChunks = 1 << FIRST_32_SECOND_64(15, 18);
   struct Header {
     uptr map_beg;
     uptr map_size;
     uptr size;
-    Header *next;
-    Header *prev;
+    uptr chunk_idx;
   };
 
   Header *GetHeader(uptr p) {
@@ -472,7 +855,11 @@ class LargeMmapAllocator {
   }
 
   uptr page_size_;
-  Header *list_;
+  Header *chunks_[kMaxNumChunks];
+  uptr n_chunks_;
+  struct Stats {
+    uptr n_allocs, n_frees, currently_allocated, max_allocated;
+  } stats;
   SpinMutex mutex_;
 };
 
@@ -501,10 +888,14 @@ class CombinedAllocator {
     if (alignment > 8)
       size = RoundUpTo(size, alignment);
     void *res;
-    if (primary_.CanAllocate(size, alignment))
-      res = cache->Allocate(&primary_, primary_.ClassID(size));
-    else
+    if (primary_.CanAllocate(size, alignment)) {
+      if (cache)  // Allocate from cache.
+        res = cache->Allocate(&primary_, primary_.ClassID(size));
+      else  // No thread-local cache, allocate directly from primary allocator.
+        res = primary_.Allocate(size, alignment);
+    } else {  // Secondary allocator does not use cache.
       res = secondary_.Allocate(size, alignment);
+    }
     if (alignment > 8)
       CHECK_EQ(reinterpret_cast<uptr>(res) & (alignment - 1), 0);
     if (cleared && res)
@@ -544,6 +935,10 @@ class CombinedAllocator {
     return secondary_.PointerIsMine(p);
   }
 
+  bool FromPrimary(void *p) {
+    return primary_.PointerIsMine(p);
+  }
+
   void *GetMetaData(void *p) {
     if (primary_.PointerIsMine(p))
       return primary_.GetMetaData(p);
@@ -572,6 +967,11 @@ class CombinedAllocator {
     cache->Drain(&primary_);
   }
 
+  void PrintStats() {
+    primary_.PrintStats();
+    secondary_.PrintStats();
+  }
+
  private:
   PrimaryAllocator primary_;
   SecondaryAllocator secondary_;
index 2c02baa954a41373168771e4701e2d5fadec93a5..55e00e2204c5d476b88333fa27e0ec327e30013a 100644 (file)
@@ -22,9 +22,31 @@ extern "C" void _mm_pause();
 extern "C" long _InterlockedExchangeAdd(  // NOLINT
     long volatile * Addend, long Value);  // NOLINT
 #pragma intrinsic(_InterlockedExchangeAdd)
-extern "C" void *InterlockedCompareExchangePointer(
+
+#ifdef _WIN64
+extern "C" void *_InterlockedCompareExchangePointer(
     void *volatile *Destination,
     void *Exchange, void *Comparand);
+#pragma intrinsic(_InterlockedCompareExchangePointer)
+#else
+// There's no _InterlockedCompareExchangePointer intrinsic on x86,
+// so call _InterlockedCompareExchange instead.
+extern "C"
+long __cdecl _InterlockedCompareExchange(  // NOLINT
+    long volatile *Destination,            // NOLINT
+    long Exchange, long Comparand);        // NOLINT
+#pragma intrinsic(_InterlockedCompareExchange)
+
+inline static void *_InterlockedCompareExchangePointer(
+    void *volatile *Destination,
+    void *Exchange, void *Comparand) {
+  return reinterpret_cast<void*>(
+      _InterlockedCompareExchange(
+          reinterpret_cast<long volatile*>(Destination),  // NOLINT
+          reinterpret_cast<long>(Exchange),               // NOLINT
+          reinterpret_cast<long>(Comparand)));            // NOLINT
+}
+#endif
 
 namespace __sanitizer {
 
@@ -113,7 +135,7 @@ INLINE bool atomic_compare_exchange_strong(volatile atomic_uintptr_t *a,
                                            uptr xchg,
                                            memory_order mo) {
   uptr cmpv = *cmp;
-  uptr prev = (uptr)InterlockedCompareExchangePointer(
+  uptr prev = (uptr)_InterlockedCompareExchangePointer(
       (void*volatile*)&a->val_dont_use, (void*)xchg, (void*)cmpv);
   if (prev == cmpv)
     return true;
index 76a55c0f8b42b8d5b15956729b1c19a96481fe00..96e8808f6d14512de7e1f5796697874ace0adfea 100644 (file)
@@ -153,6 +153,27 @@ void SortArray(uptr *array, uptr size) {
   }
 }
 
+// We want to map a chunk of address space aligned to 'alignment'.
+// We do it by maping a bit more and then unmaping redundant pieces.
+// We probably can do it with fewer syscalls in some OS-dependent way.
+void *MmapAlignedOrDie(uptr size, uptr alignment, const char *mem_type) {
+// uptr PageSize = GetPageSizeCached();
+  CHECK(IsPowerOfTwo(size));
+  CHECK(IsPowerOfTwo(alignment));
+  uptr map_size = size + alignment;
+  uptr map_res = (uptr)MmapOrDie(map_size, mem_type);
+  uptr map_end = map_res + map_size;
+  uptr res = map_res;
+  if (res & (alignment - 1))  // Not aligned.
+    res = (map_res + alignment) & ~(alignment - 1);
+  uptr end = res + size;
+  if (res != map_res)
+    UnmapOrDie((void*)map_res, res - map_res);
+  if (end != map_end)
+    UnmapOrDie((void*)end, map_end - end);
+  return (void*)res;
+}
+
 }  // namespace __sanitizer
 
 using namespace __sanitizer;  // NOLINT
@@ -178,4 +199,9 @@ void __sanitizer_set_report_fd(int fd) {
     internal_close(report_fd);
   report_fd = fd;
 }
+
+void NOINLINE __sanitizer_sandbox_on_notify(void *reserved) {
+  (void)reserved;
+  PrepareForSandboxing();
+}
 }  // extern "C"
index 5639134b031c94dd707d979c1d7e4d5ed4771206..6b104884342301083988d20c6ae85818a46f99a2 100644 (file)
@@ -42,9 +42,13 @@ void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
 void *MmapOrDie(uptr size, const char *mem_type);
 void UnmapOrDie(void *addr, uptr size);
 void *MmapFixedNoReserve(uptr fixed_addr, uptr size);
+void *MmapFixedOrDie(uptr fixed_addr, uptr size);
 void *Mprotect(uptr fixed_addr, uptr size);
+// Map aligned chunk of address space; size and alignment are powers of two.
+void *MmapAlignedOrDie(uptr size, uptr alignment, const char *mem_type);
 // Used to check if we can map shadow memory to a fixed location.
 bool MemoryRangeIsAvailable(uptr range_start, uptr range_end);
+void FlushUnneededShadowMemory(uptr addr, uptr size);
 
 // Internal allocator
 void *InternalAlloc(uptr size);
@@ -119,6 +123,7 @@ const char *GetPwd();
 void ReExec();
 bool StackSizeIsUnlimited();
 void SetStackSizeLimitInBytes(uptr limit);
+void PrepareForSandboxing();
 
 // Other
 void SleepForSeconds(int seconds);
@@ -133,6 +138,13 @@ void NORETURN Die();
 void NORETURN SANITIZER_INTERFACE_ATTRIBUTE
 CheckFailed(const char *file, int line, const char *cond, u64 v1, u64 v2);
 
+// Set the name of the current thread to 'name', return true on succees.
+// The name may be truncated to a system-dependent limit.
+bool SanitizerSetThreadName(const char *name);
+// Get the name of the current thread (no more than max_len bytes),
+// return true on succees. name should have space for at least max_len+1 bytes.
+bool SanitizerGetThreadName(char *name, int max_len);
+
 // Specific tools may override behavior of "Die" and "CheckFailed" functions
 // to do tool-specific job.
 void SetDieCallback(void (*callback)(void));
@@ -148,6 +160,12 @@ INLINE uptr RoundUpTo(uptr size, uptr boundary) {
   CHECK(IsPowerOfTwo(boundary));
   return (size + boundary - 1) & ~(boundary - 1);
 }
+INLINE uptr RoundDownTo(uptr x, uptr boundary) {
+  return x & ~(boundary - 1);
+}
+INLINE bool IsAligned(uptr a, uptr alignment) {
+  return (a & (alignment - 1)) == 0;
+}
 // Don't use std::min, std::max or std::swap, to minimize dependency
 // on libstdc++.
 template<class T> T Min(T a, T b) { return a < b ? a : b; }
diff --git a/libsanitizer/sanitizer_common/sanitizer_common_interceptors.h b/libsanitizer/sanitizer_common/sanitizer_common_interceptors.h
new file mode 100644 (file)
index 0000000..97c6b6f
--- /dev/null
@@ -0,0 +1,77 @@
+//===-- sanitizer_common_interceptors.h -------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Common function interceptors for tools like AddressSanitizer,
+// ThreadSanitizer, MemorySanitizer, etc.
+//
+// This file should be included into the tool's interceptor file,
+// which has to define it's own macros:
+//   COMMON_INTERCEPTOR_ENTER
+//   COMMON_INTERCEPTOR_READ_RANGE
+//   COMMON_INTERCEPTOR_WRITE_RANGE
+//
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_COMMON_INTERCEPTORS_H
+#define SANITIZER_COMMON_INTERCEPTORS_H
+
+#include "interception/interception.h"
+#include "sanitizer_platform_interceptors.h"
+
+#if SANITIZER_INTERCEPT_READ
+INTERCEPTOR(SSIZE_T, read, int fd, void *ptr, SIZE_T count) {
+  COMMON_INTERCEPTOR_ENTER(read, fd, ptr, count);
+  SSIZE_T res = REAL(read)(fd, ptr, count);
+  if (res > 0)
+    COMMON_INTERCEPTOR_WRITE_RANGE(ptr, res);
+  return res;
+}
+#endif
+
+#if SANITIZER_INTERCEPT_PREAD
+INTERCEPTOR(SSIZE_T, pread, int fd, void *ptr, SIZE_T count, OFF_T offset) {
+  COMMON_INTERCEPTOR_ENTER(pread, fd, ptr, count, offset);
+  SSIZE_T res = REAL(pread)(fd, ptr, count, offset);
+  if (res > 0)
+    COMMON_INTERCEPTOR_WRITE_RANGE(ptr, res);
+  return res;
+}
+#endif
+
+#if SANITIZER_INTERCEPT_PREAD64
+INTERCEPTOR(SSIZE_T, pread64, int fd, void *ptr, SIZE_T count, OFF64_T offset) {
+  COMMON_INTERCEPTOR_ENTER(pread64, fd, ptr, count, offset);
+  SSIZE_T res = REAL(pread64)(fd, ptr, count, offset);
+  if (res > 0)
+    COMMON_INTERCEPTOR_WRITE_RANGE(ptr, res);
+  return res;
+}
+#endif
+
+#if SANITIZER_INTERCEPT_READ
+# define INIT_READ INTERCEPT_FUNCTION(read)
+#else
+# define INIT_READ
+#endif
+
+#if SANITIZER_INTERCEPT_PREAD
+# define INIT_PREAD INTERCEPT_FUNCTION(pread)
+#else
+# define INIT_PREAD
+#endif
+
+#if SANITIZER_INTERCEPT_PREAD64
+# define INIT_PREAD64 INTERCEPT_FUNCTION(pread64)
+#else
+# define INIT_PREAD64
+#endif
+
+#define SANITIZER_COMMON_INTERCEPTORS_INIT \
+  INIT_READ;                               \
+  INIT_PREAD;                              \
+  INIT_PREAD64;                            \
+
+#endif  // SANITIZER_COMMON_INTERCEPTORS_H
index 4d43cd7d013396fe4b568f4440e49b9b0f089d20..b02cbd4aced227888af469f2faf557500c47ccae 100644 (file)
@@ -203,4 +203,23 @@ s64 internal_simple_strtoll(const char *nptr, char **endptr, int base) {
   }
 }
 
+bool mem_is_zero(const char *beg, uptr size) {
+  CHECK_LE(size, 1UL << FIRST_32_SECOND_64(30, 40));  // Sanity check.
+  const char *end = beg + size;
+  uptr *aligned_beg = (uptr *)RoundUpTo((uptr)beg, sizeof(uptr));
+  uptr *aligned_end = (uptr *)RoundDownTo((uptr)end, sizeof(uptr));
+  uptr all = 0;
+  // Prologue.
+  for (const char *mem = beg; mem < (char*)aligned_beg && mem < end; mem++)
+    all |= *mem;
+  // Aligned loop.
+  for (; aligned_beg < aligned_end; aligned_beg++)
+    all |= *aligned_beg;
+  // Epilogue.
+  if ((char*)aligned_end >= beg)
+    for (const char *mem = (char*)aligned_end; mem < end; mem++)
+      all |= *mem;
+  return all == 0;
+}
+
 }  // namespace __sanitizer
index 4aa4a279d402a5e017365199f2fd9a192dcee446..f193017f953eb3e2405502d4bd4a33493ff081ba 100644 (file)
@@ -45,6 +45,11 @@ char *internal_strstr(const char *haystack, const char *needle);
 // Works only for base=10 and doesn't set errno.
 s64 internal_simple_strtoll(const char *nptr, char **endptr, int base);
 
+// Return true if all bytes in [mem, mem+size) are zero.
+// Optimized for the case when the result is true.
+bool mem_is_zero(const char *mem, uptr size);
+
+
 // Memory
 void *internal_mmap(void *addr, uptr length, int prot, int flags,
                     int fd, u64 offset);
index 75f2ee18a67452eb7f0d45ba49ae949df8d41cb7..1d0bf02192cad893b1d209045aef5227557d2424 100644 (file)
@@ -17,6 +17,7 @@
 #include "sanitizer_mutex.h"
 #include "sanitizer_placement_new.h"
 #include "sanitizer_procmaps.h"
+#include "sanitizer_stacktrace.h"
 
 #include <fcntl.h>
 #include <pthread.h>
@@ -28,7 +29,9 @@
 #include <sys/time.h>
 #include <sys/types.h>
 #include <unistd.h>
+#include <unwind.h>
 #include <errno.h>
+#include <sys/prctl.h>
 
 // Are we using 32-bit or 64-bit syscalls?
 // x32 (which defines __x86_64__) has SANITIZER_WORDSIZE == 32
@@ -215,6 +218,14 @@ void ReExec() {
   execv(argv[0], argv.data());
 }
 
+void PrepareForSandboxing() {
+  // Some kinds of sandboxes may forbid filesystem access, so we won't be able
+  // to read the file mappings from /proc/self/maps. Luckily, neither the
+  // process will be able to load additional libraries, so it's fine to use the
+  // cached mappings.
+  MemoryMappingLayout::CacheMemoryMappings();
+}
+
 // ----------------- sanitizer_procmaps.h
 // Linker initialized.
 ProcSelfMapsBuff MemoryMappingLayout::cached_proc_self_maps_;
@@ -354,6 +365,75 @@ bool MemoryMappingLayout::GetObjectNameAndOffset(uptr addr, uptr *offset,
   return IterateForObjectNameAndOffset(addr, offset, filename, filename_size);
 }
 
+bool SanitizerSetThreadName(const char *name) {
+  return 0 == prctl(PR_SET_NAME, (unsigned long)name, 0, 0, 0);  // NOLINT
+}
+
+bool SanitizerGetThreadName(char *name, int max_len) {
+  char buff[17];
+  if (prctl(PR_GET_NAME, (unsigned long)buff, 0, 0, 0))  // NOLINT
+    return false;
+  internal_strncpy(name, buff, max_len);
+  name[max_len] = 0;
+  return true;
+}
+
+#ifndef SANITIZER_GO
+//------------------------- SlowUnwindStack -----------------------------------
+#ifdef __arm__
+#define UNWIND_STOP _URC_END_OF_STACK
+#define UNWIND_CONTINUE _URC_NO_REASON
+#else
+#define UNWIND_STOP _URC_NORMAL_STOP
+#define UNWIND_CONTINUE _URC_NO_REASON
+#endif
+
+uptr Unwind_GetIP(struct _Unwind_Context *ctx) {
+#ifdef __arm__
+  uptr val;
+  _Unwind_VRS_Result res = _Unwind_VRS_Get(ctx, _UVRSC_CORE,
+      15 /* r15 = PC */, _UVRSD_UINT32, &val);
+  CHECK(res == _UVRSR_OK && "_Unwind_VRS_Get failed");
+  // Clear the Thumb bit.
+  return val & ~(uptr)1;
+#else
+  return _Unwind_GetIP(ctx);
+#endif
+}
+
+_Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) {
+  StackTrace *b = (StackTrace*)param;
+  CHECK(b->size < b->max_size);
+  uptr pc = Unwind_GetIP(ctx);
+  b->trace[b->size++] = pc;
+  if (b->size == b->max_size) return UNWIND_STOP;
+  return UNWIND_CONTINUE;
+}
+
+static bool MatchPc(uptr cur_pc, uptr trace_pc) {
+  return cur_pc - trace_pc <= 64 || trace_pc - cur_pc <= 64;
+}
+
+void StackTrace::SlowUnwindStack(uptr pc, uptr max_depth) {
+  this->size = 0;
+  this->max_size = max_depth;
+  if (max_depth > 1) {
+    _Unwind_Backtrace(Unwind_Trace, this);
+    // We need to pop a few frames so that pc is on top.
+    // trace[0] belongs to the current function so we always pop it.
+    int to_pop = 1;
+    /**/ if (size > 1 && MatchPc(pc, trace[1])) to_pop = 1;
+    else if (size > 2 && MatchPc(pc, trace[2])) to_pop = 2;
+    else if (size > 3 && MatchPc(pc, trace[3])) to_pop = 3;
+    else if (size > 4 && MatchPc(pc, trace[4])) to_pop = 4;
+    else if (size > 5 && MatchPc(pc, trace[5])) to_pop = 5;
+    this->PopStackFrames(to_pop);
+  }
+  this->trace[0] = pc;
+}
+
+#endif  // #ifndef SANITIZER_GO
+
 }  // namespace __sanitizer
 
 #endif  // __linux__
index 465d0a30121a0967de2312c22cc0f660f0136cfe..0f64b306afb8b93918012b435ef0d80afbc1a5e6 100644 (file)
@@ -124,6 +124,10 @@ void ReExec() {
   UNIMPLEMENTED();
 }
 
+void PrepareForSandboxing() {
+  // Nothing here for now.
+}
+
 // ----------------- sanitizer_procmaps.h
 
 MemoryMappingLayout::MemoryMappingLayout() {
diff --git a/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h b/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h
new file mode 100644 (file)
index 0000000..e32206c
--- /dev/null
@@ -0,0 +1,27 @@
+//===-- sanitizer_platform_interceptors.h -----------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines macro telling whether sanitizer tools can/should intercept
+// given library functions on a given platform.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_internal_defs.h"
+
+#if !defined(_WIN32)
+# define SANITIZER_INTERCEPT_READ 1
+# define SANITIZER_INTERCEPT_PREAD 1
+#else
+# define SANITIZER_INTERCEPT_READ 0
+# define SANITIZER_INTERCEPT_PREAD 0
+#endif
+
+#if defined(__linux__) && !defined(ANDROID)
+# define SANITIZER_INTERCEPT_PREAD64 1
+#else
+# define SANITIZER_INTERCEPT_PREAD64 0
+#endif
index b9601eaa9436b81c8b631f49c3e847edb59a6878..17287cd950e70c51056760566d2f48ccfdcefe06 100644 (file)
@@ -91,6 +91,21 @@ void *MmapFixedNoReserve(uptr fixed_addr, uptr size) {
   return p;
 }
 
+void *MmapFixedOrDie(uptr fixed_addr, uptr size) {
+  uptr PageSize = GetPageSizeCached();
+  void *p = internal_mmap((void*)(fixed_addr & ~(PageSize - 1)),
+      RoundUpTo(size, PageSize),
+      PROT_READ | PROT_WRITE,
+      MAP_PRIVATE | MAP_ANON | MAP_FIXED,
+      -1, 0);
+  if (p == (void*)-1) {
+    Report("ERROR: Failed to allocate 0x%zx (%zd) bytes at address %p (%d)\n",
+           size, size, fixed_addr, errno);
+    CHECK("unable to mmap" && 0);
+  }
+  return p;
+}
+
 void *Mprotect(uptr fixed_addr, uptr size) {
   return internal_mmap((void*)fixed_addr, size,
                        PROT_NONE,
@@ -98,6 +113,10 @@ void *Mprotect(uptr fixed_addr, uptr size) {
                        -1, 0);
 }
 
+void FlushUnneededShadowMemory(uptr addr, uptr size) {
+  madvise((void*)addr, size, MADV_DONTNEED);
+}
+
 void *MapFileToMemory(const char *file_name, uptr *buff_size) {
   fd_t fd = internal_open(file_name, false);
   CHECK_NE(fd, kInvalidFd);
index 5876fef04f3b57d74c5bc27e3ac614be3f27db54..7771e1d34a144f4c1f61e157ff54efaafcbd11f2 100644 (file)
@@ -92,7 +92,7 @@ static int AppendPointer(char **buff, const char *buff_end, u64 ptr_value) {
 int VSNPrintf(char *buff, int buff_length,
               const char *format, va_list args) {
   static const char *kPrintfFormatsHelp =
-    "Supported Printf formats: %%(0[0-9]*)?(z|ll)?{d,u,x}; %%p; %%s; %%c\n";
+    "Supported Printf formats: %(0[0-9]*)?(z|ll)?{d,u,x}; %p; %s; %c\n";
   RAW_CHECK(format);
   RAW_CHECK(buff_length > 0);
   const char *buff_end = &buff[buff_length - 1];
diff --git a/libsanitizer/sanitizer_common/sanitizer_report_decorator.h b/libsanitizer/sanitizer_common/sanitizer_report_decorator.h
new file mode 100644 (file)
index 0000000..17f0b2e
--- /dev/null
@@ -0,0 +1,35 @@
+//===-- sanitizer_report_decorator.h ----------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Tags to decorate the sanitizer reports.
+// Currently supported tags:
+//   * None.
+//   * ANSI color sequences.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_ALLOCATOR_H
+#define SANITIZER_ALLOCATOR_H
+
+namespace __sanitizer {
+class AnsiColorDecorator {
+ public:
+  explicit AnsiColorDecorator(bool use_ansi_colors) : ansi_(use_ansi_colors) { }
+  const char *Black()        { return ansi_ ? "\033[1m\033[30m" : ""; }
+  const char *Red()          { return ansi_ ? "\033[1m\033[31m" : ""; }
+  const char *Green()        { return ansi_ ? "\033[1m\033[32m" : ""; }
+  const char *Yellow()       { return ansi_ ? "\033[1m\033[33m" : ""; }
+  const char *Blue()         { return ansi_ ? "\033[1m\033[34m" : ""; }
+  const char *Magenta()      { return ansi_ ? "\033[1m\033[35m" : ""; }
+  const char *Cyan()         { return ansi_ ? "\033[1m\033[36m" : ""; }
+  const char *White()        { return ansi_ ? "\033[1m\033[37m" : ""; }
+  const char *Default()      { return ansi_ ? "\033[1m\033[0m"  : ""; }
+ private:
+  bool ansi_;
+};
+}  // namespace __sanitizer
+#endif  // SANITIZER_ALLOCATOR_H
index d9c5b69c7a2c373377f79a151796205ef1af1544..2e22155fa7523bb5618e88ee23b77d9597723625 100644 (file)
@@ -40,6 +40,12 @@ static struct {
   atomic_uint32_t seq[kPartCount];  // Unique id generators.
 } depot;
 
+static StackDepotStats stats;
+
+StackDepotStats *StackDepotGetStats() {
+  return &stats;
+}
+
 static u32 hash(const uptr *stack, uptr size) {
   // murmur2
   const u32 m = 0x5bd1e995;
@@ -75,7 +81,7 @@ static StackDesc *tryallocDesc(uptr memsz) {
 }
 
 static StackDesc *allocDesc(uptr size) {
-  // Frist, try to allocate optimisitically.
+  // First, try to allocate optimisitically.
   uptr memsz = sizeof(StackDesc) + (size - 1) * sizeof(uptr);
   StackDesc *s = tryallocDesc(memsz);
   if (s)
@@ -91,6 +97,7 @@ static StackDesc *allocDesc(uptr size) {
     if (allocsz < memsz)
       allocsz = memsz;
     uptr mem = (uptr)MmapOrDie(allocsz, "stack depot");
+    stats.mapped += allocsz;
     atomic_store(&depot.region_end, mem + allocsz, memory_order_release);
     atomic_store(&depot.region_pos, mem, memory_order_release);
   }
@@ -154,6 +161,7 @@ u32 StackDepotPut(const uptr *stack, uptr size) {
   }
   uptr part = (h % kTabSize) / kPartSize;
   id = atomic_fetch_add(&depot.seq[part], 1, memory_order_relaxed) + 1;
+  stats.n_uniq_ids++;
   CHECK_LT(id, kMaxId);
   id |= part << kPartShift;
   CHECK_NE(id, 0);
index c4c388aa74d2fb634b7a96405918d0f844aec2d6..1e917eb53bb459c9aba8a80556baf7ae4205e36e 100644 (file)
@@ -22,6 +22,13 @@ u32 StackDepotPut(const uptr *stack, uptr size);
 // Retrieves a stored stack trace by the id.
 const uptr *StackDepotGet(u32 id, uptr *size);
 
+struct StackDepotStats {
+  uptr n_uniq_ids;
+  uptr mapped;
+};
+
+StackDepotStats *StackDepotGetStats();
+
 }  // namespace __sanitizer
 
 #endif  // SANITIZER_STACKDEPOT_H
index 308c2d90731eb2e864616e01cab0be61b294af4f..59af1c352923126fd938425c88fd93ac4b3e1ef8 100644 (file)
@@ -23,10 +23,7 @@ static const char *StripPathPrefix(const char *filepath,
 }
 
 // ----------------------- StackTrace ----------------------------- {{{1
-// PCs in stack traces are actually the return addresses, that is,
-// addresses of the next instructions after the call. That's why we
-// decrement them.
-static uptr patch_pc(uptr pc) {
+uptr StackTrace::GetPreviousInstructionPc(uptr pc) {
 #ifdef __arm__
   // Cancel Thumb bit.
   pc = pc & (~1);
@@ -69,7 +66,9 @@ void StackTrace::PrintStack(const uptr *addr, uptr size,
   InternalScopedBuffer<AddressInfo> addr_frames(64);
   uptr frame_num = 0;
   for (uptr i = 0; i < size && addr[i]; i++) {
-    uptr pc = patch_pc(addr[i]);
+    // PCs in stack traces are actually the return addresses, that is,
+    // addresses of the next instructions after the call.
+    uptr pc = GetPreviousInstructionPc(addr[i]);
     uptr addr_frames_num = 0;  // The number of stack frames for current
                                // instruction address.
     if (symbolize_callback) {
index b36a1a082c5ae54463eb0ae311e550e9ed5c8a35..c939644401cd0f6ed41717988c50fcd0441afff5 100644 (file)
@@ -42,10 +42,12 @@ struct StackTrace {
   }
 
   void FastUnwindStack(uptr pc, uptr bp, uptr stack_top, uptr stack_bottom);
+  void SlowUnwindStack(uptr pc, uptr max_depth);
 
   void PopStackFrames(uptr count);
 
   static uptr GetCurrentPc();
+  static uptr GetPreviousInstructionPc(uptr pc);
 
   static uptr CompressStack(StackTrace *stack,
                             u32 *compressed, uptr size);
index 4d7ec17fe6a854e45fe72aa85c77a865f6d6624c..0714b3824fbce7dd022a6eb8e8e7c9a4375a0beb 100644 (file)
@@ -58,6 +58,9 @@ struct AddressInfo {
 uptr SymbolizeCode(uptr address, AddressInfo *frames, uptr max_frames);
 bool SymbolizeData(uptr address, AddressInfo *frame);
 
+// Attempts to demangle the provided C++ mangled name.
+const char *Demangle(const char *Name);
+
 // Starts external symbolizer program in a subprocess. Sanitizer communicates
 // with external symbolizer via pipes.
 bool InitializeExternalSymbolizer(const char *path_to_symbolizer);
diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer_itanium.cc b/libsanitizer/sanitizer_common/sanitizer_symbolizer_itanium.cc
new file mode 100644 (file)
index 0000000..b356f9a
--- /dev/null
@@ -0,0 +1,40 @@
+//===-- sanitizer_symbolizer_itanium.cc -----------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between the sanitizer run-time libraries.
+// Itanium C++ ABI-specific implementation of symbolizer parts.
+//===----------------------------------------------------------------------===//
+#if defined(__APPLE__) || defined(__linux__)
+
+#include "sanitizer_symbolizer.h"
+
+#include <stdlib.h>
+
+// C++ demangling function, as required by Itanium C++ ABI. This is weak,
+// because we do not require a C++ ABI library to be linked to a program
+// using sanitizers; if it's not present, we'll just use the mangled name.
+namespace __cxxabiv1 {
+  extern "C" char *__cxa_demangle(const char *mangled, char *buffer,
+                                  size_t *length, int *status)
+    SANITIZER_WEAK_ATTRIBUTE;
+}
+
+const char *__sanitizer::Demangle(const char *MangledName) {
+  // FIXME: __cxa_demangle aggressively insists on allocating memory.
+  // There's not much we can do about that, short of providing our
+  // own demangler (libc++abi's implementation could be adapted so that
+  // it does not allocate). For now, we just call it anyway, and we leak
+  // the returned value.
+  if (__cxxabiv1::__cxa_demangle)
+    if (const char *Demangled =
+          __cxxabiv1::__cxa_demangle(MangledName, 0, 0, 0))
+      return Demangled;
+
+  return MangledName;
+}
+
+#endif  // __APPLE__ || __linux__
index 3b81e794e59516da3d55ff941cbd85e33b33725a..ad0053234f06def454a8fecefeaef9aaa61b7637 100644 (file)
@@ -26,6 +26,10 @@ uptr GetListOfModules(LoadedModule *modules, uptr max_modules) {
   UNIMPLEMENTED();
 };
 
+const char *Demangle(const char *MangledName) {
+  return MangledName;
+}
+
 }  // namespace __sanitizer
 
 #endif  // _WIN32
index 15ef7d96826dbd2ecdacff1df6260691b45876aa..f7300a18b60beb13252629e57c1c3e2f0b940dd0 100644 (file)
@@ -13,6 +13,7 @@
 #define WIN32_LEAN_AND_MEAN
 #define NOGDI
 #include <stdlib.h>
+#include <io.h>
 #include <windows.h>
 
 #include "sanitizer_common.h"
@@ -73,6 +74,8 @@ void UnmapOrDie(void *addr, uptr size) {
 }
 
 void *MmapFixedNoReserve(uptr fixed_addr, uptr size) {
+  // FIXME: is this really "NoReserve"? On Win32 this does not matter much,
+  // but on Win64 it does.
   void *p = VirtualAlloc((LPVOID)fixed_addr, size,
       MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
   if (p == 0)
@@ -81,6 +84,10 @@ void *MmapFixedNoReserve(uptr fixed_addr, uptr size) {
   return p;
 }
 
+void *MmapFixedOrDie(uptr fixed_addr, uptr size) {
+  return MmapFixedNoReserve(fixed_addr, size);
+}
+
 void *Mprotect(uptr fixed_addr, uptr size) {
   return VirtualAlloc((LPVOID)fixed_addr, size,
                       MEM_RESERVE | MEM_COMMIT, PAGE_NOACCESS);
@@ -127,6 +134,10 @@ void ReExec() {
   UNIMPLEMENTED();
 }
 
+void PrepareForSandboxing() {
+  // Nothing here for now.
+}
+
 bool StackSizeIsUnlimited() {
   UNIMPLEMENTED();
 }
@@ -173,7 +184,7 @@ int internal_close(fd_t fd) {
 }
 
 int internal_isatty(fd_t fd) {
-  UNIMPLEMENTED();
+  return _isatty(fd);
 }
 
 fd_t internal_open(const char *filename, bool write) {
index 26d1af24d76abd19df250d4c8d761d2e26f0b0fa..fa9c26cb73d245c0d18ccfbaa087820d87940d68 100644 (file)
@@ -31,6 +31,9 @@ tsan_files = \
         tsan_interface_ann.cc \
         tsan_mman.cc \
         tsan_rtl_report.cc \
+       tsan_fd.cc \
+        tsan_interface_java.cc \
+        tsan_mutexset.cc \
         tsan_symbolize_addr2line_linux.cc
 
 libtsan_la_SOURCES = $(tsan_files) 
index 3749819af75cf1db376741a7bc10ded7e6857ab6..c739e701c17a477641aa898cf63249e1f5762c71 100644 (file)
@@ -87,7 +87,8 @@ am__objects_1 = tsan_clock.lo tsan_interface_atomic.lo tsan_mutex.lo \
        tsan_rtl.lo tsan_stat.lo tsan_sync.lo tsan_interceptors.lo \
        tsan_md5.lo tsan_platform_mac.lo tsan_rtl_mutex.lo \
        tsan_suppressions.lo tsan_interface_ann.lo tsan_mman.lo \
-       tsan_rtl_report.lo tsan_symbolize_addr2line_linux.lo
+       tsan_rtl_report.lo tsan_fd.lo tsan_interface_java.lo \
+       tsan_mutexset.lo tsan_symbolize_addr2line_linux.lo
 am_libtsan_la_OBJECTS = $(am__objects_1)
 libtsan_la_OBJECTS = $(am_libtsan_la_OBJECTS)
 libtsan_la_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \
@@ -273,6 +274,9 @@ tsan_files = \
         tsan_interface_ann.cc \
         tsan_mman.cc \
         tsan_rtl_report.cc \
+       tsan_fd.cc \
+        tsan_interface_java.cc \
+        tsan_mutexset.cc \
         tsan_symbolize_addr2line_linux.cc
 
 libtsan_la_SOURCES = $(tsan_files) 
@@ -393,14 +397,17 @@ distclean-compile:
        -rm -f *.tab.c
 
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_clock.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_fd.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_flags.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_interceptors.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_interface.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_interface_ann.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_interface_atomic.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_interface_java.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_md5.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_mman.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_mutex.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_mutexset.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_platform_linux.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_platform_mac.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_report.Plo@am__quote@
index c14a6d196232a708967b2a5cb9a69e29e76308d9..6683a4e1abb0164a1e7560900bcb2808030b5c84 100644 (file)
@@ -137,6 +137,12 @@ T RoundDown(T p, u64 align) {
   return (T)((u64)p & ~(align - 1));
 }
 
+// Zeroizes high part, returns 'bits' lsb bits.
+template<typename T>
+T GetLsb(T v, int bits) {
+  return (T)((u64)v & ((1ull << bits) - 1));
+}
+
 struct MD5Hash {
   u64 hash[2];
   bool operator==(const MD5Hash &other) const;
diff --git a/libsanitizer/tsan/tsan_fd.cc b/libsanitizer/tsan/tsan_fd.cc
new file mode 100644 (file)
index 0000000..9aca9c5
--- /dev/null
@@ -0,0 +1,257 @@
+//===-- tsan_fd.cc --------------------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+
+#include "tsan_fd.h"
+#include "tsan_rtl.h"
+#include <sanitizer_common/sanitizer_atomic.h>
+
+namespace __tsan {
+
+const int kTableSizeL1 = 1024;
+const int kTableSizeL2 = 1024;
+const int kTableSize = kTableSizeL1 * kTableSizeL2;
+
+struct FdSync {
+  atomic_uint64_t rc;
+};
+
+struct FdDesc {
+  FdSync *sync;
+  int creation_tid;
+  u32 creation_stack;
+};
+
+struct FdContext {
+  atomic_uintptr_t tab[kTableSizeL1];
+  // Addresses used for synchronization.
+  FdSync globsync;
+  FdSync filesync;
+  FdSync socksync;
+  u64 connectsync;
+};
+
+static FdContext fdctx;
+
+static FdSync *allocsync() {
+  FdSync *s = (FdSync*)internal_alloc(MBlockFD, sizeof(FdSync));
+  atomic_store(&s->rc, 1, memory_order_relaxed);
+  return s;
+}
+
+static FdSync *ref(FdSync *s) {
+  if (s && atomic_load(&s->rc, memory_order_relaxed) != (u64)-1)
+    atomic_fetch_add(&s->rc, 1, memory_order_relaxed);
+  return s;
+}
+
+static void unref(ThreadState *thr, uptr pc, FdSync *s) {
+  if (s && atomic_load(&s->rc, memory_order_relaxed) != (u64)-1) {
+    if (atomic_fetch_sub(&s->rc, 1, memory_order_acq_rel) == 1) {
+      CHECK_NE(s, &fdctx.globsync);
+      CHECK_NE(s, &fdctx.filesync);
+      CHECK_NE(s, &fdctx.socksync);
+      SyncVar *v = CTX()->synctab.GetAndRemove(thr, pc, (uptr)s);
+      if (v)
+        DestroyAndFree(v);
+      internal_free(s);
+    }
+  }
+}
+
+static FdDesc *fddesc(ThreadState *thr, uptr pc, int fd) {
+  CHECK_LT(fd, kTableSize);
+  atomic_uintptr_t *pl1 = &fdctx.tab[fd / kTableSizeL2];
+  uptr l1 = atomic_load(pl1, memory_order_consume);
+  if (l1 == 0) {
+    uptr size = kTableSizeL2 * sizeof(FdDesc);
+    void *p = internal_alloc(MBlockFD, size);
+    internal_memset(p, 0, size);
+    MemoryResetRange(thr, (uptr)&fddesc, (uptr)p, size);
+    if (atomic_compare_exchange_strong(pl1, &l1, (uptr)p, memory_order_acq_rel))
+      l1 = (uptr)p;
+    else
+      internal_free(p);
+  }
+  return &((FdDesc*)l1)[fd % kTableSizeL2];  // NOLINT
+}
+
+// pd must be already ref'ed.
+static void init(ThreadState *thr, uptr pc, int fd, FdSync *s) {
+  FdDesc *d = fddesc(thr, pc, fd);
+  // As a matter of fact, we don't intercept all close calls.
+  // See e.g. libc __res_iclose().
+  if (d->sync) {
+    unref(thr, pc, d->sync);
+    d->sync = 0;
+  }
+  if (flags()->io_sync == 0) {
+    unref(thr, pc, s);
+  } else if (flags()->io_sync == 1) {
+    d->sync = s;
+  } else if (flags()->io_sync == 2) {
+    unref(thr, pc, s);
+    d->sync = &fdctx.globsync;
+  }
+  d->creation_tid = thr->tid;
+  d->creation_stack = CurrentStackId(thr, pc);
+  // To catch races between fd usage and open.
+  MemoryRangeImitateWrite(thr, pc, (uptr)d, 8);
+}
+
+void FdInit() {
+  atomic_store(&fdctx.globsync.rc, (u64)-1, memory_order_relaxed);
+  atomic_store(&fdctx.filesync.rc, (u64)-1, memory_order_relaxed);
+  atomic_store(&fdctx.socksync.rc, (u64)-1, memory_order_relaxed);
+}
+
+void FdOnFork(ThreadState *thr, uptr pc) {
+  // On fork() we need to reset all fd's, because the child is going
+  // close all them, and that will cause races between previous read/write
+  // and the close.
+  for (int l1 = 0; l1 < kTableSizeL1; l1++) {
+    FdDesc *tab = (FdDesc*)atomic_load(&fdctx.tab[l1], memory_order_relaxed);
+    if (tab == 0)
+      break;
+    for (int l2 = 0; l2 < kTableSizeL2; l2++) {
+      FdDesc *d = &tab[l2];
+      MemoryResetRange(thr, pc, (uptr)d, 8);
+    }
+  }
+}
+
+bool FdLocation(uptr addr, int *fd, int *tid, u32 *stack) {
+  for (int l1 = 0; l1 < kTableSizeL1; l1++) {
+    FdDesc *tab = (FdDesc*)atomic_load(&fdctx.tab[l1], memory_order_relaxed);
+    if (tab == 0)
+      break;
+    if (addr >= (uptr)tab && addr < (uptr)(tab + kTableSizeL2)) {
+      int l2 = (addr - (uptr)tab) / sizeof(FdDesc);
+      FdDesc *d = &tab[l2];
+      *fd = l1 * kTableSizeL1 + l2;
+      *tid = d->creation_tid;
+      *stack = d->creation_stack;
+      return true;
+    }
+  }
+  return false;
+}
+
+void FdAcquire(ThreadState *thr, uptr pc, int fd) {
+  FdDesc *d = fddesc(thr, pc, fd);
+  FdSync *s = d->sync;
+  DPrintf("#%d: FdAcquire(%d) -> %p\n", thr->tid, fd, s);
+  MemoryRead8Byte(thr, pc, (uptr)d);
+  if (s)
+    Acquire(thr, pc, (uptr)s);
+}
+
+void FdRelease(ThreadState *thr, uptr pc, int fd) {
+  FdDesc *d = fddesc(thr, pc, fd);
+  FdSync *s = d->sync;
+  DPrintf("#%d: FdRelease(%d) -> %p\n", thr->tid, fd, s);
+  if (s)
+    Release(thr, pc, (uptr)s);
+  MemoryRead8Byte(thr, pc, (uptr)d);
+}
+
+void FdClose(ThreadState *thr, uptr pc, int fd) {
+  DPrintf("#%d: FdClose(%d)\n", thr->tid, fd);
+  FdDesc *d = fddesc(thr, pc, fd);
+  // To catch races between fd usage and close.
+  MemoryWrite8Byte(thr, pc, (uptr)d);
+  // We need to clear it, because if we do not intercept any call out there
+  // that creates fd, we will hit false postives.
+  MemoryResetRange(thr, pc, (uptr)d, 8);
+  unref(thr, pc, d->sync);
+  d->sync = 0;
+  d->creation_tid = 0;
+  d->creation_stack = 0;
+}
+
+void FdFileCreate(ThreadState *thr, uptr pc, int fd) {
+  DPrintf("#%d: FdFileCreate(%d)\n", thr->tid, fd);
+  init(thr, pc, fd, &fdctx.filesync);
+}
+
+void FdDup(ThreadState *thr, uptr pc, int oldfd, int newfd) {
+  DPrintf("#%d: FdDup(%d, %d)\n", thr->tid, oldfd, newfd);
+  // Ignore the case when user dups not yet connected socket.
+  FdDesc *od = fddesc(thr, pc, oldfd);
+  MemoryRead8Byte(thr, pc, (uptr)od);
+  FdClose(thr, pc, newfd);
+  init(thr, pc, newfd, ref(od->sync));
+}
+
+void FdPipeCreate(ThreadState *thr, uptr pc, int rfd, int wfd) {
+  DPrintf("#%d: FdCreatePipe(%d, %d)\n", thr->tid, rfd, wfd);
+  FdSync *s = allocsync();
+  init(thr, pc, rfd, ref(s));
+  init(thr, pc, wfd, ref(s));
+  unref(thr, pc, s);
+}
+
+void FdEventCreate(ThreadState *thr, uptr pc, int fd) {
+  DPrintf("#%d: FdEventCreate(%d)\n", thr->tid, fd);
+  init(thr, pc, fd, allocsync());
+}
+
+void FdSignalCreate(ThreadState *thr, uptr pc, int fd) {
+  DPrintf("#%d: FdSignalCreate(%d)\n", thr->tid, fd);
+  init(thr, pc, fd, 0);
+}
+
+void FdInotifyCreate(ThreadState *thr, uptr pc, int fd) {
+  DPrintf("#%d: FdInotifyCreate(%d)\n", thr->tid, fd);
+  init(thr, pc, fd, 0);
+}
+
+void FdPollCreate(ThreadState *thr, uptr pc, int fd) {
+  DPrintf("#%d: FdPollCreate(%d)\n", thr->tid, fd);
+  init(thr, pc, fd, allocsync());
+}
+
+void FdSocketCreate(ThreadState *thr, uptr pc, int fd) {
+  DPrintf("#%d: FdSocketCreate(%d)\n", thr->tid, fd);
+  // It can be a UDP socket.
+  init(thr, pc, fd, &fdctx.socksync);
+}
+
+void FdSocketAccept(ThreadState *thr, uptr pc, int fd, int newfd) {
+  DPrintf("#%d: FdSocketAccept(%d, %d)\n", thr->tid, fd, newfd);
+  // Synchronize connect->accept.
+  Acquire(thr, pc, (uptr)&fdctx.connectsync);
+  init(thr, pc, newfd, &fdctx.socksync);
+}
+
+void FdSocketConnecting(ThreadState *thr, uptr pc, int fd) {
+  DPrintf("#%d: FdSocketConnecting(%d)\n", thr->tid, fd);
+  // Synchronize connect->accept.
+  Release(thr, pc, (uptr)&fdctx.connectsync);
+}
+
+void FdSocketConnect(ThreadState *thr, uptr pc, int fd) {
+  DPrintf("#%d: FdSocketConnect(%d)\n", thr->tid, fd);
+  init(thr, pc, fd, &fdctx.socksync);
+}
+
+uptr File2addr(char *path) {
+  (void)path;
+  static u64 addr;
+  return (uptr)&addr;
+}
+
+uptr Dir2addr(char *path) {
+  (void)path;
+  static u64 addr;
+  return (uptr)&addr;
+}
+
+}  //  namespace __tsan
diff --git a/libsanitizer/tsan/tsan_fd.h b/libsanitizer/tsan/tsan_fd.h
new file mode 100644 (file)
index 0000000..b4189a3
--- /dev/null
@@ -0,0 +1,62 @@
+//===-- tsan_fd.h -----------------------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+// This file handles synchronization via IO.
+// People use IO for synchronization along the lines of:
+//
+// int X;
+// int client_socket;  // initialized elsewhere
+// int server_socket;  // initialized elsewhere
+//
+// Thread 1:
+// X = 42;
+// send(client_socket, ...);
+//
+// Thread 2:
+// if (recv(server_socket, ...) > 0)
+//   assert(X == 42);
+//
+// This file determines the scope of the file descriptor (pipe, socket,
+// all local files, etc) and executes acquire and release operations on
+// the scope as necessary.  Some scopes are very fine grained (e.g. pipe
+// operations synchronize only with operations on the same pipe), while
+// others are corse-grained (e.g. all operations on local files synchronize
+// with each other).
+//===----------------------------------------------------------------------===//
+#ifndef TSAN_FD_H
+#define TSAN_FD_H
+
+#include "tsan_rtl.h"
+
+namespace __tsan {
+
+void FdInit();
+void FdAcquire(ThreadState *thr, uptr pc, int fd);
+void FdRelease(ThreadState *thr, uptr pc, int fd);
+void FdClose(ThreadState *thr, uptr pc, int fd);
+void FdFileCreate(ThreadState *thr, uptr pc, int fd);
+void FdDup(ThreadState *thr, uptr pc, int oldfd, int newfd);
+void FdPipeCreate(ThreadState *thr, uptr pc, int rfd, int wfd);
+void FdEventCreate(ThreadState *thr, uptr pc, int fd);
+void FdSignalCreate(ThreadState *thr, uptr pc, int fd);
+void FdInotifyCreate(ThreadState *thr, uptr pc, int fd);
+void FdPollCreate(ThreadState *thr, uptr pc, int fd);
+void FdSocketCreate(ThreadState *thr, uptr pc, int fd);
+void FdSocketAccept(ThreadState *thr, uptr pc, int fd, int newfd);
+void FdSocketConnecting(ThreadState *thr, uptr pc, int fd);
+void FdSocketConnect(ThreadState *thr, uptr pc, int fd);
+bool FdLocation(uptr addr, int *fd, int *tid, u32 *stack);
+void FdOnFork(ThreadState *thr, uptr pc);
+
+uptr File2addr(char *path);
+uptr Dir2addr(char *path);
+
+}  // namespace __tsan
+
+#endif  // TSAN_INTERFACE_H
index 1b726e6c8de3145dd6e4935106cb95708af70bc6..630bd75769bccad3c4b988c9cdf4ed114dc556a0 100644 (file)
@@ -56,6 +56,7 @@ void InitializeFlags(Flags *f, const char *env) {
   f->running_on_valgrind = false;
   f->external_symbolizer_path = "";
   f->history_size = kGoMode ? 1 : 2;  // There are a lot of goroutines in Go.
+  f->io_sync = 1;
 
   // Let a frontend override.
   OverrideFlags(f);
@@ -81,6 +82,7 @@ void InitializeFlags(Flags *f, const char *env) {
   ParseFlag(env, &f->stop_on_start, "stop_on_start");
   ParseFlag(env, &f->external_symbolizer_path, "external_symbolizer_path");
   ParseFlag(env, &f->history_size, "history_size");
+  ParseFlag(env, &f->io_sync, "io_sync");
 
   if (!f->report_bugs) {
     f->report_thread_leaks = false;
@@ -93,6 +95,12 @@ void InitializeFlags(Flags *f, const char *env) {
            " (must be [0..7])\n");
     Die();
   }
+
+  if (f->io_sync < 0 || f->io_sync > 2) {
+    Printf("ThreadSanitizer: incorrect value for io_sync"
+           " (must be [0..2])\n");
+    Die();
+  }
 }
 
 }  // namespace __tsan
index 6af96ec83becc55a46c9738d1a2a538315715302..ed27363c2ffff559f47c3d8ab84f308cf6b9d48d 100644 (file)
@@ -75,6 +75,11 @@ struct Flags {
   // the amount of memory accesses, up to history_size=7 that amounts to
   // 4M memory accesses.  The default value is 2 (128K memory accesses).
   int history_size;
+  // Controls level of synchronization implied by IO operations.
+  // 0 - no synchronization
+  // 1 - reasonable level of synchronization (write->read)
+  // 2 - global synchronization of all IO operations
+  int io_sync;
 };
 
 Flags *flags();
index dea6415078636524e4f04dfddbb081a797eaf157..88acebf8e81bfcc6539abe903c5eb4f7f779294e 100644 (file)
@@ -7,6 +7,8 @@
 //
 // This file is a part of ThreadSanitizer (TSan), a race detector.
 //
+// FIXME: move as many interceptors as possible into
+// sanitizer_common/sanitizer_common_interceptors.h
 //===----------------------------------------------------------------------===//
 
 #include "sanitizer_common/sanitizer_atomic.h"
@@ -18,6 +20,7 @@
 #include "tsan_platform.h"
 #include "tsan_rtl.h"
 #include "tsan_mman.h"
+#include "tsan_fd.h"
 
 using namespace __tsan;  // NOLINT
 
@@ -50,6 +53,7 @@ extern "C" void *pthread_self();
 extern "C" void _exit(int status);
 extern "C" int __cxa_atexit(void (*func)(void *arg), void *arg, void *dso);
 extern "C" int *__errno_location();
+extern "C" int fileno_unlocked(void *stream);
 const int PTHREAD_MUTEX_RECURSIVE = 1;
 const int PTHREAD_MUTEX_RECURSIVE_NP = 1;
 const int kPthreadAttrSize = 56;
@@ -124,10 +128,8 @@ static SignalContext *SigCtx(ThreadState *thr) {
   SignalContext *ctx = (SignalContext*)thr->signal_ctx;
   if (ctx == 0 && thr->is_alive) {
     ScopedInRtl in_rtl;
-    ctx = (SignalContext*)internal_alloc(
-        MBlockSignal, sizeof(*ctx));
-    MemoryResetRange(thr, 0, (uptr)ctx, sizeof(*ctx));
-    internal_memset(ctx, 0, sizeof(*ctx));
+    ctx = (SignalContext*)MmapOrDie(sizeof(*ctx), "SignalContext");
+    MemoryResetRange(thr, (uptr)&SigCtx, (uptr)ctx, sizeof(*ctx));
     thr->signal_ctx = ctx;
   }
   return ctx;
@@ -173,8 +175,8 @@ ScopedInterceptor::~ScopedInterceptor() {
     StatInc(thr, StatInt_##func); \
     const uptr caller_pc = GET_CALLER_PC(); \
     ScopedInterceptor si(thr, #func, caller_pc); \
-    /* Subtract one from pc as we need current instruction address */ \
-    const uptr pc = __sanitizer::StackTrace::GetCurrentPc() - 1; \
+    const uptr pc = __sanitizer::StackTrace::GetPreviousInstructionPc( \
+        __sanitizer::StackTrace::GetCurrentPc()); \
     (void)pc; \
 /**/
 
@@ -306,30 +308,6 @@ TSAN_INTERCEPTOR(void, siglongjmp, void *env, int val) {
   Die();
 }
 
-static uptr fd2addr(int fd) {
-  (void)fd;
-  static u64 addr;
-  return (uptr)&addr;
-}
-
-static uptr epollfd2addr(int fd) {
-  (void)fd;
-  static u64 addr;
-  return (uptr)&addr;
-}
-
-static uptr file2addr(char *path) {
-  (void)path;
-  static u64 addr;
-  return (uptr)&addr;
-}
-
-static uptr dir2addr(char *path) {
-  (void)path;
-  static u64 addr;
-  return (uptr)&addr;
-}
-
 TSAN_INTERCEPTOR(void*, malloc, uptr size) {
   void *p = 0;
   {
@@ -660,7 +638,7 @@ static void thread_finalize(void *v) {
     SignalContext *sctx = thr->signal_ctx;
     if (sctx) {
       thr->signal_ctx = 0;
-      internal_free(sctx);
+      UnmapOrDie(sctx, sizeof(*sctx));
     }
   }
 }
@@ -934,11 +912,15 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_unlock, void *m) {
   return res;
 }
 
+// libpthread.so contains several versions of pthread_cond_init symbol.
+// When we just dlsym() it, we get the wrong (old) version.
+/*
 TSAN_INTERCEPTOR(int, pthread_cond_init, void *c, void *a) {
   SCOPED_TSAN_INTERCEPTOR(pthread_cond_init, c, a);
   int res = REAL(pthread_cond_init)(c, a);
   return res;
 }
+*/
 
 TSAN_INTERCEPTOR(int, pthread_cond_destroy, void *c) {
   SCOPED_TSAN_INTERCEPTOR(pthread_cond_destroy, c);
@@ -1080,11 +1062,188 @@ TSAN_INTERCEPTOR(int, sem_getvalue, void *s, int *sval) {
   return res;
 }
 
+TSAN_INTERCEPTOR(int, open, const char *name, int flags, int mode) {
+  SCOPED_TSAN_INTERCEPTOR(open, name, flags, mode);
+  int fd = REAL(open)(name, flags, mode);
+  if (fd >= 0)
+    FdFileCreate(thr, pc, fd);
+  return fd;
+}
+
+TSAN_INTERCEPTOR(int, open64, const char *name, int flags, int mode) {
+  SCOPED_TSAN_INTERCEPTOR(open64, name, flags, mode);
+  int fd = REAL(open64)(name, flags, mode);
+  if (fd >= 0)
+    FdFileCreate(thr, pc, fd);
+  return fd;
+}
+
+TSAN_INTERCEPTOR(int, creat, const char *name, int mode) {
+  SCOPED_TSAN_INTERCEPTOR(creat, name, mode);
+  int fd = REAL(creat)(name, mode);
+  if (fd >= 0)
+    FdFileCreate(thr, pc, fd);
+  return fd;
+}
+
+TSAN_INTERCEPTOR(int, creat64, const char *name, int mode) {
+  SCOPED_TSAN_INTERCEPTOR(creat64, name, mode);
+  int fd = REAL(creat64)(name, mode);
+  if (fd >= 0)
+    FdFileCreate(thr, pc, fd);
+  return fd;
+}
+
+TSAN_INTERCEPTOR(int, dup, int oldfd) {
+  SCOPED_TSAN_INTERCEPTOR(dup, oldfd);
+  int newfd = REAL(dup)(oldfd);
+  if (oldfd >= 0 && newfd >= 0 && newfd != oldfd)
+    FdDup(thr, pc, oldfd, newfd);
+  return newfd;
+}
+
+TSAN_INTERCEPTOR(int, dup2, int oldfd, int newfd) {
+  SCOPED_TSAN_INTERCEPTOR(dup2, oldfd, newfd);
+  int newfd2 = REAL(dup2)(oldfd, newfd);
+  if (oldfd >= 0 && newfd2 >= 0 && newfd2 != oldfd)
+    FdDup(thr, pc, oldfd, newfd2);
+  return newfd2;
+}
+
+TSAN_INTERCEPTOR(int, dup3, int oldfd, int newfd, int flags) {
+  SCOPED_TSAN_INTERCEPTOR(dup3, oldfd, newfd, flags);
+  int newfd2 = REAL(dup3)(oldfd, newfd, flags);
+  if (oldfd >= 0 && newfd2 >= 0 && newfd2 != oldfd)
+    FdDup(thr, pc, oldfd, newfd2);
+  return newfd2;
+}
+
+TSAN_INTERCEPTOR(int, eventfd, unsigned initval, int flags) {
+  SCOPED_TSAN_INTERCEPTOR(eventfd, initval, flags);
+  int fd = REAL(eventfd)(initval, flags);
+  if (fd >= 0)
+    FdEventCreate(thr, pc, fd);
+  return fd;
+}
+
+TSAN_INTERCEPTOR(int, signalfd, int fd, void *mask, int flags) {
+  SCOPED_TSAN_INTERCEPTOR(signalfd, fd, mask, flags);
+  if (fd >= 0)
+    FdClose(thr, pc, fd);
+  fd = REAL(signalfd)(fd, mask, flags);
+  if (fd >= 0)
+    FdSignalCreate(thr, pc, fd);
+  return fd;
+}
+
+TSAN_INTERCEPTOR(int, inotify_init, int fake) {
+  SCOPED_TSAN_INTERCEPTOR(inotify_init, fake);
+  int fd = REAL(inotify_init)(fake);
+  if (fd >= 0)
+    FdInotifyCreate(thr, pc, fd);
+  return fd;
+}
+
+TSAN_INTERCEPTOR(int, inotify_init1, int flags) {
+  SCOPED_TSAN_INTERCEPTOR(inotify_init1, flags);
+  int fd = REAL(inotify_init1)(flags);
+  if (fd >= 0)
+    FdInotifyCreate(thr, pc, fd);
+  return fd;
+}
+
+TSAN_INTERCEPTOR(int, socket, int domain, int type, int protocol) {
+  SCOPED_TSAN_INTERCEPTOR(socket, domain, type, protocol);
+  int fd = REAL(socket)(domain, type, protocol);
+  if (fd >= 0)
+    FdSocketCreate(thr, pc, fd);
+  return fd;
+}
+
+TSAN_INTERCEPTOR(int, socketpair, int domain, int type, int protocol, int *fd) {
+  SCOPED_TSAN_INTERCEPTOR(socketpair, domain, type, protocol, fd);
+  int res = REAL(socketpair)(domain, type, protocol, fd);
+  if (res == 0 && fd[0] >= 0 && fd[1] >= 0)
+    FdPipeCreate(thr, pc, fd[0], fd[1]);
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, connect, int fd, void *addr, unsigned addrlen) {
+  SCOPED_TSAN_INTERCEPTOR(connect, fd, addr, addrlen);
+  FdSocketConnecting(thr, pc, fd);
+  int res = REAL(connect)(fd, addr, addrlen);
+  if (res == 0 && fd >= 0)
+    FdSocketConnect(thr, pc, fd);
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, accept, int fd, void *addr, unsigned *addrlen) {
+  SCOPED_TSAN_INTERCEPTOR(accept, fd, addr, addrlen);
+  int fd2 = REAL(accept)(fd, addr, addrlen);
+  if (fd >= 0 && fd2 >= 0)
+    FdSocketAccept(thr, pc, fd, fd2);
+  return fd2;
+}
+
+TSAN_INTERCEPTOR(int, accept4, int fd, void *addr, unsigned *addrlen, int f) {
+  SCOPED_TSAN_INTERCEPTOR(accept4, fd, addr, addrlen, f);
+  int fd2 = REAL(accept4)(fd, addr, addrlen, f);
+  if (fd >= 0 && fd2 >= 0)
+    FdSocketAccept(thr, pc, fd, fd2);
+  return fd2;
+}
+
+TSAN_INTERCEPTOR(int, epoll_create, int size) {
+  SCOPED_TSAN_INTERCEPTOR(epoll_create, size);
+  int fd = REAL(epoll_create)(size);
+  if (fd >= 0)
+    FdPollCreate(thr, pc, fd);
+  return fd;
+}
+
+TSAN_INTERCEPTOR(int, epoll_create1, int flags) {
+  SCOPED_TSAN_INTERCEPTOR(epoll_create1, flags);
+  int fd = REAL(epoll_create1)(flags);
+  if (fd >= 0)
+    FdPollCreate(thr, pc, fd);
+  return fd;
+}
+
+TSAN_INTERCEPTOR(int, close, int fd) {
+  SCOPED_TSAN_INTERCEPTOR(close, fd);
+  if (fd >= 0)
+    FdClose(thr, pc, fd);
+  return REAL(close)(fd);
+}
+
+TSAN_INTERCEPTOR(int, __close, int fd) {
+  SCOPED_TSAN_INTERCEPTOR(__close, fd);
+  if (fd >= 0)
+    FdClose(thr, pc, fd);
+  return REAL(__close)(fd);
+}
+
+TSAN_INTERCEPTOR(int, pipe, int *pipefd) {
+  SCOPED_TSAN_INTERCEPTOR(pipe, pipefd);
+  int res = REAL(pipe)(pipefd);
+  if (res == 0 && pipefd[0] >= 0 && pipefd[1] >= 0)
+    FdPipeCreate(thr, pc, pipefd[0], pipefd[1]);
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, pipe2, int *pipefd, int flags) {
+  SCOPED_TSAN_INTERCEPTOR(pipe2, pipefd, flags);
+  int res = REAL(pipe2)(pipefd, flags);
+  if (res == 0 && pipefd[0] >= 0 && pipefd[1] >= 0)
+    FdPipeCreate(thr, pc, pipefd[0], pipefd[1]);
+  return res;
+}
+
 TSAN_INTERCEPTOR(long_t, read, int fd, void *buf, long_t sz) {
   SCOPED_TSAN_INTERCEPTOR(read, fd, buf, sz);
   int res = REAL(read)(fd, buf, sz);
-  if (res >= 0) {
-    Acquire(thr, pc, fd2addr(fd));
+  if (res >= 0 && fd >= 0) {
+    FdAcquire(thr, pc, fd);
   }
   return res;
 }
@@ -1092,8 +1251,8 @@ TSAN_INTERCEPTOR(long_t, read, int fd, void *buf, long_t sz) {
 TSAN_INTERCEPTOR(long_t, pread, int fd, void *buf, long_t sz, unsigned off) {
   SCOPED_TSAN_INTERCEPTOR(pread, fd, buf, sz, off);
   int res = REAL(pread)(fd, buf, sz, off);
-  if (res >= 0) {
-    Acquire(thr, pc, fd2addr(fd));
+  if (res >= 0 && fd >= 0) {
+    FdAcquire(thr, pc, fd);
   }
   return res;
 }
@@ -1101,8 +1260,8 @@ TSAN_INTERCEPTOR(long_t, pread, int fd, void *buf, long_t sz, unsigned off) {
 TSAN_INTERCEPTOR(long_t, pread64, int fd, void *buf, long_t sz, u64 off) {
   SCOPED_TSAN_INTERCEPTOR(pread64, fd, buf, sz, off);
   int res = REAL(pread64)(fd, buf, sz, off);
-  if (res >= 0) {
-    Acquire(thr, pc, fd2addr(fd));
+  if (res >= 0 && fd >= 0) {
+    FdAcquire(thr, pc, fd);
   }
   return res;
 }
@@ -1110,8 +1269,8 @@ TSAN_INTERCEPTOR(long_t, pread64, int fd, void *buf, long_t sz, u64 off) {
 TSAN_INTERCEPTOR(long_t, readv, int fd, void *vec, int cnt) {
   SCOPED_TSAN_INTERCEPTOR(readv, fd, vec, cnt);
   int res = REAL(readv)(fd, vec, cnt);
-  if (res >= 0) {
-    Acquire(thr, pc, fd2addr(fd));
+  if (res >= 0 && fd >= 0) {
+    FdAcquire(thr, pc, fd);
   }
   return res;
 }
@@ -1119,57 +1278,64 @@ TSAN_INTERCEPTOR(long_t, readv, int fd, void *vec, int cnt) {
 TSAN_INTERCEPTOR(long_t, preadv64, int fd, void *vec, int cnt, u64 off) {
   SCOPED_TSAN_INTERCEPTOR(preadv64, fd, vec, cnt, off);
   int res = REAL(preadv64)(fd, vec, cnt, off);
-  if (res >= 0) {
-    Acquire(thr, pc, fd2addr(fd));
+  if (res >= 0 && fd >= 0) {
+    FdAcquire(thr, pc, fd);
   }
   return res;
 }
 
 TSAN_INTERCEPTOR(long_t, write, int fd, void *buf, long_t sz) {
   SCOPED_TSAN_INTERCEPTOR(write, fd, buf, sz);
-  Release(thr, pc, fd2addr(fd));
+  if (fd >= 0)
+    FdRelease(thr, pc, fd);
   int res = REAL(write)(fd, buf, sz);
   return res;
 }
 
 TSAN_INTERCEPTOR(long_t, pwrite, int fd, void *buf, long_t sz, unsigned off) {
   SCOPED_TSAN_INTERCEPTOR(pwrite, fd, buf, sz, off);
-  Release(thr, pc, fd2addr(fd));
+  if (fd >= 0)
+    FdRelease(thr, pc, fd);
   int res = REAL(pwrite)(fd, buf, sz, off);
   return res;
 }
 
 TSAN_INTERCEPTOR(long_t, pwrite64, int fd, void *buf, long_t sz, u64 off) {
   SCOPED_TSAN_INTERCEPTOR(pwrite64, fd, buf, sz, off);
-  Release(thr, pc, fd2addr(fd));
+  if (fd >= 0)
+    FdRelease(thr, pc, fd);
   int res = REAL(pwrite64)(fd, buf, sz, off);
   return res;
 }
 
 TSAN_INTERCEPTOR(long_t, writev, int fd, void *vec, int cnt) {
   SCOPED_TSAN_INTERCEPTOR(writev, fd, vec, cnt);
-  Release(thr, pc, fd2addr(fd));
+  if (fd >= 0)
+    FdRelease(thr, pc, fd);
   int res = REAL(writev)(fd, vec, cnt);
   return res;
 }
 
 TSAN_INTERCEPTOR(long_t, pwritev64, int fd, void *vec, int cnt, u64 off) {
   SCOPED_TSAN_INTERCEPTOR(pwritev64, fd, vec, cnt, off);
-  Release(thr, pc, fd2addr(fd));
+  if (fd >= 0)
+    FdRelease(thr, pc, fd);
   int res = REAL(pwritev64)(fd, vec, cnt, off);
   return res;
 }
 
 TSAN_INTERCEPTOR(long_t, send, int fd, void *buf, long_t len, int flags) {
   SCOPED_TSAN_INTERCEPTOR(send, fd, buf, len, flags);
-  Release(thr, pc, fd2addr(fd));
+  if (fd >= 0)
+    FdRelease(thr, pc, fd);
   int res = REAL(send)(fd, buf, len, flags);
   return res;
 }
 
 TSAN_INTERCEPTOR(long_t, sendmsg, int fd, void *msg, int flags) {
   SCOPED_TSAN_INTERCEPTOR(sendmsg, fd, msg, flags);
-  Release(thr, pc, fd2addr(fd));
+  if (fd >= 0)
+    FdRelease(thr, pc, fd);
   int res = REAL(sendmsg)(fd, msg, flags);
   return res;
 }
@@ -1177,8 +1343,8 @@ TSAN_INTERCEPTOR(long_t, sendmsg, int fd, void *msg, int flags) {
 TSAN_INTERCEPTOR(long_t, recv, int fd, void *buf, long_t len, int flags) {
   SCOPED_TSAN_INTERCEPTOR(recv, fd, buf, len, flags);
   int res = REAL(recv)(fd, buf, len, flags);
-  if (res >= 0) {
-    Acquire(thr, pc, fd2addr(fd));
+  if (res >= 0 && fd >= 0) {
+    FdAcquire(thr, pc, fd);
   }
   return res;
 }
@@ -1186,15 +1352,15 @@ TSAN_INTERCEPTOR(long_t, recv, int fd, void *buf, long_t len, int flags) {
 TSAN_INTERCEPTOR(long_t, recvmsg, int fd, void *msg, int flags) {
   SCOPED_TSAN_INTERCEPTOR(recvmsg, fd, msg, flags);
   int res = REAL(recvmsg)(fd, msg, flags);
-  if (res >= 0) {
-    Acquire(thr, pc, fd2addr(fd));
+  if (res >= 0 && fd >= 0) {
+    FdAcquire(thr, pc, fd);
   }
   return res;
 }
 
 TSAN_INTERCEPTOR(int, unlink, char *path) {
   SCOPED_TSAN_INTERCEPTOR(unlink, path);
-  Release(thr, pc, file2addr(path));
+  Release(thr, pc, File2addr(path));
   int res = REAL(unlink)(path);
   return res;
 }
@@ -1202,19 +1368,57 @@ TSAN_INTERCEPTOR(int, unlink, char *path) {
 TSAN_INTERCEPTOR(void*, fopen, char *path, char *mode) {
   SCOPED_TSAN_INTERCEPTOR(fopen, path, mode);
   void *res = REAL(fopen)(path, mode);
-  Acquire(thr, pc, file2addr(path));
+  Acquire(thr, pc, File2addr(path));
+  if (res) {
+    int fd = fileno_unlocked(res);
+    if (fd >= 0)
+      FdFileCreate(thr, pc, fd);
+  }
   return res;
 }
 
+TSAN_INTERCEPTOR(void*, freopen, char *path, char *mode, void *stream) {
+  SCOPED_TSAN_INTERCEPTOR(freopen, path, mode, stream);
+  if (stream) {
+    int fd = fileno_unlocked(stream);
+    if (fd >= 0)
+      FdClose(thr, pc, fd);
+  }
+  void *res = REAL(freopen)(path, mode, stream);
+  Acquire(thr, pc, File2addr(path));
+  if (res) {
+    int fd = fileno_unlocked(res);
+    if (fd >= 0)
+      FdFileCreate(thr, pc, fd);
+  }
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, fclose, void *stream) {
+  {
+    SCOPED_TSAN_INTERCEPTOR(fclose, stream);
+    if (stream) {
+      int fd = fileno_unlocked(stream);
+      if (fd >= 0)
+        FdClose(thr, pc, fd);
+    }
+  }
+  return REAL(fclose)(stream);
+}
+
 TSAN_INTERCEPTOR(uptr, fread, void *ptr, uptr size, uptr nmemb, void *f) {
-  SCOPED_TSAN_INTERCEPTOR(fread, ptr, size, nmemb, f);
-  MemoryAccessRange(thr, pc, (uptr)ptr, size * nmemb, true);
+  {
+    SCOPED_TSAN_INTERCEPTOR(fread, ptr, size, nmemb, f);
+    MemoryAccessRange(thr, pc, (uptr)ptr, size * nmemb, true);
+  }
   return REAL(fread)(ptr, size, nmemb, f);
 }
 
 TSAN_INTERCEPTOR(uptr, fwrite, const void *p, uptr size, uptr nmemb, void *f) {
-  SCOPED_TSAN_INTERCEPTOR(fwrite, p, size, nmemb, f);
-  MemoryAccessRange(thr, pc, (uptr)p, size * nmemb, false);
+  {
+    SCOPED_TSAN_INTERCEPTOR(fwrite, p, size, nmemb, f);
+    MemoryAccessRange(thr, pc, (uptr)p, size * nmemb, false);
+  }
   return REAL(fwrite)(p, size, nmemb, f);
 }
 
@@ -1226,7 +1430,7 @@ TSAN_INTERCEPTOR(int, puts, const char *s) {
 
 TSAN_INTERCEPTOR(int, rmdir, char *path) {
   SCOPED_TSAN_INTERCEPTOR(rmdir, path);
-  Release(thr, pc, dir2addr(path));
+  Release(thr, pc, Dir2addr(path));
   int res = REAL(rmdir)(path);
   return res;
 }
@@ -1234,14 +1438,15 @@ TSAN_INTERCEPTOR(int, rmdir, char *path) {
 TSAN_INTERCEPTOR(void*, opendir, char *path) {
   SCOPED_TSAN_INTERCEPTOR(opendir, path);
   void *res = REAL(opendir)(path);
-  Acquire(thr, pc, dir2addr(path));
+  if (res != 0)
+    Acquire(thr, pc, Dir2addr(path));
   return res;
 }
 
 TSAN_INTERCEPTOR(int, epoll_ctl, int epfd, int op, int fd, void *ev) {
   SCOPED_TSAN_INTERCEPTOR(epoll_ctl, epfd, op, fd, ev);
-  if (op == EPOLL_CTL_ADD) {
-    Release(thr, pc, epollfd2addr(epfd));
+  if (op == EPOLL_CTL_ADD && epfd >= 0) {
+    FdRelease(thr, pc, epfd);
   }
   int res = REAL(epoll_ctl)(epfd, op, fd, ev);
   return res;
@@ -1250,8 +1455,8 @@ TSAN_INTERCEPTOR(int, epoll_ctl, int epfd, int op, int fd, void *ev) {
 TSAN_INTERCEPTOR(int, epoll_wait, int epfd, void *ev, int cnt, int timeout) {
   SCOPED_TSAN_INTERCEPTOR(epoll_wait, epfd, ev, cnt, timeout);
   int res = BLOCK_REAL(epoll_wait)(epfd, ev, cnt, timeout);
-  if (res > 0) {
-    Acquire(thr, pc, epollfd2addr(epfd));
+  if (res > 0 && epfd >= 0) {
+    FdAcquire(thr, pc, epfd);
   }
   return res;
 }
@@ -1423,6 +1628,19 @@ TSAN_INTERCEPTOR(int, munlockall, void) {
   return 0;
 }
 
+TSAN_INTERCEPTOR(int, fork, int fake) {
+  SCOPED_TSAN_INTERCEPTOR(fork, fake);
+  // It's intercepted merely to process pending signals.
+  int pid = REAL(fork)(fake);
+  if (pid == 0) {
+    // child
+    FdOnFork(thr, pc);
+  } else if (pid > 0) {
+    // parent
+  }
+  return pid;
+}
+
 namespace __tsan {
 
 void ProcessPendingSignals(ThreadState *thr) {
@@ -1545,7 +1763,7 @@ void InitializeInterceptors() {
   TSAN_INTERCEPT(pthread_rwlock_timedwrlock);
   TSAN_INTERCEPT(pthread_rwlock_unlock);
 
-  TSAN_INTERCEPT(pthread_cond_init);
+  // TSAN_INTERCEPT(pthread_cond_init);
   TSAN_INTERCEPT(pthread_cond_destroy);
   TSAN_INTERCEPT(pthread_cond_signal);
   TSAN_INTERCEPT(pthread_cond_broadcast);
@@ -1566,6 +1784,28 @@ void InitializeInterceptors() {
   TSAN_INTERCEPT(sem_post);
   TSAN_INTERCEPT(sem_getvalue);
 
+  TSAN_INTERCEPT(open);
+  TSAN_INTERCEPT(open64);
+  TSAN_INTERCEPT(creat);
+  TSAN_INTERCEPT(creat64);
+  TSAN_INTERCEPT(dup);
+  TSAN_INTERCEPT(dup2);
+  TSAN_INTERCEPT(dup3);
+  TSAN_INTERCEPT(eventfd);
+  TSAN_INTERCEPT(signalfd);
+  TSAN_INTERCEPT(inotify_init);
+  TSAN_INTERCEPT(inotify_init1);
+  TSAN_INTERCEPT(socket);
+  TSAN_INTERCEPT(socketpair);
+  TSAN_INTERCEPT(connect);
+  TSAN_INTERCEPT(accept);
+  TSAN_INTERCEPT(accept4);
+  TSAN_INTERCEPT(epoll_create);
+  TSAN_INTERCEPT(epoll_create1);
+  TSAN_INTERCEPT(close);
+  TSAN_INTERCEPT(pipe);
+  TSAN_INTERCEPT(pipe2);
+
   TSAN_INTERCEPT(read);
   TSAN_INTERCEPT(pread);
   TSAN_INTERCEPT(pread64);
@@ -1583,6 +1823,8 @@ void InitializeInterceptors() {
 
   TSAN_INTERCEPT(unlink);
   TSAN_INTERCEPT(fopen);
+  TSAN_INTERCEPT(freopen);
+  TSAN_INTERCEPT(fclose);
   TSAN_INTERCEPT(fread);
   TSAN_INTERCEPT(fwrite);
   TSAN_INTERCEPT(puts);
@@ -1608,6 +1850,8 @@ void InitializeInterceptors() {
   TSAN_INTERCEPT(mlockall);
   TSAN_INTERCEPT(munlockall);
 
+  TSAN_INTERCEPT(fork);
+
   // Need to setup it, because interceptors check that the function is resolved.
   // But atexit is emitted directly into the module, so can't be resolved.
   REAL(atexit) = (int(*)(void(*)()))unreachable;
@@ -1623,6 +1867,8 @@ void InitializeInterceptors() {
     Printf("ThreadSanitizer: failed to create thread key\n");
     Die();
   }
+
+  FdInit();
 }
 
 void internal_start_thread(void(*func)(void *arg), void *arg) {
index 63860edcfbb2419249bc4c3e4737e1995c9e4656..770f8bd1014dd26cf8b4dfdc8d8a91fc916e0683 100644 (file)
@@ -229,7 +229,7 @@ static T AtomicLoad(ThreadState *thr, uptr pc, const volatile T *a,
   // Assume the access is atomic.
   if (!IsAcquireOrder(mo) && sizeof(T) <= sizeof(a))
     return *a;
-  SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, (uptr)a, false);
+  SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, false);
   thr->clock.set(thr->tid, thr->fast_state.epoch());
   thr->clock.acquire(&s->clock);
   T v = *a;
@@ -251,7 +251,7 @@ static void AtomicStore(ThreadState *thr, uptr pc, volatile T *a, T v,
     return;
   }
   __sync_synchronize();
-  SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, (uptr)a, true);
+  SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, true);
   thr->clock.set(thr->tid, thr->fast_state.epoch());
   thr->clock.ReleaseStore(&s->clock);
   *a = v;
@@ -263,7 +263,7 @@ static void AtomicStore(ThreadState *thr, uptr pc, volatile T *a, T v,
 
 template<typename T, T (*F)(volatile T *v, T op)>
 static T AtomicRMW(ThreadState *thr, uptr pc, volatile T *a, T v, morder mo) {
-  SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, (uptr)a, true);
+  SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, true);
   thr->clock.set(thr->tid, thr->fast_state.epoch());
   if (IsAcqRelOrder(mo))
     thr->clock.acq_rel(&s->clock);
@@ -322,7 +322,7 @@ template<typename T>
 static bool AtomicCAS(ThreadState *thr, uptr pc,
     volatile T *a, T *c, T v, morder mo, morder fmo) {
   (void)fmo;  // Unused because llvm does not pass it yet.
-  SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, (uptr)a, true);
+  SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, true);
   thr->clock.set(thr->tid, thr->fast_state.epoch());
   if (IsAcqRelOrder(mo))
     thr->clock.acq_rel(&s->clock);
index 9db31da0638dd6441c958e86161f885fc6ad1462..c500614acc4cae36f6ba8976a1727c9818e23dc1 100644 (file)
@@ -26,7 +26,7 @@ typedef long     __tsan_atomic64;  // NOLINT
 
 #if defined(__SIZEOF_INT128__) \
     || (__clang_major__ * 100 + __clang_minor__ >= 302)
-typedef __int128 __tsan_atomic128;
+__extension__ typedef __int128 __tsan_atomic128;
 #define __TSAN_HAS_INT128 1
 #else
 typedef char     __tsan_atomic128;
diff --git a/libsanitizer/tsan/tsan_interface_java.cc b/libsanitizer/tsan/tsan_interface_java.cc
new file mode 100644 (file)
index 0000000..d7325dc
--- /dev/null
@@ -0,0 +1,303 @@
+//===-- tsan_interface_java.cc --------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+
+#include "tsan_interface_java.h"
+#include "tsan_rtl.h"
+#include "tsan_mutex.h"
+#include "sanitizer_common/sanitizer_internal_defs.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
+
+using namespace __tsan;  // NOLINT
+
+namespace __tsan {
+
+const uptr kHeapShadow = 0x300000000000ull;
+const uptr kHeapAlignment = 8;
+
+struct BlockDesc {
+  bool begin;
+  Mutex mtx;
+  SyncVar *head;
+
+  BlockDesc()
+      : mtx(MutexTypeJavaMBlock, StatMtxJavaMBlock)
+      , head() {
+    CHECK_EQ(begin, false);
+    begin = true;
+  }
+
+  ~BlockDesc() {
+    CHECK_EQ(begin, true);
+    begin = false;
+    ThreadState *thr = cur_thread();
+    SyncVar *s = head;
+    while (s) {
+      SyncVar *s1 = s->next;
+      StatInc(thr, StatSyncDestroyed);
+      s->mtx.Lock();
+      s->mtx.Unlock();
+      thr->mset.Remove(s->GetId());
+      DestroyAndFree(s);
+      s = s1;
+    }
+  }
+};
+
+struct JavaContext {
+  const uptr heap_begin;
+  const uptr heap_size;
+  BlockDesc *heap_shadow;
+
+  JavaContext(jptr heap_begin, jptr heap_size)
+      : heap_begin(heap_begin)
+      , heap_size(heap_size) {
+    uptr size = heap_size / kHeapAlignment * sizeof(BlockDesc);
+    heap_shadow = (BlockDesc*)MmapFixedNoReserve(kHeapShadow, size);
+    if ((uptr)heap_shadow != kHeapShadow) {
+      Printf("ThreadSanitizer: failed to mmap Java heap shadow\n");
+      Die();
+    }
+  }
+};
+
+class ScopedJavaFunc {
+ public:
+  ScopedJavaFunc(ThreadState *thr, uptr pc)
+      : thr_(thr) {
+    Initialize(thr_);
+    FuncEntry(thr, pc);
+    CHECK_EQ(thr_->in_rtl, 0);
+    thr_->in_rtl++;
+  }
+
+  ~ScopedJavaFunc() {
+    thr_->in_rtl--;
+    CHECK_EQ(thr_->in_rtl, 0);
+    FuncExit(thr_);
+    // FIXME(dvyukov): process pending signals.
+  }
+
+ private:
+  ThreadState *thr_;
+};
+
+static u64 jctx_buf[sizeof(JavaContext) / sizeof(u64) + 1];
+static JavaContext *jctx;
+
+static BlockDesc *getblock(uptr addr) {
+  uptr i = (addr - jctx->heap_begin) / kHeapAlignment;
+  return &jctx->heap_shadow[i];
+}
+
+static uptr USED getmem(BlockDesc *b) {
+  uptr i = b - jctx->heap_shadow;
+  uptr p = jctx->heap_begin + i * kHeapAlignment;
+  CHECK_GE(p, jctx->heap_begin);
+  CHECK_LT(p, jctx->heap_begin + jctx->heap_size);
+  return p;
+}
+
+static BlockDesc *getblockbegin(uptr addr) {
+  for (BlockDesc *b = getblock(addr);; b--) {
+    CHECK_GE(b, jctx->heap_shadow);
+    if (b->begin)
+      return b;
+  }
+  return 0;
+}
+
+SyncVar* GetJavaSync(ThreadState *thr, uptr pc, uptr addr,
+                     bool write_lock, bool create) {
+  if (jctx == 0 || addr < jctx->heap_begin
+      || addr >= jctx->heap_begin + jctx->heap_size)
+    return 0;
+  BlockDesc *b = getblockbegin(addr);
+  DPrintf("#%d: GetJavaSync %p->%p\n", thr->tid, addr, b);
+  Lock l(&b->mtx);
+  SyncVar *s = b->head;
+  for (; s; s = s->next) {
+    if (s->addr == addr) {
+      DPrintf("#%d: found existing sync for %p\n", thr->tid, addr);
+      break;
+    }
+  }
+  if (s == 0 && create) {
+    DPrintf("#%d: creating new sync for %p\n", thr->tid, addr);
+    s = CTX()->synctab.Create(thr, pc, addr);
+    s->next = b->head;
+    b->head = s;
+  }
+  if (s) {
+    if (write_lock)
+      s->mtx.Lock();
+    else
+      s->mtx.ReadLock();
+  }
+  return s;
+}
+
+SyncVar* GetAndRemoveJavaSync(ThreadState *thr, uptr pc, uptr addr) {
+  // We do not destroy Java mutexes other than in __tsan_java_free().
+  return 0;
+}
+
+}  // namespace __tsan {
+
+#define SCOPED_JAVA_FUNC(func) \
+  ThreadState *thr = cur_thread(); \
+  const uptr caller_pc = GET_CALLER_PC(); \
+  const uptr pc = (uptr)&func; \
+  (void)pc; \
+  ScopedJavaFunc scoped(thr, caller_pc); \
+/**/
+
+void __tsan_java_init(jptr heap_begin, jptr heap_size) {
+  SCOPED_JAVA_FUNC(__tsan_java_init);
+  DPrintf("#%d: java_init(%p, %p)\n", thr->tid, heap_begin, heap_size);
+  CHECK_EQ(jctx, 0);
+  CHECK_GT(heap_begin, 0);
+  CHECK_GT(heap_size, 0);
+  CHECK_EQ(heap_begin % kHeapAlignment, 0);
+  CHECK_EQ(heap_size % kHeapAlignment, 0);
+  CHECK_LT(heap_begin, heap_begin + heap_size);
+  jctx = new(jctx_buf) JavaContext(heap_begin, heap_size);
+}
+
+int  __tsan_java_fini() {
+  SCOPED_JAVA_FUNC(__tsan_java_fini);
+  DPrintf("#%d: java_fini()\n", thr->tid);
+  CHECK_NE(jctx, 0);
+  // FIXME(dvyukov): this does not call atexit() callbacks.
+  int status = Finalize(thr);
+  DPrintf("#%d: java_fini() = %d\n", thr->tid, status);
+  return status;
+}
+
+void __tsan_java_alloc(jptr ptr, jptr size) {
+  SCOPED_JAVA_FUNC(__tsan_java_alloc);
+  DPrintf("#%d: java_alloc(%p, %p)\n", thr->tid, ptr, size);
+  CHECK_NE(jctx, 0);
+  CHECK_NE(size, 0);
+  CHECK_EQ(ptr % kHeapAlignment, 0);
+  CHECK_EQ(size % kHeapAlignment, 0);
+  CHECK_GE(ptr, jctx->heap_begin);
+  CHECK_LE(ptr + size, jctx->heap_begin + jctx->heap_size);
+
+  BlockDesc *b = getblock(ptr);
+  new(b) BlockDesc();
+}
+
+void __tsan_java_free(jptr ptr, jptr size) {
+  SCOPED_JAVA_FUNC(__tsan_java_free);
+  DPrintf("#%d: java_free(%p, %p)\n", thr->tid, ptr, size);
+  CHECK_NE(jctx, 0);
+  CHECK_NE(size, 0);
+  CHECK_EQ(ptr % kHeapAlignment, 0);
+  CHECK_EQ(size % kHeapAlignment, 0);
+  CHECK_GE(ptr, jctx->heap_begin);
+  CHECK_LE(ptr + size, jctx->heap_begin + jctx->heap_size);
+
+  BlockDesc *beg = getblock(ptr);
+  BlockDesc *end = getblock(ptr + size);
+  for (BlockDesc *b = beg; b != end; b++) {
+    if (b->begin)
+      b->~BlockDesc();
+  }
+}
+
+void __tsan_java_move(jptr src, jptr dst, jptr size) {
+  SCOPED_JAVA_FUNC(__tsan_java_move);
+  DPrintf("#%d: java_move(%p, %p, %p)\n", thr->tid, src, dst, size);
+  CHECK_NE(jctx, 0);
+  CHECK_NE(size, 0);
+  CHECK_EQ(src % kHeapAlignment, 0);
+  CHECK_EQ(dst % kHeapAlignment, 0);
+  CHECK_EQ(size % kHeapAlignment, 0);
+  CHECK_GE(src, jctx->heap_begin);
+  CHECK_LE(src + size, jctx->heap_begin + jctx->heap_size);
+  CHECK_GE(dst, jctx->heap_begin);
+  CHECK_LE(dst + size, jctx->heap_begin + jctx->heap_size);
+  CHECK(dst >= src + size || src >= dst + size);
+
+  // Assuming it's not running concurrently with threads that do
+  // memory accesses and mutex operations (stop-the-world phase).
+  {  // NOLINT
+    BlockDesc *s = getblock(src);
+    BlockDesc *d = getblock(dst);
+    BlockDesc *send = getblock(src + size);
+    for (; s != send; s++, d++) {
+      CHECK_EQ(d->begin, false);
+      if (s->begin) {
+        DPrintf("#%d: moving block %p->%p\n", thr->tid, getmem(s), getmem(d));
+        new(d) BlockDesc;
+        d->head = s->head;
+        for (SyncVar *sync = d->head; sync; sync = sync->next) {
+          uptr newaddr = sync->addr - src + dst;
+          DPrintf("#%d: moving sync %p->%p\n", thr->tid, sync->addr, newaddr);
+          sync->addr = newaddr;
+        }
+        s->head = 0;
+        s->~BlockDesc();
+      }
+    }
+  }
+
+  {  // NOLINT
+    u64 *s = (u64*)MemToShadow(src);
+    u64 *d = (u64*)MemToShadow(dst);
+    u64 *send = (u64*)MemToShadow(src + size);
+    for (; s != send; s++, d++) {
+      *d = *s;
+      *s = 0;
+    }
+  }
+}
+
+void __tsan_java_mutex_lock(jptr addr) {
+  SCOPED_JAVA_FUNC(__tsan_java_mutex_lock);
+  DPrintf("#%d: java_mutex_lock(%p)\n", thr->tid, addr);
+  CHECK_NE(jctx, 0);
+  CHECK_GE(addr, jctx->heap_begin);
+  CHECK_LT(addr, jctx->heap_begin + jctx->heap_size);
+
+  MutexLock(thr, pc, addr);
+}
+
+void __tsan_java_mutex_unlock(jptr addr) {
+  SCOPED_JAVA_FUNC(__tsan_java_mutex_unlock);
+  DPrintf("#%d: java_mutex_unlock(%p)\n", thr->tid, addr);
+  CHECK_NE(jctx, 0);
+  CHECK_GE(addr, jctx->heap_begin);
+  CHECK_LT(addr, jctx->heap_begin + jctx->heap_size);
+
+  MutexUnlock(thr, pc, addr);
+}
+
+void __tsan_java_mutex_read_lock(jptr addr) {
+  SCOPED_JAVA_FUNC(__tsan_java_mutex_read_lock);
+  DPrintf("#%d: java_mutex_read_lock(%p)\n", thr->tid, addr);
+  CHECK_NE(jctx, 0);
+  CHECK_GE(addr, jctx->heap_begin);
+  CHECK_LT(addr, jctx->heap_begin + jctx->heap_size);
+
+  MutexReadLock(thr, pc, addr);
+}
+
+void __tsan_java_mutex_read_unlock(jptr addr) {
+  SCOPED_JAVA_FUNC(__tsan_java_mutex_read_unlock);
+  DPrintf("#%d: java_mutex_read_unlock(%p)\n", thr->tid, addr);
+  CHECK_NE(jctx, 0);
+  CHECK_GE(addr, jctx->heap_begin);
+  CHECK_LT(addr, jctx->heap_begin + jctx->heap_size);
+
+  MutexReadUnlock(thr, pc, addr);
+}
diff --git a/libsanitizer/tsan/tsan_interface_java.h b/libsanitizer/tsan/tsan_interface_java.h
new file mode 100644 (file)
index 0000000..01922bc
--- /dev/null
@@ -0,0 +1,72 @@
+//===-- tsan_interface_java.h -----------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+// Interface for verification of Java or mixed Java/C++ programs.
+// The interface is intended to be used from within a JVM and notify TSan
+// about such events like Java locks and GC memory compaction.
+//
+// For plain memory accesses and function entry/exit a JVM is intended to use
+// C++ interfaces: __tsan_readN/writeN and __tsan_func_enter/exit.
+//
+// For volatile memory accesses and atomic operations JVM is intended to use
+// standard atomics API: __tsan_atomicN_load/store/etc.
+//
+// For usage examples see lit_tests/java_*.cc
+//===----------------------------------------------------------------------===//
+#ifndef TSAN_INTERFACE_JAVA_H
+#define TSAN_INTERFACE_JAVA_H
+
+#ifndef INTERFACE_ATTRIBUTE
+# define INTERFACE_ATTRIBUTE __attribute__((visibility("default")))
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef unsigned long jptr;  // NOLINT
+
+// Must be called before any other callback from Java.
+void __tsan_java_init(jptr heap_begin, jptr heap_size) INTERFACE_ATTRIBUTE;
+// Must be called when the application exits.
+// Not necessary the last callback (concurrently running threads are OK).
+// Returns exit status or 0 if tsan does not want to override it.
+int  __tsan_java_fini() INTERFACE_ATTRIBUTE;
+
+// Callback for memory allocations.
+// May be omitted for allocations that are not subject to data races
+// nor contain synchronization objects (e.g. String).
+void __tsan_java_alloc(jptr ptr, jptr size) INTERFACE_ATTRIBUTE;
+// Callback for memory free.
+// Can be aggregated for several objects (preferably).
+void __tsan_java_free(jptr ptr, jptr size) INTERFACE_ATTRIBUTE;
+// Callback for memory move by GC.
+// Can be aggregated for several objects (preferably).
+// The ranges must not overlap.
+void __tsan_java_move(jptr src, jptr dst, jptr size) INTERFACE_ATTRIBUTE;
+
+// Mutex lock.
+// Addr is any unique address associated with the mutex.
+// Must not be called on recursive reentry.
+// Object.wait() is handled as a pair of unlock/lock.
+void __tsan_java_mutex_lock(jptr addr) INTERFACE_ATTRIBUTE;
+// Mutex unlock.
+void __tsan_java_mutex_unlock(jptr addr) INTERFACE_ATTRIBUTE;
+// Mutex read lock.
+void __tsan_java_mutex_read_lock(jptr addr) INTERFACE_ATTRIBUTE;
+// Mutex read unlock.
+void __tsan_java_mutex_read_unlock(jptr addr) INTERFACE_ATTRIBUTE;
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#undef INTERFACE_ATTRIBUTE
+
+#endif  // #ifndef TSAN_INTERFACE_JAVA_H
index ba4252eccc5ddd7943e0d3db651105b11fb7828d..9a8a524f2622036a4a19d63ba855dbf001efed81 100644 (file)
@@ -58,8 +58,9 @@ void *user_alloc(ThreadState *thr, uptr pc, uptr sz, uptr align) {
   void *p = allocator()->Allocate(&thr->alloc_cache, sz, align);
   if (p == 0)
     return 0;
-  MBlock *b = (MBlock*)allocator()->GetMetaData(p);
+  MBlock *b = new(allocator()->GetMetaData(p)) MBlock;
   b->size = sz;
+  b->head = 0;
   b->alloc_tid = thr->unique_id;
   b->alloc_stack_id = CurrentStackId(thr, pc);
   if (CTX() && CTX()->initialized) {
@@ -90,6 +91,7 @@ void user_free(ThreadState *thr, uptr pc, void *p) {
   if (CTX() && CTX()->initialized && thr->in_rtl == 1) {
     MemoryRangeFreed(thr, pc, (uptr)p, b->size);
   }
+  b->~MBlock();
   allocator()->Deallocate(&thr->alloc_cache, p);
   SignalUnsafeCall(thr, pc);
 }
@@ -115,9 +117,11 @@ void *user_realloc(ThreadState *thr, uptr pc, void *p, uptr sz) {
 }
 
 MBlock *user_mblock(ThreadState *thr, void *p) {
-  // CHECK_GT(thr->in_rtl, 0);
   CHECK_NE(p, (void*)0);
-  return (MBlock*)allocator()->GetMetaData(p);
+  Allocator *a = allocator();
+  void *b = a->GetBlockBegin(p);
+  CHECK_NE(b, 0);
+  return (MBlock*)a->GetMetaData(b);
 }
 
 void invoke_malloc_hook(void *ptr, uptr size) {
index 326bda7ebd774d2c42e057877bad8a3f7bc0ebbb..8697d228730800d8b5acf2dba77e191c13e09a8b 100644 (file)
@@ -57,6 +57,7 @@ enum MBlockType {
   MBlockSuppression,
   MBlockExpectRace,
   MBlockSignal,
+  MBlockFD,
 
   // This must be the last.
   MBlockTypeCount
index 6a1e0cec53ad48d576b42791f269cfcca5e63901..716722b0897b62e51ab6869936b6ae575a6c17ee 100644 (file)
@@ -23,22 +23,28 @@ namespace __tsan {
 // then Report mutex can be locked while under Threads mutex.
 // The leaf mutexes can be locked under any other mutexes.
 // Recursive locking is not supported.
+#if TSAN_DEBUG && !TSAN_GO
 const MutexType MutexTypeLeaf = (MutexType)-1;
 static MutexType CanLockTab[MutexTypeCount][MutexTypeCount] = {
-  /*0 MutexTypeInvalid*/     {},
-  /*1 MutexTypeTrace*/       {MutexTypeLeaf},
-  /*2 MutexTypeThreads*/     {MutexTypeReport},
-  /*3 MutexTypeReport*/      {},
-  /*4 MutexTypeSyncVar*/     {},
-  /*5 MutexTypeSyncTab*/     {MutexTypeSyncVar},
-  /*6 MutexTypeSlab*/        {MutexTypeLeaf},
-  /*7 MutexTypeAnnotations*/ {},
-  /*8 MutexTypeAtExit*/      {MutexTypeSyncTab},
+  /*0  MutexTypeInvalid*/     {},
+  /*1  MutexTypeTrace*/       {MutexTypeLeaf},
+  /*2  MutexTypeThreads*/     {MutexTypeReport},
+  /*3  MutexTypeReport*/      {MutexTypeSyncTab, MutexTypeMBlock,
+                               MutexTypeJavaMBlock},
+  /*4  MutexTypeSyncVar*/     {},
+  /*5  MutexTypeSyncTab*/     {MutexTypeSyncVar},
+  /*6  MutexTypeSlab*/        {MutexTypeLeaf},
+  /*7  MutexTypeAnnotations*/ {},
+  /*8  MutexTypeAtExit*/      {MutexTypeSyncTab},
+  /*9  MutexTypeMBlock*/      {MutexTypeSyncVar},
+  /*10 MutexTypeJavaMBlock*/  {MutexTypeSyncVar},
 };
 
 static bool CanLockAdj[MutexTypeCount][MutexTypeCount];
+#endif
 
 void InitializeMutex() {
+#if TSAN_DEBUG && !TSAN_GO
   // Build the "can lock" adjacency matrix.
   // If [i][j]==true, then one can lock mutex j while under mutex i.
   const int N = MutexTypeCount;
@@ -112,14 +118,18 @@ void InitializeMutex() {
       Die();
     }
   }
+#endif
 }
 
 DeadlockDetector::DeadlockDetector() {
   // Rely on zero initialization because some mutexes can be locked before ctor.
 }
 
+#if TSAN_DEBUG && !TSAN_GO
 void DeadlockDetector::Lock(MutexType t) {
   // Printf("LOCK %d @%zu\n", t, seq_ + 1);
+  CHECK_GT(t, MutexTypeInvalid);
+  CHECK_LT(t, MutexTypeCount);
   u64 max_seq = 0;
   u64 max_idx = MutexTypeInvalid;
   for (int i = 0; i != MutexTypeCount; i++) {
@@ -148,6 +158,7 @@ void DeadlockDetector::Unlock(MutexType t) {
   CHECK(locked_[t]);
   locked_[t] = 0;
 }
+#endif
 
 const uptr kUnlocked = 0;
 const uptr kWriteLock = 1;
index 118066e75c3596ebb5176b6ab81f19ed216ee70e..6d1450593301d6bd0129b855d48ad38154563f19 100644 (file)
@@ -27,6 +27,8 @@ enum MutexType {
   MutexTypeSlab,
   MutexTypeAnnotations,
   MutexTypeAtExit,
+  MutexTypeMBlock,
+  MutexTypeJavaMBlock,
 
   // This must be the last.
   MutexTypeCount
diff --git a/libsanitizer/tsan/tsan_mutexset.cc b/libsanitizer/tsan/tsan_mutexset.cc
new file mode 100644 (file)
index 0000000..3ebae3a
--- /dev/null
@@ -0,0 +1,87 @@
+//===-- tsan_mutexset.cc --------------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#include "tsan_mutexset.h"
+#include "tsan_rtl.h"
+
+namespace __tsan {
+
+const uptr MutexSet::kMaxSize;
+
+MutexSet::MutexSet() {
+  size_ = 0;
+  internal_memset(&descs_, 0, sizeof(descs_));
+}
+
+void MutexSet::Add(u64 id, bool write, u64 epoch) {
+  // Look up existing mutex with the same id.
+  for (uptr i = 0; i < size_; i++) {
+    if (descs_[i].id == id) {
+      descs_[i].count++;
+      descs_[i].epoch = epoch;
+      return;
+    }
+  }
+  // On overflow, find the oldest mutex and drop it.
+  if (size_ == kMaxSize) {
+    u64 minepoch = (u64)-1;
+    u64 mini = (u64)-1;
+    for (uptr i = 0; i < size_; i++) {
+      if (descs_[i].epoch < minepoch) {
+        minepoch = descs_[i].epoch;
+        mini = i;
+      }
+    }
+    RemovePos(mini);
+    CHECK_EQ(size_, kMaxSize - 1);
+  }
+  // Add new mutex descriptor.
+  descs_[size_].id = id;
+  descs_[size_].write = write;
+  descs_[size_].epoch = epoch;
+  descs_[size_].count = 1;
+  size_++;
+}
+
+void MutexSet::Del(u64 id, bool write) {
+  for (uptr i = 0; i < size_; i++) {
+    if (descs_[i].id == id) {
+      if (--descs_[i].count == 0)
+        RemovePos(i);
+      return;
+    }
+  }
+}
+
+void MutexSet::Remove(u64 id) {
+  for (uptr i = 0; i < size_; i++) {
+    if (descs_[i].id == id) {
+      RemovePos(i);
+      return;
+    }
+  }
+}
+
+void MutexSet::RemovePos(uptr i) {
+  CHECK_LT(i, size_);
+  descs_[i] = descs_[size_ - 1];
+  size_--;
+}
+
+uptr MutexSet::Size() const {
+  return size_;
+}
+
+MutexSet::Desc MutexSet::Get(uptr i) const {
+  CHECK_LT(i, size_);
+  return descs_[i];
+}
+
+}  // namespace __tsan
diff --git a/libsanitizer/tsan/tsan_mutexset.h b/libsanitizer/tsan/tsan_mutexset.h
new file mode 100644 (file)
index 0000000..6924ead
--- /dev/null
@@ -0,0 +1,63 @@
+//===-- tsan_mutexset.h -----------------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+// MutexSet holds the set of mutexes currently held by a thread.
+//===----------------------------------------------------------------------===//
+#ifndef TSAN_MUTEXSET_H
+#define TSAN_MUTEXSET_H
+
+#include "tsan_defs.h"
+
+namespace __tsan {
+
+class MutexSet {
+ public:
+  // Holds limited number of mutexes.
+  // The oldest mutexes are discarded on overflow.
+  static const uptr kMaxSize = 64;
+  struct Desc {
+    u64 id;
+    u64 epoch;
+    int count;
+    bool write;
+  };
+
+  MutexSet();
+  // The 'id' is obtained from SyncVar::GetId().
+  void Add(u64 id, bool write, u64 epoch);
+  void Del(u64 id, bool write);
+  void Remove(u64 id);  // Removes the mutex completely (if it's destroyed).
+  uptr Size() const;
+  Desc Get(uptr i) const;
+
+ private:
+#ifndef TSAN_GO
+  uptr size_;
+  Desc descs_[kMaxSize];
+#endif
+
+  void RemovePos(uptr i);
+};
+
+// Go does not have mutexes, so do not spend memory and time.
+// (Go sync.Mutex is actually a semaphore -- can be unlocked
+// in different goroutine).
+#ifdef TSAN_GO
+MutexSet::MutexSet() {}
+void MutexSet::Add(u64 id, bool write, u64 epoch) {}
+void MutexSet::Del(u64 id, bool write) {}
+void MutexSet::Remove(u64 id) {}
+void MutexSet::RemovePos(uptr i) {}
+uptr MutexSet::Size() const { return 0; }
+MutexSet::Desc MutexSet::Get(uptr i) const { return Desc(); }
+#endif
+
+}  // namespace __tsan
+
+#endif  // TSAN_REPORT_H
index 5c776f14e8a600c41d5bba6245563ad93bb7c6ff..9fdc4dd46e7a5dd4018336894dfdf411a53e9bbc 100644 (file)
@@ -135,7 +135,6 @@ void FlushShadowMemory();
 
 const char *InitializePlatform();
 void FinalizePlatform();
-void MapThreadTrace(uptr addr, uptr size);
 uptr ALWAYS_INLINE INLINE GetThreadTrace(int tid) {
   uptr p = kTraceMemBegin + (uptr)tid * kTraceSize * sizeof(Event);
   DCHECK_LT(p, kTraceMemBegin + kTraceMemSize);
index 34221af16c9ef5776bcd35cd1db1b26e77269e9a..2e7cd5138d6c12907e8205ab4068ceed050f9981 100644 (file)
@@ -69,9 +69,7 @@ uptr GetShadowMemoryConsumption() {
 }
 
 void FlushShadowMemory() {
-  madvise((void*)kLinuxShadowBeg,
-          kLinuxShadowEnd - kLinuxShadowBeg,
-          MADV_DONTNEED);
+  FlushUnneededShadowMemory(kLinuxShadowBeg, kLinuxShadowEnd - kLinuxShadowBeg);
 }
 
 #ifndef TSAN_GO
@@ -118,16 +116,6 @@ void InitializeShadowMemory() {
 }
 #endif
 
-void MapThreadTrace(uptr addr, uptr size) {
-  DPrintf("Mapping trace at %p-%p(0x%zx)\n", addr, addr + size, size);
-  CHECK_GE(addr, kTraceMemBegin);
-  CHECK_LE(addr + size, kTraceMemBegin + kTraceMemSize);
-  if (addr != (uptr)MmapFixedNoReserve(addr, size)) {
-    Printf("FATAL: ThreadSanitizer can not mmap thread trace\n");
-    Die();
-  }
-}
-
 static uptr g_data_start;
 static uptr g_data_end;
 
@@ -180,18 +168,14 @@ static uptr g_tls_size;
 #else
 # define INTERNAL_FUNCTION
 #endif
-extern "C" void _dl_get_tls_static_info(size_t*, size_t*)
-    __attribute__((weak)) INTERNAL_FUNCTION;
 
 static int InitTlsSize() {
   typedef void (*get_tls_func)(size_t*, size_t*) INTERNAL_FUNCTION;
-  get_tls_func get_tls = &_dl_get_tls_static_info;
-  if (get_tls == 0) {
-    void *get_tls_static_info_ptr = dlsym(RTLD_NEXT, "_dl_get_tls_static_info");
-    CHECK_EQ(sizeof(get_tls), sizeof(get_tls_static_info_ptr));
-    internal_memcpy(&get_tls, &get_tls_static_info_ptr,
-                    sizeof(get_tls_static_info_ptr));
-  }
+  get_tls_func get_tls;
+  void *get_tls_static_info_ptr = dlsym(RTLD_NEXT, "_dl_get_tls_static_info");
+  CHECK_EQ(sizeof(get_tls), sizeof(get_tls_static_info_ptr));
+  internal_memcpy(&get_tls, &get_tls_static_info_ptr,
+                  sizeof(get_tls_static_info_ptr));
   CHECK_NE(get_tls, 0);
   size_t tls_size = 0;
   size_t tls_align = 0;
@@ -220,29 +204,35 @@ const char *InitializePlatform() {
     // Disable core dumps, dumping of 16TB usually takes a bit long.
     setlim(RLIMIT_CORE, 0);
   }
-  bool reexec = false;
-  // TSan doesn't play well with unlimited stack size (as stack
-  // overlaps with shadow memory). If we detect unlimited stack size,
-  // we re-exec the program with limited stack size as a best effort.
-  if (getlim(RLIMIT_STACK) == (rlim_t)-1) {
-    const uptr kMaxStackSize = 32 * 1024 * 1024;
-    Report("WARNING: Program is run with unlimited stack size, which "
-           "wouldn't work with ThreadSanitizer.\n");
-    Report("Re-execing with stack size limited to %zd bytes.\n", kMaxStackSize);
-    SetStackSizeLimitInBytes(kMaxStackSize);
-    reexec = true;
-  }
 
-  if (getlim(RLIMIT_AS) != (rlim_t)-1) {
-    Report("WARNING: Program is run with limited virtual address space, which "
-           "wouldn't work with ThreadSanitizer.\n");
-    Report("Re-execing with unlimited virtual address space.\n");
-    setlim(RLIMIT_AS, -1);
-    reexec = true;
-  }
+  // Go maps shadow memory lazily and works fine with limited address space.
+  // Unlimited stack is not a problem as well, because the executable
+  // is not compiled with -pie.
+  if (kCppMode) {
+    bool reexec = false;
+    // TSan doesn't play well with unlimited stack size (as stack
+    // overlaps with shadow memory). If we detect unlimited stack size,
+    // we re-exec the program with limited stack size as a best effort.
+    if (getlim(RLIMIT_STACK) == (rlim_t)-1) {
+      const uptr kMaxStackSize = 32 * 1024 * 1024;
+      Report("WARNING: Program is run with unlimited stack size, which "
+             "wouldn't work with ThreadSanitizer.\n");
+      Report("Re-execing with stack size limited to %zd bytes.\n",
+             kMaxStackSize);
+      SetStackSizeLimitInBytes(kMaxStackSize);
+      reexec = true;
+    }
 
-  if (reexec)
-    ReExec();
+    if (getlim(RLIMIT_AS) != (rlim_t)-1) {
+      Report("WARNING: Program is run with limited virtual address space,"
+             " which wouldn't work with ThreadSanitizer.\n");
+      Report("Re-execing with unlimited virtual address space.\n");
+      setlim(RLIMIT_AS, -1);
+      reexec = true;
+    }
+    if (reexec)
+      ReExec();
+  }
 
 #ifndef TSAN_GO
   CheckPIE();
index 18870a76eb7e8b85b73cb63e16ec3f122e17aa41..ca35266290251874b257ec0785c4795de43cc7b0 100644 (file)
@@ -23,12 +23,24 @@ ReportDesc::ReportDesc()
     , sleep() {
 }
 
+ReportMop::ReportMop()
+    : mset(MBlockReportMutex) {
+}
+
 ReportDesc::~ReportDesc() {
   // FIXME(dvyukov): it must be leaking a lot of memory.
 }
 
 #ifndef TSAN_GO
 
+const int kThreadBufSize = 32;
+const char *thread_name(char *buf, int tid) {
+  if (tid == 0)
+    return "main thread";
+  internal_snprintf(buf, kThreadBufSize, "thread T%d", tid);
+  return buf;
+}
+
 static void PrintHeader(ReportType typ) {
   Printf("WARNING: ThreadSanitizer: ");
 
@@ -65,52 +77,69 @@ void PrintStack(const ReportStack *ent) {
   Printf("\n");
 }
 
+static void PrintMutexSet(Vector<ReportMopMutex> const& mset) {
+  for (uptr i = 0; i < mset.Size(); i++) {
+    if (i == 0)
+      Printf(" (mutexes:");
+    const ReportMopMutex m = mset[i];
+    Printf(" %s M%llu", m.write ? "write" : "read", m.id);
+    Printf(i == mset.Size() - 1 ? ")" : ",");
+  }
+}
+
 static void PrintMop(const ReportMop *mop, bool first) {
-  Printf("  %s of size %d at %p",
+  char thrbuf[kThreadBufSize];
+  Printf("  %s of size %d at %p by %s",
       (first ? (mop->write ? "Write" : "Read")
              : (mop->write ? "Previous write" : "Previous read")),
-      mop->size, (void*)mop->addr);
-  if (mop->tid == 0)
-    Printf(" by main thread:\n");
-  else
-    Printf(" by thread %d:\n", mop->tid);
+      mop->size, (void*)mop->addr,
+      thread_name(thrbuf, mop->tid));
+  PrintMutexSet(mop->mset);
+  Printf(":\n");
   PrintStack(mop->stack);
 }
 
 static void PrintLocation(const ReportLocation *loc) {
+  char thrbuf[kThreadBufSize];
   if (loc->type == ReportLocationGlobal) {
     Printf("  Location is global '%s' of size %zu at %zx %s:%d (%s+%p)\n\n",
                loc->name, loc->size, loc->addr, loc->file, loc->line,
                loc->module, loc->offset);
   } else if (loc->type == ReportLocationHeap) {
-    Printf("  Location is heap block of size %zu at %p allocated",
-        loc->size, loc->addr);
-    if (loc->tid == 0)
-      Printf(" by main thread:\n");
-    else
-      Printf(" by thread %d:\n", loc->tid);
+    char thrbuf[kThreadBufSize];
+    Printf("  Location is heap block of size %zu at %p allocated by %s:\n",
+        loc->size, loc->addr, thread_name(thrbuf, loc->tid));
     PrintStack(loc->stack);
   } else if (loc->type == ReportLocationStack) {
-    Printf("  Location is stack of thread %d:\n\n", loc->tid);
+    Printf("  Location is stack of %s\n\n", thread_name(thrbuf, loc->tid));
+  } else if (loc->type == ReportLocationFD) {
+    Printf("  Location is file descriptor %d created by %s at:\n",
+        loc->fd, thread_name(thrbuf, loc->tid));
+    PrintStack(loc->stack);
   }
 }
 
 static void PrintMutex(const ReportMutex *rm) {
-  if (rm->stack == 0)
-    return;
-  Printf("  Mutex %d created at:\n", rm->id);
-  PrintStack(rm->stack);
+  if (rm->destroyed) {
+    Printf("  Mutex M%llu is already destroyed.\n\n", rm->id);
+  } else {
+    Printf("  Mutex M%llu created at:\n", rm->id);
+    PrintStack(rm->stack);
+  }
 }
 
 static void PrintThread(const ReportThread *rt) {
   if (rt->id == 0)  // Little sense in describing the main thread.
     return;
-  Printf("  Thread %d", rt->id);
+  Printf("  Thread T%d", rt->id);
   if (rt->name)
     Printf(" '%s'", rt->name);
-  Printf(" (tid=%zu, %s)", rt->pid, rt->running ? "running" : "finished");
+  char thrbuf[kThreadBufSize];
+  Printf(" (tid=%zu, %s) created by %s",
+    rt->pid, rt->running ? "running" : "finished",
+    thread_name(thrbuf, rt->parent_tid));
   if (rt->stack)
-    Printf(" created at:");
+    Printf(" at:");
   Printf("\n");
   PrintStack(rt->stack);
 }
index 6776f1e078c0c92a2c16ed499e4daffb7fdcda1a..23fbc68420937c669d24e69972d7a3937b51242a 100644 (file)
@@ -36,20 +36,27 @@ struct ReportStack {
   int col;
 };
 
+struct ReportMopMutex {
+  u64 id;
+  bool write;
+};
+
 struct ReportMop {
   int tid;
   uptr addr;
   int size;
   bool write;
-  int nmutex;
-  int *mutex;
+  Vector<ReportMopMutex> mset;
   ReportStack *stack;
+
+  ReportMop();
 };
 
 enum ReportLocationType {
   ReportLocationGlobal,
   ReportLocationHeap,
-  ReportLocationStack
+  ReportLocationStack,
+  ReportLocationFD
 };
 
 struct ReportLocation {
@@ -59,6 +66,7 @@ struct ReportLocation {
   char *module;
   uptr offset;
   int tid;
+  int fd;
   char *name;
   char *file;
   int line;
@@ -70,11 +78,13 @@ struct ReportThread {
   uptr pid;
   bool running;
   char *name;
+  int parent_tid;
   ReportStack *stack;
 };
 
 struct ReportMutex {
-  int id;
+  u64 id;
+  bool destroyed;
   ReportStack *stack;
 };
 
index 2778ac3e49062c176ad2c2018c2b238dabf674e4..3615a7a9c2f6e1538362799fc47fc91867f0e998 100644 (file)
@@ -164,6 +164,16 @@ void MapShadow(uptr addr, uptr size) {
   MmapFixedNoReserve(MemToShadow(addr), size * kShadowMultiplier);
 }
 
+void MapThreadTrace(uptr addr, uptr size) {
+  DPrintf("#0: Mapping trace at %p-%p(0x%zx)\n", addr, addr + size, size);
+  CHECK_GE(addr, kTraceMemBegin);
+  CHECK_LE(addr + size, kTraceMemBegin + kTraceMemSize);
+  if (addr != (uptr)MmapFixedNoReserve(addr, size)) {
+    Printf("FATAL: ThreadSanitizer can not mmap thread trace\n");
+    Die();
+  }
+}
+
 void Initialize(ThreadState *thr) {
   // Thread safe because done before all threads exist.
   static bool is_initialized = false;
@@ -289,6 +299,7 @@ void TraceSwitch(ThreadState *thr) {
   TraceHeader *hdr = &thr->trace.headers[trace];
   hdr->epoch0 = thr->fast_state.epoch();
   hdr->stack0.ObtainCurrent(thr, 0);
+  hdr->mset0 = thr->mset;
   thr->nomalloc--;
 }
 
@@ -443,7 +454,7 @@ ALWAYS_INLINE
 void MemoryAccess(ThreadState *thr, uptr pc, uptr addr,
     int kAccessSizeLog, bool kAccessIsWrite) {
   u64 *shadow_mem = (u64*)MemToShadow(addr);
-  DPrintf2("#%d: tsan::OnMemoryAccess: @%p %p size=%d"
+  DPrintf2("#%d: MemoryAccess: @%p %p size=%d"
       " is_write=%d shadow_mem=%p {%zx, %zx, %zx, %zx}\n",
       (int)thr->fast_state.tid(), (void*)pc, (void*)addr,
       (int)(1 << kAccessSizeLog), kAccessIsWrite, shadow_mem,
index 56fcad1412f900a65be32de7056144d95d107ca8..b911791c18747bffe2bc60b593c7a33e9c5bc96f 100644 (file)
@@ -34,6 +34,7 @@
 #include "tsan_vector.h"
 #include "tsan_report.h"
 #include "tsan_platform.h"
+#include "tsan_mutexset.h"
 
 #if SANITIZER_WORDSIZE != 64
 # error "ThreadSanitizer is supported only on 64-bit platforms"
@@ -48,6 +49,10 @@ struct MBlock {
   u32 alloc_tid;
   u32 alloc_stack_id;
   SyncVar *head;
+
+  MBlock()
+    : mtx(MutexTypeMBlock, StatMtxMBlock) {
+  }
 };
 
 #ifndef TSAN_GO
@@ -58,10 +63,22 @@ const uptr kAllocatorSpace = 0x7d0000000000ULL;
 #endif
 const uptr kAllocatorSize  =  0x10000000000ULL;  // 1T.
 
+struct TsanMapUnmapCallback {
+  void OnMap(uptr p, uptr size) const { }
+  void OnUnmap(uptr p, uptr size) const {
+    // We are about to unmap a chunk of user memory.
+    // Mark the corresponding shadow memory as not needed.
+    uptr shadow_beg = MemToShadow(p);
+    uptr shadow_end = MemToShadow(p + size);
+    CHECK(IsAligned(shadow_end|shadow_beg, GetPageSizeCached()));
+    FlushUnneededShadowMemory(shadow_beg, shadow_end - shadow_beg);
+  }
+};
+
 typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, sizeof(MBlock),
     DefaultSizeClassMap> PrimaryAllocator;
 typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache;
-typedef LargeMmapAllocator SecondaryAllocator;
+typedef LargeMmapAllocator<TsanMapUnmapCallback> SecondaryAllocator;
 typedef CombinedAllocator<PrimaryAllocator, AllocatorCache,
     SecondaryAllocator> Allocator;
 Allocator *allocator();
@@ -298,6 +315,7 @@ struct ThreadState {
   uptr *shadow_stack;
   uptr *shadow_stack_end;
 #endif
+  MutexSet mset;
   ThreadClock clock;
 #ifndef TSAN_GO
   AllocatorCache alloc_cache;
@@ -369,6 +387,7 @@ struct ThreadContext {
   u64 epoch0;
   u64 epoch1;
   StackTrace creation_stack;
+  int creation_tid;
   ThreadDeadInfo *dead_info;
   ThreadContext *dead_next;  // In dead thread list.
   char *name;  // As annotated by user.
@@ -445,7 +464,8 @@ class ScopedReport {
   ~ScopedReport();
 
   void AddStack(const StackTrace *stack);
-  void AddMemoryAccess(uptr addr, Shadow s, const StackTrace *stack);
+  void AddMemoryAccess(uptr addr, Shadow s, const StackTrace *stack,
+                       const MutexSet *mset);
   void AddThread(const ThreadContext *tctx);
   void AddMutex(const SyncVar *s);
   void AddLocation(uptr addr, uptr size);
@@ -457,11 +477,13 @@ class ScopedReport {
   Context *ctx_;
   ReportDesc *rep_;
 
+  void AddMutex(u64 id);
+
   ScopedReport(const ScopedReport&);
   void operator = (const ScopedReport&);
 };
 
-void RestoreStack(int tid, const u64 epoch, StackTrace *stk);
+void RestoreStack(int tid, const u64 epoch, StackTrace *stk, MutexSet *mset);
 
 void StatAggregate(u64 *dst, u64 *src);
 void StatOutput(u64 *stat);
@@ -471,6 +493,7 @@ void ALWAYS_INLINE INLINE StatInc(ThreadState *thr, StatType typ, u64 n = 1) {
 }
 
 void MapShadow(uptr addr, uptr size);
+void MapThreadTrace(uptr addr, uptr size);
 void InitializeShadowMemory();
 void InitializeInterceptors();
 void InitializeDynamicAnnotations();
@@ -502,6 +525,10 @@ void PrintCurrentStack(ThreadState *thr, uptr pc);
 void Initialize(ThreadState *thr);
 int Finalize(ThreadState *thr);
 
+SyncVar* GetJavaSync(ThreadState *thr, uptr pc, uptr addr,
+                     bool write_lock, bool create);
+SyncVar* GetAndRemoveJavaSync(ThreadState *thr, uptr pc, uptr addr);
+
 void MemoryAccess(ThreadState *thr, uptr pc, uptr addr,
     int kAccessSizeLog, bool kAccessIsWrite);
 void MemoryAccessImpl(ThreadState *thr, uptr addr,
@@ -575,7 +602,10 @@ uptr TraceParts();
 
 extern "C" void __tsan_trace_switch();
 void ALWAYS_INLINE INLINE TraceAddEvent(ThreadState *thr, FastState fs,
-                                        EventType typ, uptr addr) {
+                                        EventType typ, u64 addr) {
+  DCHECK_GE((int)typ, 0);
+  DCHECK_LE((int)typ, 7);
+  DCHECK_EQ(GetLsb(addr, 61), addr);
   StatInc(thr, StatEvents);
   u64 pos = fs.GetTracePos();
   if (UNLIKELY((pos % kTracePartSize) == 0)) {
index e5b43be6a4972796a4ff81f1fcb3174f4e91ad34..8dd0e6d4d9b17b0f6682954bb4286395af8f64bc 100644 (file)
@@ -26,7 +26,7 @@ void MutexCreate(ThreadState *thr, uptr pc, uptr addr,
   StatInc(thr, StatMutexCreate);
   if (!linker_init && IsAppMem(addr))
     MemoryWrite1Byte(thr, pc, addr);
-  SyncVar *s = ctx->synctab.GetAndLock(thr, pc, addr, true);
+  SyncVar *s = ctx->synctab.GetOrCreateAndLock(thr, pc, addr, true);
   s->is_rw = rw;
   s->is_recursive = recursive;
   s->is_linker_init = linker_init;
@@ -59,11 +59,12 @@ void MutexDestroy(ThreadState *thr, uptr pc, uptr addr) {
     trace.ObtainCurrent(thr, pc);
     rep.AddStack(&trace);
     FastState last(s->last_lock);
-    RestoreStack(last.tid(), last.epoch(), &trace);
+    RestoreStack(last.tid(), last.epoch(), &trace, 0);
     rep.AddStack(&trace);
     rep.AddLocation(s->addr, 1);
     OutputReport(ctx, rep);
   }
+  thr->mset.Remove(s->GetId());
   DestroyAndFree(s);
 }
 
@@ -72,9 +73,9 @@ void MutexLock(ThreadState *thr, uptr pc, uptr addr) {
   DPrintf("#%d: MutexLock %zx\n", thr->tid, addr);
   if (IsAppMem(addr))
     MemoryRead1Byte(thr, pc, addr);
+  SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true);
   thr->fast_state.IncrementEpoch();
-  TraceAddEvent(thr, thr->fast_state, EventTypeLock, addr);
-  SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, addr, true);
+  TraceAddEvent(thr, thr->fast_state, EventTypeLock, s->GetId());
   if (s->owner_tid == SyncVar::kInvalidTid) {
     CHECK_EQ(s->recursion, 0);
     s->owner_tid = thr->tid;
@@ -96,6 +97,7 @@ void MutexLock(ThreadState *thr, uptr pc, uptr addr) {
     StatInc(thr, StatMutexRecLock);
   }
   s->recursion++;
+  thr->mset.Add(s->GetId(), true, thr->fast_state.epoch());
   s->mtx.Unlock();
 }
 
@@ -104,9 +106,9 @@ void MutexUnlock(ThreadState *thr, uptr pc, uptr addr) {
   DPrintf("#%d: MutexUnlock %zx\n", thr->tid, addr);
   if (IsAppMem(addr))
     MemoryRead1Byte(thr, pc, addr);
+  SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true);
   thr->fast_state.IncrementEpoch();
-  TraceAddEvent(thr, thr->fast_state, EventTypeUnlock, addr);
-  SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, addr, true);
+  TraceAddEvent(thr, thr->fast_state, EventTypeUnlock, s->GetId());
   if (s->recursion == 0) {
     if (!s->is_broken) {
       s->is_broken = true;
@@ -132,6 +134,7 @@ void MutexUnlock(ThreadState *thr, uptr pc, uptr addr) {
       StatInc(thr, StatMutexRecUnlock);
     }
   }
+  thr->mset.Del(s->GetId(), true);
   s->mtx.Unlock();
 }
 
@@ -141,9 +144,9 @@ void MutexReadLock(ThreadState *thr, uptr pc, uptr addr) {
   StatInc(thr, StatMutexReadLock);
   if (IsAppMem(addr))
     MemoryRead1Byte(thr, pc, addr);
+  SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, false);
   thr->fast_state.IncrementEpoch();
-  TraceAddEvent(thr, thr->fast_state, EventTypeRLock, addr);
-  SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, addr, false);
+  TraceAddEvent(thr, thr->fast_state, EventTypeRLock, s->GetId());
   if (s->owner_tid != SyncVar::kInvalidTid) {
     Printf("ThreadSanitizer WARNING: read lock of a write locked mutex\n");
     PrintCurrentStack(thr, pc);
@@ -152,6 +155,7 @@ void MutexReadLock(ThreadState *thr, uptr pc, uptr addr) {
   thr->clock.acquire(&s->clock);
   s->last_lock = thr->fast_state.raw();
   StatInc(thr, StatSyncAcquire);
+  thr->mset.Add(s->GetId(), false, thr->fast_state.epoch());
   s->mtx.ReadUnlock();
 }
 
@@ -161,9 +165,9 @@ void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr) {
   StatInc(thr, StatMutexReadUnlock);
   if (IsAppMem(addr))
     MemoryRead1Byte(thr, pc, addr);
+  SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true);
   thr->fast_state.IncrementEpoch();
-  TraceAddEvent(thr, thr->fast_state, EventTypeRUnlock, addr);
-  SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, addr, true);
+  TraceAddEvent(thr, thr->fast_state, EventTypeRUnlock, s->GetId());
   if (s->owner_tid != SyncVar::kInvalidTid) {
     Printf("ThreadSanitizer WARNING: read unlock of a write "
                "locked mutex\n");
@@ -174,6 +178,7 @@ void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr) {
   thr->clock.release(&s->read_clock);
   StatInc(thr, StatSyncRelease);
   s->mtx.Unlock();
+  thr->mset.Del(s->GetId(), false);
 }
 
 void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) {
@@ -181,18 +186,22 @@ void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) {
   DPrintf("#%d: MutexReadOrWriteUnlock %zx\n", thr->tid, addr);
   if (IsAppMem(addr))
     MemoryRead1Byte(thr, pc, addr);
-  SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, addr, true);
+  SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true);
+  bool write = true;
   if (s->owner_tid == SyncVar::kInvalidTid) {
     // Seems to be read unlock.
+    write = false;
     StatInc(thr, StatMutexReadUnlock);
     thr->fast_state.IncrementEpoch();
-    TraceAddEvent(thr, thr->fast_state, EventTypeRUnlock, addr);
+    TraceAddEvent(thr, thr->fast_state, EventTypeRUnlock, s->GetId());
     thr->clock.set(thr->tid, thr->fast_state.epoch());
     thr->fast_synch_epoch = thr->fast_state.epoch();
     thr->clock.release(&s->read_clock);
     StatInc(thr, StatSyncRelease);
   } else if (s->owner_tid == thr->tid) {
     // Seems to be write unlock.
+    thr->fast_state.IncrementEpoch();
+    TraceAddEvent(thr, thr->fast_state, EventTypeUnlock, s->GetId());
     CHECK_GT(s->recursion, 0);
     s->recursion--;
     if (s->recursion == 0) {
@@ -202,8 +211,6 @@ void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) {
       // The sequence of events is quite tricky and doubled in several places.
       // First, it's a bug to increment the epoch w/o writing to the trace.
       // Then, the acquire/release logic can be factored out as well.
-      thr->fast_state.IncrementEpoch();
-      TraceAddEvent(thr, thr->fast_state, EventTypeUnlock, addr);
       thr->clock.set(thr->tid, thr->fast_state.epoch());
       thr->fast_synch_epoch = thr->fast_state.epoch();
       thr->clock.ReleaseStore(&s->clock);
@@ -216,13 +223,14 @@ void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) {
     Printf("ThreadSanitizer WARNING: mutex unlock by another thread\n");
     PrintCurrentStack(thr, pc);
   }
+  thr->mset.Del(s->GetId(), write);
   s->mtx.Unlock();
 }
 
 void Acquire(ThreadState *thr, uptr pc, uptr addr) {
   CHECK_GT(thr->in_rtl, 0);
   DPrintf("#%d: Acquire %zx\n", thr->tid, addr);
-  SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, addr, false);
+  SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, false);
   thr->clock.set(thr->tid, thr->fast_state.epoch());
   thr->clock.acquire(&s->clock);
   StatInc(thr, StatSyncAcquire);
@@ -246,7 +254,7 @@ void AcquireGlobal(ThreadState *thr, uptr pc) {
 void Release(ThreadState *thr, uptr pc, uptr addr) {
   CHECK_GT(thr->in_rtl, 0);
   DPrintf("#%d: Release %zx\n", thr->tid, addr);
-  SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, addr, true);
+  SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true);
   thr->clock.set(thr->tid, thr->fast_state.epoch());
   thr->clock.release(&s->clock);
   StatInc(thr, StatSyncRelease);
@@ -256,7 +264,7 @@ void Release(ThreadState *thr, uptr pc, uptr addr) {
 void ReleaseStore(ThreadState *thr, uptr pc, uptr addr) {
   CHECK_GT(thr->in_rtl, 0);
   DPrintf("#%d: ReleaseStore %zx\n", thr->tid, addr);
-  SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, addr, true);
+  SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true);
   thr->clock.set(thr->tid, thr->fast_state.epoch());
   thr->clock.ReleaseStore(&s->clock);
   StatInc(thr, StatSyncRelease);
index 6aae6cf6e8afcbf7c512defddcf1ca6bbad8f8cc..b65b24fce8998b0a1350426817af5263b6b542ff 100644 (file)
@@ -12,6 +12,7 @@
 #include "sanitizer_common/sanitizer_libc.h"
 #include "sanitizer_common/sanitizer_placement_new.h"
 #include "sanitizer_common/sanitizer_stackdepot.h"
+#include "sanitizer_common/sanitizer_common.h"
 #include "tsan_platform.h"
 #include "tsan_rtl.h"
 #include "tsan_suppressions.h"
 #include "tsan_sync.h"
 #include "tsan_mman.h"
 #include "tsan_flags.h"
+#include "tsan_fd.h"
 
 namespace __tsan {
 
+using namespace __sanitizer;  // NOLINT
+
 void TsanCheckFailed(const char *file, int line, const char *cond,
                      u64 v1, u64 v2) {
   ScopedInRtl in_rtl;
@@ -132,7 +136,7 @@ void ScopedReport::AddStack(const StackTrace *stack) {
 }
 
 void ScopedReport::AddMemoryAccess(uptr addr, Shadow s,
-                                   const StackTrace *stack) {
+    const StackTrace *stack, const MutexSet *mset) {
   void *mem = internal_alloc(MBlockReportMop, sizeof(ReportMop));
   ReportMop *mop = new(mem) ReportMop;
   rep_->mops.PushBack(mop);
@@ -140,8 +144,27 @@ void ScopedReport::AddMemoryAccess(uptr addr, Shadow s,
   mop->addr = addr + s.addr0();
   mop->size = s.size();
   mop->write = s.is_write();
-  mop->nmutex = 0;
   mop->stack = SymbolizeStack(*stack);
+  for (uptr i = 0; i < mset->Size(); i++) {
+    MutexSet::Desc d = mset->Get(i);
+    u64 uid = 0;
+    uptr addr = SyncVar::SplitId(d.id, &uid);
+    SyncVar *s = ctx_->synctab.GetIfExistsAndLock(addr, false);
+    // Check that the mutex is still alive.
+    // Another mutex can be created at the same address,
+    // so check uid as well.
+    if (s && s->CheckId(uid)) {
+      ReportMopMutex mtx = {s->uid, d.write};
+      mop->mset.PushBack(mtx);
+      AddMutex(s);
+    } else {
+      ReportMopMutex mtx = {d.id, d.write};
+      mop->mset.PushBack(mtx);
+      AddMutex(d.id);
+    }
+    if (s)
+      s->mtx.ReadUnlock();
+  }
 }
 
 void ScopedReport::AddThread(const ThreadContext *tctx) {
@@ -156,6 +179,7 @@ void ScopedReport::AddThread(const ThreadContext *tctx) {
   rt->pid = tctx->os_id;
   rt->running = (tctx->status == ThreadStatusRunning);
   rt->name = tctx->name ? internal_strdup(tctx->name) : 0;
+  rt->parent_tid = tctx->creation_tid;
   rt->stack = SymbolizeStack(tctx->creation_stack);
 }
 
@@ -173,17 +197,58 @@ static ThreadContext *FindThread(int unique_id) {
 #endif
 
 void ScopedReport::AddMutex(const SyncVar *s) {
+  for (uptr i = 0; i < rep_->mutexes.Size(); i++) {
+    if (rep_->mutexes[i]->id == s->uid)
+      return;
+  }
   void *mem = internal_alloc(MBlockReportMutex, sizeof(ReportMutex));
   ReportMutex *rm = new(mem) ReportMutex();
   rep_->mutexes.PushBack(rm);
-  rm->id = 42;
+  rm->id = s->uid;
+  rm->destroyed = false;
   rm->stack = SymbolizeStack(s->creation_stack);
 }
 
+void ScopedReport::AddMutex(u64 id) {
+  for (uptr i = 0; i < rep_->mutexes.Size(); i++) {
+    if (rep_->mutexes[i]->id == id)
+      return;
+  }
+  void *mem = internal_alloc(MBlockReportMutex, sizeof(ReportMutex));
+  ReportMutex *rm = new(mem) ReportMutex();
+  rep_->mutexes.PushBack(rm);
+  rm->id = id;
+  rm->destroyed = true;
+  rm->stack = 0;
+}
+
 void ScopedReport::AddLocation(uptr addr, uptr size) {
   if (addr == 0)
     return;
 #ifndef TSAN_GO
+  int fd = -1;
+  int creat_tid = -1;
+  u32 creat_stack = 0;
+  if (FdLocation(addr, &fd, &creat_tid, &creat_stack)
+      || FdLocation(AlternativeAddress(addr), &fd, &creat_tid, &creat_stack)) {
+    void *mem = internal_alloc(MBlockReportLoc, sizeof(ReportLocation));
+    ReportLocation *loc = new(mem) ReportLocation();
+    rep_->locs.PushBack(loc);
+    loc->type = ReportLocationFD;
+    loc->fd = fd;
+    loc->tid = creat_tid;
+    uptr ssz = 0;
+    const uptr *stack = StackDepotGet(creat_stack, &ssz);
+    if (stack) {
+      StackTrace trace;
+      trace.Init(stack, ssz);
+      loc->stack = SymbolizeStack(trace);
+    }
+    ThreadContext *tctx = FindThread(creat_tid);
+    if (tctx)
+      AddThread(tctx);
+    return;
+  }
   if (allocator()->PointerIsMine((void*)addr)) {
     MBlock *b = user_mblock(0, (void*)addr);
     ThreadContext *tctx = FindThread(b->alloc_tid);
@@ -246,7 +311,10 @@ const ReportDesc *ScopedReport::GetReport() const {
   return rep_;
 }
 
-void RestoreStack(int tid, const u64 epoch, StackTrace *stk) {
+void RestoreStack(int tid, const u64 epoch, StackTrace *stk, MutexSet *mset) {
+  // This function restores stack trace and mutex set for the thread/epoch.
+  // It does so by getting stack trace and mutex set at the beginning of
+  // trace part, and then replaying the trace till the given epoch.
   ThreadContext *tctx = CTX()->threads[tid];
   if (tctx == 0)
     return;
@@ -267,6 +335,7 @@ void RestoreStack(int tid, const u64 epoch, StackTrace *stk) {
   TraceHeader* hdr = &trace->headers[partidx];
   if (epoch < hdr->epoch0)
     return;
+  const u64 epoch0 = RoundDown(epoch, TraceSize());
   const u64 eend = epoch % TraceSize();
   const u64 ebegin = RoundDown(eend, kTracePartSize);
   DPrintf("#%d: RestoreStack epoch=%zu ebegin=%zu eend=%zu partidx=%d\n",
@@ -276,12 +345,14 @@ void RestoreStack(int tid, const u64 epoch, StackTrace *stk) {
     stack[i] = hdr->stack0.Get(i);
     DPrintf2("  #%02lu: pc=%zx\n", i, stack[i]);
   }
+  if (mset)
+    *mset = hdr->mset0;
   uptr pos = hdr->stack0.Size();
   Event *events = (Event*)GetThreadTrace(tid);
   for (uptr i = ebegin; i <= eend; i++) {
     Event ev = events[i];
     EventType typ = (EventType)(ev >> 61);
-    uptr pc = (uptr)(ev & 0xffffffffffffull);
+    uptr pc = (uptr)(ev & ((1ull << 61) - 1));
     DPrintf2("  %zu typ=%d pc=%zx\n", i, typ, pc);
     if (typ == EventTypeMop) {
       stack[pos] = pc;
@@ -291,6 +362,17 @@ void RestoreStack(int tid, const u64 epoch, StackTrace *stk) {
       if (pos > 0)
         pos--;
     }
+    if (mset) {
+      if (typ == EventTypeLock) {
+        mset->Add(pc, true, epoch0 + i);
+      } else if (typ == EventTypeUnlock) {
+        mset->Del(pc, true);
+      } else if (typ == EventTypeRLock) {
+        mset->Add(pc, false, epoch0 + i);
+      } else if (typ == EventTypeRUnlock) {
+        mset->Del(pc, false);
+      }
+    }
     for (uptr j = 0; j <= pos; j++)
       DPrintf2("      #%zu: %zx\n", j, stack[j]);
   }
@@ -400,8 +482,11 @@ static bool IsJavaNonsense(const ReportDesc *rep) {
     if (frame != 0 && frame->func != 0
         && (internal_strcmp(frame->func, "memset") == 0
         || internal_strcmp(frame->func, "memcpy") == 0
+        || internal_strcmp(frame->func, "memmove") == 0
         || internal_strcmp(frame->func, "strcmp") == 0
         || internal_strcmp(frame->func, "strncpy") == 0
+        || internal_strcmp(frame->func, "strlen") == 0
+        || internal_strcmp(frame->func, "free") == 0
         || internal_strcmp(frame->func, "pthread_mutex_lock") == 0)) {
       frame = frame->next;
       if (frame == 0
@@ -423,6 +508,10 @@ void ReportRace(ThreadState *thr) {
     return;
   ScopedInRtl in_rtl;
 
+  if (thr->in_signal_handler)
+    Printf("ThreadSanitizer: printing report from signal handler."
+           " Can crash or hang.\n");
+
   bool freed = false;
   {
     Shadow s(thr->racy_state[1]);
@@ -454,15 +543,18 @@ void ReportRace(ThreadState *thr) {
   traces[0].ObtainCurrent(thr, toppc);
   if (IsFiredSuppression(ctx, rep, traces[0]))
     return;
+  InternalScopedBuffer<MutexSet> mset2(1);
+  new(mset2.data()) MutexSet();
   Shadow s2(thr->racy_state[1]);
-  RestoreStack(s2.tid(), s2.epoch(), &traces[1]);
+  RestoreStack(s2.tid(), s2.epoch(), &traces[1], mset2.data());
 
   if (HandleRacyStacks(thr, traces, addr_min, addr_max))
     return;
 
   for (uptr i = 0; i < kMop; i++) {
     Shadow s(thr->racy_state[i]);
-    rep.AddMemoryAccess(addr, s, &traces[i]);
+    rep.AddMemoryAccess(addr, s, &traces[i],
+                        i == 0 ? &thr->mset : mset2.data());
   }
 
   if (flags()->suppress_java && IsJavaNonsense(rep.GetReport()))
index 462d12c7dee39a9dd85e648e36a8a5714ddee48c..d5b3444be6d606246c984b02728e8c9b9b50dc70 100644 (file)
@@ -154,6 +154,7 @@ int ThreadCreate(ThreadState *thr, uptr pc, uptr uid, bool detached) {
     thr->clock.release(&tctx->sync);
     StatInc(thr, StatSyncRelease);
     tctx->creation_stack.ObtainCurrent(thr, pc);
+    tctx->creation_tid = thr->tid;
   }
   return tid;
 }
@@ -303,6 +304,7 @@ void ThreadJoin(ThreadState *thr, uptr pc, int tid) {
     Printf("ThreadSanitizer: join of non-existent thread\n");
     return;
   }
+  // FIXME(dvyukov): print message and continue (it's user error).
   CHECK_EQ(tctx->detached, false);
   CHECK_EQ(tctx->status, ThreadStatusFinished);
   thr->clock.acquire(&tctx->sync);
index 7b9133066739909b2aab54b5dcf0431525f001f0..394c911162609f3c720346eda2ebb9b8e9e36187 100644 (file)
@@ -179,6 +179,28 @@ void StatOutput(u64 *stat) {
   name[StatInt_sem_timedwait]            = "  sem_timedwait                   ";
   name[StatInt_sem_post]                 = "  sem_post                        ";
   name[StatInt_sem_getvalue]             = "  sem_getvalue                    ";
+  name[StatInt_open]                     = "  open                            ";
+  name[StatInt_open64]                   = "  open64                          ";
+  name[StatInt_creat]                    = "  creat                           ";
+  name[StatInt_creat64]                  = "  creat64                         ";
+  name[StatInt_dup]                      = "  dup                             ";
+  name[StatInt_dup2]                     = "  dup2                            ";
+  name[StatInt_dup3]                     = "  dup3                            ";
+  name[StatInt_eventfd]                  = "  eventfd                         ";
+  name[StatInt_signalfd]                 = "  signalfd                        ";
+  name[StatInt_inotify_init]             = "  inotify_init                    ";
+  name[StatInt_inotify_init1]            = "  inotify_init1                   ";
+  name[StatInt_socket]                   = "  socket                          ";
+  name[StatInt_socketpair]               = "  socketpair                      ";
+  name[StatInt_connect]                  = "  connect                         ";
+  name[StatInt_accept]                   = "  accept                          ";
+  name[StatInt_accept4]                  = "  accept4                         ";
+  name[StatInt_epoll_create]             = "  epoll_create                    ";
+  name[StatInt_epoll_create1]            = "  epoll_create1                   ";
+  name[StatInt_close]                    = "  close                           ";
+  name[StatInt___close]                  = "  __close                         ";
+  name[StatInt_pipe]                     = "  pipe                            ";
+  name[StatInt_pipe2]                    = "  pipe2                           ";
   name[StatInt_read]                     = "  read                            ";
   name[StatInt_pread]                    = "  pread                           ";
   name[StatInt_pread64]                  = "  pread64                         ";
@@ -195,6 +217,8 @@ void StatOutput(u64 *stat) {
   name[StatInt_recvmsg]                  = "  recvmsg                         ";
   name[StatInt_unlink]                   = "  unlink                          ";
   name[StatInt_fopen]                    = "  fopen                           ";
+  name[StatInt_freopen]                  = "  freopen                         ";
+  name[StatInt_fclose]                   = "  fclose                          ";
   name[StatInt_fread]                    = "  fread                           ";
   name[StatInt_fwrite]                   = "  fwrite                          ";
   name[StatInt_puts]                     = "  puts                            ";
@@ -208,6 +232,7 @@ void StatOutput(u64 *stat) {
   name[StatInt_usleep]                   = "  usleep                          ";
   name[StatInt_nanosleep]                = "  nanosleep                       ";
   name[StatInt_gettimeofday]             = "  gettimeofday                    ";
+  name[StatInt_fork]                     = "  fork                            ";
 
   name[StatAnnotation]                   = "Dynamic annotations               ";
   name[StatAnnotateHappensBefore]        = "  HappensBefore                   ";
@@ -251,6 +276,8 @@ void StatOutput(u64 *stat) {
   name[StatMtxSlab]                      = "  Slab                            ";
   name[StatMtxAtExit]                    = "  Atexit                          ";
   name[StatMtxAnnotations]               = "  Annotations                     ";
+  name[StatMtxMBlock]                    = "  MBlock                          ";
+  name[StatMtxJavaMBlock]                = "  JavaMBlock                      ";
 
   Printf("Statistics:\n");
   for (int i = 0; i < StatCnt; i++)
index 0dc1cd9a90ddb674c05adce03c376ad80fdccf5c..cdd57365baefa1c482a8a3d4105126b4fbe93b70 100644 (file)
@@ -174,6 +174,28 @@ enum StatType {
   StatInt_sem_timedwait,
   StatInt_sem_post,
   StatInt_sem_getvalue,
+  StatInt_open,
+  StatInt_open64,
+  StatInt_creat,
+  StatInt_creat64,
+  StatInt_dup,
+  StatInt_dup2,
+  StatInt_dup3,
+  StatInt_eventfd,
+  StatInt_signalfd,
+  StatInt_inotify_init,
+  StatInt_inotify_init1,
+  StatInt_socket,
+  StatInt_socketpair,
+  StatInt_connect,
+  StatInt_accept,
+  StatInt_accept4,
+  StatInt_epoll_create,
+  StatInt_epoll_create1,
+  StatInt_close,
+  StatInt___close,
+  StatInt_pipe,
+  StatInt_pipe2,
   StatInt_read,
   StatInt_pread,
   StatInt_pread64,
@@ -190,6 +212,8 @@ enum StatType {
   StatInt_recvmsg,
   StatInt_unlink,
   StatInt_fopen,
+  StatInt_freopen,
+  StatInt_fclose,
   StatInt_fread,
   StatInt_fwrite,
   StatInt_puts,
@@ -207,6 +231,7 @@ enum StatType {
   StatInt_usleep,
   StatInt_nanosleep,
   StatInt_gettimeofday,
+  StatInt_fork,
 
   // Dynamic annotations.
   StatAnnotation,
@@ -253,6 +278,8 @@ enum StatType {
   StatMtxSlab,
   StatMtxAnnotations,
   StatMtxAtExit,
+  StatMtxMBlock,
+  StatMtxJavaMBlock,
 
   // This must be the last.
   StatCnt
index 23540c07ca937fb99a79ca7fc6162a7913f67fe1..9bdd1ffdc5e603c6a83f359edef3a317bb2ce385 100644 (file)
@@ -102,11 +102,11 @@ static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) {
   m->base = (uptr)info->dlpi_addr;
   m->inp_fd = -1;
   m->out_fd = -1;
-  DPrintf("Module %s %zx\n", m->name, m->base);
+  DPrintf2("Module %s %zx\n", m->name, m->base);
   for (int i = 0; i < info->dlpi_phnum; i++) {
     const Elf64_Phdr *s = &info->dlpi_phdr[i];
-    DPrintf("  Section p_type=%zx p_offset=%zx p_vaddr=%zx p_paddr=%zx"
-            " p_filesz=%zx p_memsz=%zx p_flags=%zx p_align=%zx\n",
+    DPrintf2("  Section p_type=%zx p_offset=%zx p_vaddr=%zx p_paddr=%zx"
+        " p_filesz=%zx p_memsz=%zx p_flags=%zx p_align=%zx\n",
             (uptr)s->p_type, (uptr)s->p_offset, (uptr)s->p_vaddr,
             (uptr)s->p_paddr, (uptr)s->p_filesz, (uptr)s->p_memsz,
             (uptr)s->p_flags, (uptr)s->p_align);
@@ -119,7 +119,7 @@ static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) {
     sec->end = sec->base + s->p_memsz;
     sec->next = ctx->sections;
     ctx->sections = sec;
-    DPrintf("  Section %zx-%zx\n", sec->base, sec->end);
+    DPrintf2("  Section %zx-%zx\n", sec->base, sec->end);
   }
   return 0;
 }
index 3bd1b35f9b3ab76cba6663f1832b9e9090e1c334..d392408fd888f23aa0279d99a60e707e7480c80c 100644 (file)
 
 namespace __tsan {
 
-SyncVar::SyncVar(uptr addr)
+SyncVar::SyncVar(uptr addr, u64 uid)
   : mtx(MutexTypeSyncVar, StatMtxSyncVar)
   , addr(addr)
+  , uid(uid)
   , owner_tid(kInvalidTid)
   , last_lock()
   , recursion()
@@ -45,9 +46,38 @@ SyncTab::~SyncTab() {
   }
 }
 
+SyncVar* SyncTab::GetOrCreateAndLock(ThreadState *thr, uptr pc,
+                                     uptr addr, bool write_lock) {
+  return GetAndLock(thr, pc, addr, write_lock, true);
+}
+
+SyncVar* SyncTab::GetIfExistsAndLock(uptr addr, bool write_lock) {
+  return GetAndLock(0, 0, addr, write_lock, false);
+}
+
+SyncVar* SyncTab::Create(ThreadState *thr, uptr pc, uptr addr) {
+  StatInc(thr, StatSyncCreated);
+  void *mem = internal_alloc(MBlockSync, sizeof(SyncVar));
+  const u64 uid = atomic_fetch_add(&uid_gen_, 1, memory_order_relaxed);
+  SyncVar *res = new(mem) SyncVar(addr, uid);
+#ifndef TSAN_GO
+  res->creation_stack.ObtainCurrent(thr, pc);
+#endif
+  return res;
+}
+
 SyncVar* SyncTab::GetAndLock(ThreadState *thr, uptr pc,
-                             uptr addr, bool write_lock) {
+                             uptr addr, bool write_lock, bool create) {
 #ifndef TSAN_GO
+  {  // NOLINT
+    SyncVar *res = GetJavaSync(thr, pc, addr, write_lock, create);
+    if (res)
+      return res;
+  }
+
+  // Here we ask only PrimaryAllocator, because
+  // SecondaryAllocator::PointerIsMine() is slow and we have fallback on
+  // the hashmap anyway.
   if (PrimaryAllocator::PointerIsMine((void*)addr)) {
     MBlock *b = user_mblock(thr, (void*)addr);
     Lock l(&b->mtx);
@@ -57,10 +87,9 @@ SyncVar* SyncTab::GetAndLock(ThreadState *thr, uptr pc,
         break;
     }
     if (res == 0) {
-      StatInc(thr, StatSyncCreated);
-      void *mem = internal_alloc(MBlockSync, sizeof(SyncVar));
-      res = new(mem) SyncVar(addr);
-      res->creation_stack.ObtainCurrent(thr, pc);
+      if (!create)
+        return 0;
+      res = Create(thr, pc, addr);
       res->next = b->head;
       b->head = res;
     }
@@ -85,6 +114,8 @@ SyncVar* SyncTab::GetAndLock(ThreadState *thr, uptr pc,
       }
     }
   }
+  if (!create)
+    return 0;
   {
     Lock l(&p->mtx);
     SyncVar *res = p->val;
@@ -93,12 +124,7 @@ SyncVar* SyncTab::GetAndLock(ThreadState *thr, uptr pc,
         break;
     }
     if (res == 0) {
-      StatInc(thr, StatSyncCreated);
-      void *mem = internal_alloc(MBlockSync, sizeof(SyncVar));
-      res = new(mem) SyncVar(addr);
-#ifndef TSAN_GO
-      res->creation_stack.ObtainCurrent(thr, pc);
-#endif
+      res = Create(thr, pc, addr);
       res->next = p->val;
       p->val = res;
     }
@@ -112,6 +138,11 @@ SyncVar* SyncTab::GetAndLock(ThreadState *thr, uptr pc,
 
 SyncVar* SyncTab::GetAndRemove(ThreadState *thr, uptr pc, uptr addr) {
 #ifndef TSAN_GO
+  {  // NOLINT
+    SyncVar *res = GetAndRemoveJavaSync(thr, pc, addr);
+    if (res)
+      return res;
+  }
   if (PrimaryAllocator::PointerIsMine((void*)addr)) {
     MBlock *b = user_mblock(thr, (void*)addr);
     SyncVar *res = 0;
index 2912d2c0ddd467f330c139e4667ab74b98b2b104..4dbb055a17e1de215df92f4512fcadb4faea8af5 100644 (file)
@@ -48,12 +48,13 @@ class StackTrace {
 };
 
 struct SyncVar {
-  explicit SyncVar(uptr addr);
+  explicit SyncVar(uptr addr, u64 uid);
 
   static const int kInvalidTid = -1;
 
   Mutex mtx;
-  const uptr addr;
+  uptr addr;
+  const u64 uid;  // Globally unique id.
   SyncClock clock;
   SyncClock read_clock;  // Used for rw mutexes only.
   StackTrace creation_stack;
@@ -67,6 +68,18 @@ struct SyncVar {
   SyncVar *next;  // In SyncTab hashtable.
 
   uptr GetMemoryConsumption();
+  u64 GetId() const {
+    // 47 lsb is addr, then 14 bits is low part of uid, then 3 zero bits.
+    return GetLsb((u64)addr | (uid << 47), 61);
+  }
+  bool CheckId(u64 uid) const {
+    CHECK_EQ(uid, GetLsb(uid, 14));
+    return GetLsb(this->uid, 14) == uid;
+  }
+  static uptr SplitId(u64 id, u64 *uid) {
+    *uid = id >> 47;
+    return (uptr)GetLsb(id, 47);
+  }
 };
 
 class SyncTab {
@@ -74,13 +87,15 @@ class SyncTab {
   SyncTab();
   ~SyncTab();
 
-  // If the SyncVar does not exist yet, it is created.
-  SyncVar* GetAndLock(ThreadState *thr, uptr pc,
-                      uptr addr, bool write_lock);
+  SyncVar* GetOrCreateAndLock(ThreadState *thr, uptr pc,
+                              uptr addr, bool write_lock);
+  SyncVar* GetIfExistsAndLock(uptr addr, bool write_lock);
 
   // If the SyncVar does not exist, returns 0.
   SyncVar* GetAndRemove(ThreadState *thr, uptr pc, uptr addr);
 
+  SyncVar* Create(ThreadState *thr, uptr pc, uptr addr);
+
   uptr GetMemoryConsumption(uptr *nsync);
 
  private:
@@ -94,9 +109,13 @@ class SyncTab {
   // FIXME: Implement something more sane.
   static const int kPartCount = 1009;
   Part tab_[kPartCount];
+  atomic_uint64_t uid_gen_;
 
   int PartIdx(uptr addr);
 
+  SyncVar* GetAndLock(ThreadState *thr, uptr pc,
+                      uptr addr, bool write_lock, bool create);
+
   SyncTab(const SyncTab&);  // Not implemented.
   void operator = (const SyncTab&);  // Not implemented.
 };
index 154cc15c083f02a5cf848dd4a30f83a69502a558..69864838e267e835bff5bd7df822b19ca82063a0 100644 (file)
@@ -14,6 +14,7 @@
 #include "tsan_defs.h"
 #include "tsan_mutex.h"
 #include "tsan_sync.h"
+#include "tsan_mutexset.h"
 
 namespace __tsan {
 
@@ -41,6 +42,7 @@ typedef u64 Event;
 struct TraceHeader {
   StackTrace stack0;  // Start stack for the trace.
   u64        epoch0;  // Start epoch for the trace.
+  MutexSet   mset0;
 #ifndef TSAN_GO
   uptr       stack0buf[kTraceStackSize];
 #endif