[hwasan] implement detection of double-free (invalid-free)
authorKostya Serebryany <kcc@google.com>
Fri, 24 Aug 2018 01:12:26 +0000 (01:12 +0000)
committerKostya Serebryany <kcc@google.com>
Fri, 24 Aug 2018 01:12:26 +0000 (01:12 +0000)
llvm-svn: 340591

compiler-rt/lib/hwasan/hwasan_allocator.cc
compiler-rt/lib/hwasan/hwasan_report.cc
compiler-rt/lib/hwasan/hwasan_report.h
compiler-rt/test/hwasan/TestCases/double-free.c [new file with mode: 0644]

index 23e919e..95dcc07 100644 (file)
@@ -24,6 +24,7 @@
 #include "hwasan_mapping.h"
 #include "hwasan_thread.h"
 #include "hwasan_poisoning.h"
+#include "hwasan_report.h"
 
 namespace __hwasan {
 
@@ -173,11 +174,21 @@ static void *HwasanAllocate(StackTrace *stack, uptr size, uptr alignment,
   return user_ptr;
 }
 
+static bool PointerAndMemoryTagsMatch(void *user_ptr) {
+  CHECK(user_ptr);
+  tag_t ptr_tag = GetTagFromPointer(reinterpret_cast<uptr>(user_ptr));
+  tag_t mem_tag = *reinterpret_cast<tag_t *>(
+      MEM_TO_SHADOW(GetAddressFromPointer(user_ptr)));
+  return ptr_tag == mem_tag;
+}
+
 void HwasanDeallocate(StackTrace *stack, void *user_ptr) {
   CHECK(user_ptr);
   HWASAN_FREE_HOOK(user_ptr);
 
   void *p = GetAddressFromPointer(user_ptr);
+  if (!PointerAndMemoryTagsMatch(user_ptr))
+    ReportInvalidFree(stack, reinterpret_cast<uptr>(user_ptr));
   Metadata *meta = reinterpret_cast<Metadata *>(allocator.GetMetaData(p));
   uptr size = meta->requested_size;
   meta->state = CHUNK_FREE;
@@ -226,7 +237,7 @@ void *HwasanReallocate(StackTrace *stack, void *user_old_p, uptr new_size,
           t ? t->GenerateRandomTag() : kFallbackAllocTag);
     }
     if (new_size > old_size) {
-      tag_t tag = GetTagFromPointer((uptr)user_old_p);
+      tag_t tag = GetTagFromPointer(reinterpret_cast<uptr>(user_old_p));
       TagMemoryAligned((uptr)old_p + old_size, new_size - old_size, tag);
     }
     return user_old_p;
index c390c0c..ea52d1a 100644 (file)
@@ -109,6 +109,54 @@ void ReportInvalidAccessInsideAddressRange(const char *what, const void *start,
   //   DescribeMemoryRange(start, size);
 }
 
+static void PrintTagsAroundAddr(tag_t *tag_ptr) {
+  Printf(
+      "Memory tags around the buggy address (one tag corresponds to %zd "
+      "bytes):\n", kShadowAlignment);
+
+  const uptr row_len = 16;  // better be power of two.
+  const uptr num_rows = 11;
+  tag_t *center_row_beg = reinterpret_cast<tag_t *>(
+      RoundDownTo(reinterpret_cast<uptr>(tag_ptr), row_len));
+  tag_t *beg_row = center_row_beg - row_len * (num_rows / 2);
+  tag_t *end_row = center_row_beg + row_len * (num_rows / 2);
+  for (tag_t *row = beg_row; row < end_row; row += row_len) {
+    Printf("%s", row == center_row_beg ? "=>" : "  ");
+    for (uptr i = 0; i < row_len; i++) {
+      Printf("%s", row + i == tag_ptr ? "[" : " ");
+      Printf("%02x", row[i]);
+      Printf("%s", row + i == tag_ptr ? "]" : " ");
+    }
+    Printf("%s\n", row == center_row_beg ? "<=" : "  ");
+  }
+}
+
+void ReportInvalidFree(StackTrace *stack, uptr addr) {
+  ScopedErrorReportLock l;
+  uptr address = GetAddressFromPointer(addr);
+  tag_t ptr_tag = GetTagFromPointer(addr);
+  tag_t *tag_ptr = reinterpret_cast<tag_t*>(MEM_TO_SHADOW(address));
+  tag_t mem_tag = *tag_ptr;
+  Decorator d;
+  Printf("%s", d.Error());
+  uptr pc = stack->size ? stack->trace[0] : 0;
+  const char *bug_type = "invalid-free";
+  Report("ERROR: %s: %s on address %p at pc %p\n", SanitizerToolName, bug_type,
+         address, pc);
+  Printf("%s", d.Access());
+  Printf("tags: %02x/%02x (ptr/mem)\n", ptr_tag, mem_tag);
+  Printf("%s", d.Default());
+
+  stack->Print();
+
+  PrintAddressDescription(address, 0);
+
+  PrintTagsAroundAddr(tag_ptr);
+
+  ReportErrorSummary(bug_type, stack);
+  Die();
+}
+
 void ReportTagMismatch(StackTrace *stack, uptr addr, uptr access_size,
                        bool is_store) {
   ScopedErrorReportLock l;
@@ -119,7 +167,8 @@ void ReportTagMismatch(StackTrace *stack, uptr addr, uptr access_size,
   // TODO: when possible, try to print heap-use-after-free, etc.
   const char *bug_type = "tag-mismatch";
   uptr pc = stack->size ? stack->trace[0] : 0;
-  Report("ERROR: %s: %s on address %p at pc %p\n", SanitizerToolName, bug_type, address, pc);
+  Report("ERROR: %s: %s on address %p at pc %p\n", SanitizerToolName, bug_type,
+         address, pc);
 
   tag_t ptr_tag = GetTagFromPointer(addr);
   tag_t *tag_ptr = reinterpret_cast<tag_t*>(MEM_TO_SHADOW(address));
@@ -133,25 +182,7 @@ void ReportTagMismatch(StackTrace *stack, uptr addr, uptr access_size,
 
   PrintAddressDescription(address, access_size);
 
-  Printf(
-      "Memory tags around the buggy address (one tag corresponds to %zd "
-      "bytes):\n", kShadowAlignment);
-
-  const uptr row_len = 16;  // better be power of two.
-  const uptr num_rows = 11;
-  tag_t *center_row_beg = reinterpret_cast<tag_t *>(
-      RoundDownTo(reinterpret_cast<uptr>(tag_ptr), row_len));
-  tag_t *beg_row = center_row_beg - row_len * (num_rows / 2);
-  tag_t *end_row = center_row_beg + row_len * (num_rows / 2);
-  for (tag_t *row = beg_row; row < end_row; row += row_len) {
-    Printf("%s", row == center_row_beg ? "=>" : "  ");
-    for (uptr i = 0; i < row_len; i++) {
-      Printf("%s", row + i == tag_ptr ? "[" : " ");
-      Printf("%02x", row[i]);
-      Printf("%s", row + i == tag_ptr ? "]" : " ");
-    }
-    Printf("%s\n", row == center_row_beg ? "<=" : "  ");
-  }
+  PrintTagsAroundAddr(tag_ptr);
 
   ReportErrorSummary(bug_type, stack);
 }
index bb33f1a..7a9eec8 100644 (file)
@@ -27,6 +27,7 @@ void ReportInvalidAccessInsideAddressRange(const char *what, const void *start,
                                            uptr size, uptr offset);
 void ReportTagMismatch(StackTrace *stack, uptr addr, uptr access_size,
                        bool is_store);
+void ReportInvalidFree(StackTrace *stack, uptr addr);
 
 void ReportAtExitStatistics();
 
diff --git a/compiler-rt/test/hwasan/TestCases/double-free.c b/compiler-rt/test/hwasan/TestCases/double-free.c
new file mode 100644 (file)
index 0000000..bb7b4ff
--- /dev/null
@@ -0,0 +1,23 @@
+// RUN: %clang_hwasan %s -o %t && not %run %t 2>&1 | FileCheck %s
+
+// REQUIRES: stable-runtime
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sanitizer/hwasan_interface.h>
+
+int main() {
+  __hwasan_enable_allocator_tagging();
+  char * volatile x = (char*)malloc(40);
+  free(x);
+  free(x);
+// CHECK: ERROR: HWAddressSanitizer: invalid-free on address
+// CHECK: tags: [[PTR_TAG:..]]/[[MEM_TAG:..]] (ptr/mem)
+// CHECK: freed here:
+// CHECK: previously allocated here:
+// CHECK: Memory tags around the buggy address (one tag corresponds to 16 bytes):
+// CHECK: =>{{.*}}[[MEM_TAG]]
+  fprintf(stderr, "DONE\n");
+  __hwasan_disable_allocator_tagging();
+// CHECK-NOT: DONE
+}