[compiler-rt] b458bb8 - [hwasan] Display causes in order of probability.

Florian Mayer via llvm-commits llvm-commits at lists.llvm.org
Tue Jun 29 05:00:24 PDT 2021


Author: Florian Mayer
Date: 2021-06-29T13:00:05+01:00
New Revision: b458bb8c04cd5ed025884d424f386a00c9c6857e

URL: https://github.com/llvm/llvm-project/commit/b458bb8c04cd5ed025884d424f386a00c9c6857e
DIFF: https://github.com/llvm/llvm-project/commit/b458bb8c04cd5ed025884d424f386a00c9c6857e.diff

LOG: [hwasan] Display causes in order of probability.

A heap or global buffer that is far away from the faulting address is
unlikely to be the cause, especially if there is a potential
use-after-free as well, so we want to show it after the other
causes.

Reviewed By: eugenis

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

Added: 
    compiler-rt/test/hwasan/TestCases/use-after-free-and-overflow.c

Modified: 
    compiler-rt/lib/hwasan/hwasan_report.cpp
    compiler-rt/test/hwasan/TestCases/global.c
    compiler-rt/test/hwasan/TestCases/heap-buffer-overflow.c
    compiler-rt/test/hwasan/TestCases/stack-oob.c
    compiler-rt/test/hwasan/TestCases/stack-uar.c
    compiler-rt/test/hwasan/TestCases/thread-uaf.c
    compiler-rt/test/hwasan/TestCases/use-after-free.c

Removed: 
    


################################################################################
diff  --git a/compiler-rt/lib/hwasan/hwasan_report.cpp b/compiler-rt/lib/hwasan/hwasan_report.cpp
index 715b4e05992a..00a78193e3a3 100644
--- a/compiler-rt/lib/hwasan/hwasan_report.cpp
+++ b/compiler-rt/lib/hwasan/hwasan_report.cpp
@@ -296,6 +296,75 @@ static uptr GetGlobalSizeFromDescriptor(uptr ptr) {
   return 0;
 }
 
