[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