+static void ShowCandidate(uptr untagged_addr, tag_t *candidate, tag_t *left,
+                          tag_t *right) {
+  Decorator d;
+  uptr mem = ShadowToMem(reinterpret_cast<uptr>(candidate));
+  HwasanChunkView chunk = FindHeapChunkByAddress(mem);
+  if (chunk.IsAllocated()) {
+    uptr offset;
+    const char *whence;
+    if (untagged_addr < chunk.End() && untagged_addr >= chunk.Beg()) {
+      offset = untagged_addr - chunk.Beg();
+      whence = "inside";
+    } else if (candidate == left) {
+      offset = untagged_addr - chunk.End();
+      whence = "to the right of";
+    } else {
+      offset = chunk.Beg() - untagged_addr;
+      whence = "to the left of";
+    }
+    Printf("%s", d.Error());
+    Printf("\nCause: heap-buffer-overflow\n");
+    Printf("%s", d.Default());
+    Printf("%s", d.Location());
+    Printf("%p is located %zd bytes %s %zd-byte region [%p,%p)\n",
+           untagged_addr, offset, whence, chunk.UsedSize(), chunk.Beg(),
+           chunk.End());
+    Printf("%s", d.Allocation());
+    Printf("allocated here:\n");
+    Printf("%s", d.Default());
+    GetStackTraceFromId(chunk.GetAllocStackId()).Print();
+    return;
+  }
+  // Check whether the address points into a loaded library. If so, this is
+  // most likely a global variable.
+  const char *module_name;
+  uptr module_address;
+  Symbolizer *sym = Symbolizer::GetOrInit();
+  if (sym->GetModuleNameAndOffsetForPC(mem, &module_name, &module_address)) {
+    Printf("%s", d.Error());
+    Printf("\nCause: global-overflow\n");
+    Printf("%s", d.Default());
+    DataInfo info;
+    Printf("%s", d.Location());
+    if (sym->SymbolizeData(mem, &info) && info.start) {
+      Printf(
+          "%p is located %zd bytes to the %s of %zd-byte global variable "
+          "%s [%p,%p) in %s\n",
+          untagged_addr,
+          candidate == left ? untagged_addr - (info.start + info.size)
+                            : info.start - untagged_addr,
+          candidate == left ? "right" : "left", info.size, info.name,
+          info.start, info.start + info.size, module_name);
+    } else {
+      uptr size = GetGlobalSizeFromDescriptor(mem);
+      if (size == 0)
+        // We couldn't find the size of the global from the descriptors.
+        Printf("%p is located to the %s of a global variable in (%s+0x%x)\n",
+               untagged_addr, candidate == left ? "right" : "left", module_name,
+               module_address);
+      else
+        Printf(
+            "%p is located to the %s of a %zd-byte global variable in "
+            "(%s+0x%x)\n",
+            untagged_addr, candidate == left ? "right" : "left", size,
+            module_name, module_address);
+    }
+    Printf("%s", d.Default());
+  }
+}
+
 void PrintAddressDescription(
     uptr tagged_addr, uptr access_size,
     StackAllocationsRingBuffer *current_stack_allocations) {
@@ -324,7 +393,8 @@ void PrintAddressDescription(
   tag_t addr_tag = GetTagFromPointer(tagged_addr);
   tag_t *tag_ptr = reinterpret_cast<tag_t*>(MemToShadow(untagged_addr));
   tag_t *candidate = nullptr, *left = tag_ptr, *right = tag_ptr;
-  for (int i = 0; i < 1000; i++) {
+  uptr candidate_distance = 0;
+  for (; candidate_distance < 1000; candidate_distance++) {
     if (TagsEqual(addr_tag, left)) {
       candidate = left;
       break;
@@ -337,68 +407,32 @@ void PrintAddressDescription(
     ++right;
   }
 
-  if (candidate) {
-    uptr mem = ShadowToMem(reinterpret_cast<uptr>(candidate));
-    HwasanChunkView chunk = FindHeapChunkByAddress(mem);
-    if (chunk.IsAllocated()) {
-      uptr offset;
-      const char *whence;
-      if (untagged_addr < chunk.End() && untagged_addr >= chunk.Beg()) {
-        offset = untagged_addr - chunk.Beg();
-        whence = "inside";
-      } else if (candidate == left) {
-        offset = untagged_addr - chunk.End();
-        whence = "to the right of";
-      } else {
-        offset = chunk.Beg() - untagged_addr;
-        whence = "to the left of";
-      }
+  constexpr auto kCloseCandidateDistance = 1;
+
+  if (candidate && candidate_distance <= kCloseCandidateDistance) {
+    ShowCandidate(untagged_addr, candidate, left, right);
+    num_descriptions_printed++;
+  }
+
+  hwasanThreadList().VisitAllLiveThreads([&](Thread *t) {
+    if (t->AddrIsInStack(untagged_addr)) {
+      // TODO(fmayer): figure out how to distinguish use-after-return and
+      // stack-buffer-overflow.
+      Printf("%s", d.Error());
+      Printf("\nCause: stack tag-mismatch\n");
       Printf("%s", d.Location());
-      Printf("%p is located %zd bytes %s %zd-byte region [%p,%p)\n",
-             untagged_addr, offset, whence, chunk.UsedSize(), chunk.Beg(),
-             chunk.End());
-      Printf("%s", d.Allocation());
-      Printf("allocated here:\n");
+      Printf("Address %p is located in stack of thread T%zd\n", untagged_addr,
+             t->unique_id());
       Printf("%s", d.Default());
-      GetStackTraceFromId(chunk.GetAllocStackId()).Print();
+      t->Announce();
+
+      auto *sa = (t == GetCurrentThread() && current_stack_allocations)
+                     ? current_stack_allocations
+                     : t->stack_allocations();
+      PrintStackAllocations(sa, addr_tag, untagged_addr);
       num_descriptions_printed++;
-    } else {
-      // Check whether the address points into a loaded library. If so, this is
-      // most likely a global variable.
-      const char *module_name;
-      uptr module_address;
-      Symbolizer *sym = Symbolizer::GetOrInit();
-      if (sym->GetModuleNameAndOffsetForPC(mem, &module_name,
-                                           &module_address)) {
-        DataInfo info;
-        if (sym->SymbolizeData(mem, &info) && info.start) {
-          Printf(
-              "%p is located %zd bytes to the %s of %zd-byte global variable "
-              "%s [%p,%p) in %s\n",
-              untagged_addr,
-              candidate == left ? untagged_addr - (info.start + info.size)
-                                : info.start - untagged_addr,
-              candidate == left ? "right" : "left", info.size, info.name,
-              info.start, info.start + info.size, module_name);
-        } else {
-          uptr size = GetGlobalSizeFromDescriptor(mem);
-          if (size == 0)
-            // We couldn't find the size of the global from the descriptors.
-            Printf(
-                "%p is located to the %s of a global variable in (%s+0x%x)\n",
-                untagged_addr, candidate == left ? "right" : "left",
-                module_name, module_address);
-          else
-            Printf(
-                "%p is located to the %s of a %zd-byte global variable in "
-                "(%s+0x%x)\n",
-                untagged_addr, candidate == left ? "right" : "left", size,
-                module_name, module_address);
-        }
-        num_descriptions_printed++;
-      }
     }
-  }
+  });
 
   hwasanThreadList().VisitAllLiveThreads([&](Thread *t) {
     // Scan all threads' ring buffers to find if it's a heap-use-after-free.
@@ -407,6 +441,8 @@ void PrintAddressDescription(
     if (FindHeapAllocation(t->heap_allocations(), tagged_addr, &har,
                            &ring_index, &num_matching_addrs,
                            &num_matching_addrs_4b)) {
+      Printf("%s", d.Error());
+      Printf("\nCause: use-after-free\n");
       Printf("%s", d.Location());
       Printf("%p is located %zd bytes inside of %zd-byte region [%p,%p)\n",
              untagged_addr, untagged_addr - UntagAddr(har.tagged_addr),
@@ -433,29 +469,25 @@ void PrintAddressDescription(
       t->Announce();
       num_descriptions_printed++;
     }
-
-    // Very basic check for stack memory.
-    if (t->AddrIsInStack(untagged_addr)) {
-      Printf("%s", d.Location());
-      Printf("Address %p is located in stack of thread T%zd\n", untagged_addr,
-             t->unique_id());
-      Printf("%s", d.Default());
-      t->Announce();
-
-      auto *sa = (t == GetCurrentThread() && current_stack_allocations)
-                     ? current_stack_allocations
-                     : t->stack_allocations();
-      PrintStackAllocations(sa, addr_tag, untagged_addr);
-      num_descriptions_printed++;
-    }
   });
 
+  if (candidate && num_descriptions_printed == 0) {
+    ShowCandidate(untagged_addr, candidate, left, right);
+    num_descriptions_printed++;
+  }
+
   // Print the remaining threads, as an extra information, 1 line per thread.
   hwasanThreadList().VisitAllLiveThreads([&](Thread *t) { t->Announce(); });
 
   if (!num_descriptions_printed)
     // We exhausted our possibilities. Bail out.
     Printf("HWAddressSanitizer can not describe address in more detail.\n");
+  if (num_descriptions_printed > 1) {
+    Printf(
+        "There are %d potential causes, printed above in order "
+        "of likeliness.",
+        num_descriptions_printed);
+  }
 }
 
 void ReportStats() {}

diff  --git a/compiler-rt/test/hwasan/TestCases/global.c b/compiler-rt/test/hwasan/TestCases/global.c
index e1a7fd72f2b8..82fca892c23a 100644
--- a/compiler-rt/test/hwasan/TestCases/global.c
+++ b/compiler-rt/test/hwasan/TestCases/global.c
@@ -10,6 +10,7 @@
 int x = 1;
 
 int main(int argc, char **argv) {
+  // CHECK: Cause: global-overflow
   // RSYM: is located 0 bytes to the right of 4-byte global variable x {{.*}} in {{.*}}global.c.tmp
   // RNOSYM: is located to the right of a 4-byte global variable in ({{.*}}global.c.tmp+{{.*}})
   // LSYM: is located 4 bytes to the left of 4-byte global variable x {{.*}} in {{.*}}global.c.tmp

diff  --git a/compiler-rt/test/hwasan/TestCases/heap-buffer-overflow.c b/compiler-rt/test/hwasan/TestCases/heap-buffer-overflow.c
index 67398141209a..8d41ac51dd1e 100644
--- a/compiler-rt/test/hwasan/TestCases/heap-buffer-overflow.c
+++ b/compiler-rt/test/hwasan/TestCases/heap-buffer-overflow.c
@@ -31,6 +31,7 @@ int main(int argc, char **argv) {
   if (size == 1000000) {
     fprintf(stderr, "is a large allocated heap chunk; size: 1003520 offset: %d\n",
             offset);
+    fprintf(stderr, "Cause: heap-buffer-overflow\n");
     fprintf(stderr, "is located %s of 1000000-byte region\n",
             offset == -30 ? "30 bytes to the left" : "0 bytes to the right");
     return -1;
@@ -38,26 +39,33 @@ int main(int argc, char **argv) {
 #endif
 
 // CHECK40: allocated heap chunk; size: 32 offset: 8
+// CHECK40: Cause: heap-buffer-overflow
 // CHECK40: is located 10 bytes to the right of 30-byte region
 //
 // CHECK80: allocated heap chunk; size: 32 offset: 16
+// CHECK80: Cause: heap-buffer-overflow
 // CHECK80: is located 50 bytes to the right of 30-byte region
 //
+// CHECKm30: Cause: heap-buffer-overflow
 // CHECKm30: is located 30 bytes to the left of 30-byte region
 //
 // CHECKMm30: is a large allocated heap chunk; size: 1003520 offset: -30
+// CHECKMm30: Cause: heap-buffer-overflow
 // CHECKMm30: is located 30 bytes to the left of 1000000-byte region
 //
 // CHECKM: is a large allocated heap chunk; size: 1003520 offset: 1000000
+// CHECKM: Cause: heap-buffer-overflow
 // CHECKM: is located 0 bytes to the right of 1000000-byte region
 //
 // CHECK31: tags: [[TAG:..]]/0e (ptr/mem)
+// CHECK31: Cause: heap-buffer-overflow
 // CHECK31: is located 1 bytes to the right of 30-byte region
 // CHECK31: Memory tags around the buggy address
 // CHECK31: [0e]
 // CHECK31: Tags for short granules around the buggy address
 // CHECK31: {{\[}}[[TAG]]]
 //
+// CHECK20: Cause: heap-buffer-overflow
 // CHECK20: is located 10 bytes to the right of 20-byte region [0x{{.*}}0,0x{{.*}}4)
   free(x);
 }

diff  --git a/compiler-rt/test/hwasan/TestCases/stack-oob.c b/compiler-rt/test/hwasan/TestCases/stack-oob.c
index 1088300c74bc..7dfd7deb2767 100644
--- a/compiler-rt/test/hwasan/TestCases/stack-oob.c
+++ b/compiler-rt/test/hwasan/TestCases/stack-oob.c
@@ -27,6 +27,7 @@ int main() {
   // CHECK: READ of size 1 at
   // CHECK: #0 {{.*}} in f{{.*}}stack-oob.c:[[@LINE-6]]
 
+  // CHECK: Cause: stack tag-mismatch
   // CHECK: is located in stack of threa
 
   // CHECK: SUMMARY: HWAddressSanitizer: tag-mismatch {{.*}} in f

diff  --git a/compiler-rt/test/hwasan/TestCases/stack-uar.c b/compiler-rt/test/hwasan/TestCases/stack-uar.c
index 113f36c31920..9a81dfc3d6bc 100644
--- a/compiler-rt/test/hwasan/TestCases/stack-uar.c
+++ b/compiler-rt/test/hwasan/TestCases/stack-uar.c
@@ -30,9 +30,10 @@ int main() {
   return *p;
   // CHECK: READ of size 1 at
   // CHECK: #0 {{.*}} in main{{.*}}stack-uar.c:[[@LINE-2]]
+  // CHECK: Cause: stack tag-mismatch
   // CHECK: is located in stack of thread
   // CHECK: Potentially referenced stack objects:
-  // CHECK-NEXT: zzz in buggy {{.*}}stack-uar.c:[[@LINE-19]]
+  // CHECK-NEXT: zzz in buggy {{.*}}stack-uar.c:[[@LINE-20]]
   // CHECK-NEXT: Memory tags around the buggy address
 
   // NOSYM: Previously allocated frames:

diff  --git a/compiler-rt/test/hwasan/TestCases/thread-uaf.c b/compiler-rt/test/hwasan/TestCases/thread-uaf.c
index 7051b2632e60..c368882f4589 100644
--- a/compiler-rt/test/hwasan/TestCases/thread-uaf.c
+++ b/compiler-rt/test/hwasan/TestCases/thread-uaf.c
@@ -30,6 +30,7 @@ void *Use(void *arg) {
   // CHECK: ERROR: HWAddressSanitizer: tag-mismatch on address
   // CHECK: WRITE of size 1 {{.*}} in thread T3
   // CHECK: thread-uaf.c:[[@LINE-3]]
+  // CHECK: Cause: use-after-free
   // CHECK: freed by thread T2 here
   // CHECK: in Deallocate
   // CHECK: previously allocated here:

diff  --git a/compiler-rt/test/hwasan/TestCases/use-after-free-and-overflow.c b/compiler-rt/test/hwasan/TestCases/use-after-free-and-overflow.c
new file mode 100644
index 000000000000..c08b00fc35ac
--- /dev/null
+++ b/compiler-rt/test/hwasan/TestCases/use-after-free-and-overflow.c
@@ -0,0 +1,61 @@
+// Checks that we do not print a faraway buffer overrun if we find a
+// use-after-free.
+// RUN: %clang_hwasan -O0 %s -o %t
+// RUN: not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
+// REQUIRES: stable-runtime
+
+#include <sanitizer/hwasan_interface.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define ALLOC_ATTEMPTS 256
+
+char *Untag(void *x) {
+  return (char *)__hwasan_tag_pointer(x, 0);
+}
+
+void *FindMatch(void *ptrs[ALLOC_ATTEMPTS], void *value) {
+  for (int i = 0; i < ALLOC_ATTEMPTS; ++i) {
+    if (!ptrs[i])
+      return NULL;
+    int distance = Untag(value) - Untag(ptrs[i]);
+    // Leave at least one granule of gap to the allocation.
+    if (abs(distance) < 1000 && abs(distance) > 32)
+      return ptrs[i];
+  }
+  return NULL;
+}
+
+int main(int argc, char **argv) {
+  __hwasan_enable_allocator_tagging();
+  void *ptrs[ALLOC_ATTEMPTS] = {};
+  // Find two allocations that are close enough so that they would be
+  // candidates as buffer overflows for each other.
+  void *one;
+  void *other;
+  for (int i = 0; i < ALLOC_ATTEMPTS; ++i) {
+    one = malloc(16);
+    other = FindMatch(ptrs, one);
+    ptrs[i] = one;
+    if (other)
+      break;
+  }
+  if (!other) {
+    fprintf(stderr, "Could not find closeby allocations.\n");
+    abort();
+  }
+  __hwasan_tag_memory(Untag(one), 3, 16);
+  __hwasan_tag_memory(Untag(other), 3, 16);
+  // Tag potential adjaceant allocations with a mismatching tag, otherwise this
+  // test would flake.
+  __hwasan_tag_memory(Untag(one) + 16, 4, 16);
+  __hwasan_tag_memory(Untag(one) - 16, 4, 16);
+  void *retagged_one = __hwasan_tag_pointer(one, 3);
+  free(retagged_one);
+  volatile char *ptr = (char *)retagged_one;
+  *ptr = 1;
+}
+
+// CHECK-NOT: Cause: heap-buffer-overflow
+// CHECK: Cause: use-after-free
+// CHECK-NOT: Cause: heap-buffer-overflow

diff  --git a/compiler-rt/test/hwasan/TestCases/use-after-free.c b/compiler-rt/test/hwasan/TestCases/use-after-free.c
index ed4512387cc8..608f58894453 100644
--- a/compiler-rt/test/hwasan/TestCases/use-after-free.c
+++ b/compiler-rt/test/hwasan/TestCases/use-after-free.c
@@ -24,15 +24,16 @@ int main() {
   // CHECK: #{{[0-9]}} {{.*}} in main {{.*}}use-after-free.c:[[@LINE-2]]
   // Offset is 5 or 11 depending on left/right alignment.
   // CHECK: is a small unallocated heap chunk; size: 32 offset: {{5|11}}
+  // CHECK: Cause: use-after-free
   // CHECK: is located 5 bytes inside of 10-byte region
   //
   // CHECK: freed by thread {{.*}} here:
   // CHECK: #0 {{.*}} in {{.*}}free{{.*}} {{.*}}hwasan_allocation_functions.cpp
-  // CHECK: #1 {{.*}} in main {{.*}}use-after-free.c:[[@LINE-14]]
+  // CHECK: #1 {{.*}} in main {{.*}}use-after-free.c:[[@LINE-15]]
 
   // CHECK: previously allocated here:
   // CHECK: #0 {{.*}} in {{.*}}malloc{{.*}} {{.*}}hwasan_allocation_functions.cpp
-  // CHECK: #1 {{.*}} in main {{.*}}use-after-free.c:[[@LINE-19]]
+  // CHECK: #1 {{.*}} in main {{.*}}use-after-free.c:[[@LINE-20]]
   // CHECK: Memory tags around the buggy address (one tag corresponds to 16 bytes):
   // CHECK: =>{{.*}}[[MEM_TAG]]
   // CHECK: SUMMARY: HWAddressSanitizer: tag-mismatch


        


More information about the llvm-commits mailing list