-
Notifications
You must be signed in to change notification settings - Fork 12.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[hwasan] Store some report data early #66682
Conversation
@llvm/pr-subscribers-compiler-rt-sanitizer Changes
Full diff: https://github.com/llvm/llvm-project/pull/66682.diff 1 Files Affected:
diff --git a/compiler-rt/lib/hwasan/hwasan_report.cpp b/compiler-rt/lib/hwasan/hwasan_report.cpp
index d318e26c6a86849..924371a2e90cc05 100644
--- a/compiler-rt/lib/hwasan/hwasan_report.cpp
+++ b/compiler-rt/lib/hwasan/hwasan_report.cpp
@@ -37,7 +37,7 @@ namespace __hwasan {
class ScopedReport {
public:
- ScopedReport(bool fatal = false) : fatal(fatal) {
+ ScopedReport(bool fatal) : fatal(fatal) {
Lock lock(&error_message_lock_);
error_message_ptr_ = fatal ? &error_message_ : nullptr;
++hwasan_report_count;
@@ -108,29 +108,45 @@ static void MaybePrintAndroidHelpUrl() {
#endif
}
+namespace {
// A RAII object that holds a copy of the current thread stack ring buffer.
// The actual stack buffer may change while we are iterating over it (for
// example, Printf may call syslog() which can itself be built with hwasan).
class SavedStackAllocations {
public:
- SavedStackAllocations(StackAllocationsRingBuffer *rb) {
+ SavedStackAllocations() = default;
+
+ explicit SavedStackAllocations(Thread *t) { CopyFrom(t); }
+
+ void CopyFrom(Thread *t) {
+ StackAllocationsRingBuffer *rb = t->stack_allocations();
uptr size = rb->size() * sizeof(uptr);
void *storage =
MmapAlignedOrDieOnFatalError(size, size * 2, "saved stack allocations");
new (&rb_) StackAllocationsRingBuffer(*rb, storage);
+ thread_id_ = t->unique_id();
}
~SavedStackAllocations() {
- StackAllocationsRingBuffer *rb = get();
- UnmapOrDie(rb->StartOfStorage(), rb->size() * sizeof(uptr));
+ if (rb_) {
+ StackAllocationsRingBuffer *rb = get();
+ UnmapOrDie(rb->StartOfStorage(), rb->size() * sizeof(uptr));
+ }
+ }
+
+ const StackAllocationsRingBuffer *get() const {
+ return (const StackAllocationsRingBuffer *)&rb_;
}
StackAllocationsRingBuffer *get() {
return (StackAllocationsRingBuffer *)&rb_;
}
+ u32 thread_id() const { return thread_id_; }
+
private:
- uptr rb_;
+ uptr rb_ = 0;
+ u32 thread_id_;
};
class Decorator: public __sanitizer::SanitizerCommonDecorator {
@@ -143,6 +159,7 @@ class Decorator: public __sanitizer::SanitizerCommonDecorator {
const char *Location() { return Green(); }
const char *Thread() { return Green(); }
};
+} // namespace
static bool FindHeapAllocation(HeapAllocationsRingBuffer *rb, uptr tagged_addr,
HeapAllocationRecord *har, uptr *ring_index,
@@ -183,7 +200,7 @@ static bool FindHeapAllocation(HeapAllocationsRingBuffer *rb, uptr tagged_addr,
return false;
}
-static void PrintStackAllocations(StackAllocationsRingBuffer *sa,
+static void PrintStackAllocations(const StackAllocationsRingBuffer *sa,
tag_t addr_tag, uptr untagged_addr) {
uptr frames = Min((uptr)flags()->stack_history_size, sa->size());
bool found_local = false;
@@ -373,12 +390,117 @@ static void ShowHeapOrGlobalCandidate(uptr untagged_addr, tag_t *candidate,
}
}
-void PrintAddressDescription(
- uptr tagged_addr, uptr access_size,
- StackAllocationsRingBuffer *current_stack_allocations) {
+void ReportStats() {}
+
+static void PrintTagInfoAroundAddr(tag_t *tag_ptr, uptr num_rows,
+ void (*print_tag)(InternalScopedString &s,
+ tag_t *tag)) {
+ const uptr row_len = 16; // better be power of two.
+ 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 + 1) / 2);
+ InternalScopedString s;
+ for (tag_t *row = beg_row; row < end_row; row += row_len) {
+ s.Append(row == center_row_beg ? "=>" : " ");
+ s.AppendF("%p:", (void *)ShadowToMem(reinterpret_cast<uptr>(row)));
+ for (uptr i = 0; i < row_len; i++) {
+ s.Append(row + i == tag_ptr ? "[" : " ");
+ print_tag(s, &row[i]);
+ s.Append(row + i == tag_ptr ? "]" : " ");
+ }
+ s.AppendF("\n");
+ }
+ Printf("%s", s.data());
+}
+
+static void PrintTagsAroundAddr(tag_t *tag_ptr) {
+ Printf(
+ "Memory tags around the buggy address (one tag corresponds to %zd "
+ "bytes):\n",
+ kShadowAlignment);
+ PrintTagInfoAroundAddr(tag_ptr, 17, [](InternalScopedString &s, tag_t *tag) {
+ s.AppendF("%02x", *tag);
+ });
+
+ Printf(
+ "Tags for short granules around the buggy address (one tag corresponds "
+ "to %zd bytes):\n",
+ kShadowAlignment);
+ PrintTagInfoAroundAddr(tag_ptr, 3, [](InternalScopedString &s, tag_t *tag) {
+ if (*tag >= 1 && *tag <= kShadowAlignment) {
+ uptr granule_addr = ShadowToMem(reinterpret_cast<uptr>(tag));
+ s.AppendF("%02x",
+ *reinterpret_cast<u8 *>(granule_addr + kShadowAlignment - 1));
+ } else {
+ s.AppendF("..");
+ }
+ });
+ Printf(
+ "See "
+ "https://clang.llvm.org/docs/"
+ "HardwareAssistedAddressSanitizerDesign.html#short-granules for a "
+ "description of short granule tags\n");
+}
+
+static uptr GetTopPc(StackTrace *stack) {
+ return stack->size ? StackTrace::GetPreviousInstructionPc(stack->trace[0])
+ : 0;
+}
+
+namespace {
+class BaseReport {
+ public:
+ BaseReport(StackTrace *stack, bool fatal, uptr tagged_addr, uptr access_size)
+ : scoped_report(fatal),
+ stack(stack),
+ tagged_addr(tagged_addr),
+ access_size(access_size),
+ untagged_addr(UntagAddr(tagged_addr)),
+ ptr_tag(GetTagFromPointer(tagged_addr)) {
+ if (MemIsShadow(untagged_addr))
+ return;
+
+ HwasanChunkView chunk = FindHeapChunkByAddress(untagged_addr);
+ heap.begin = chunk.Beg();
+ if (heap.begin) {
+ heap.size = chunk.ActualSize();
+ heap.from_small_heap = chunk.FromSmallHeap();
+ heap.is_allocated = chunk.IsAllocated();
+ }
+
+ hwasanThreadList().VisitAllLiveThreads([&](Thread *t) {
+ if (stack_count < ARRAY_SIZE(stack_allocations) &&
+ t->AddrIsInStack(untagged_addr)) {
+ stack_allocations[stack_count++].CopyFrom(t);
+ }
+ });
+ }
+
+ protected:
+ void PrintAddressDescription() const;
+
+ ScopedReport scoped_report;
+ StackTrace *stack = nullptr;
+ uptr tagged_addr = 0;
+ uptr access_size = 0;
+ uptr untagged_addr = 0;
+ tag_t ptr_tag = 0;
+
+ uptr stack_count = 0;
+ SavedStackAllocations stack_allocations[16];
+
+ struct {
+ uptr begin = 0;
+ uptr size = 0;
+ bool from_small_heap = false;
+ bool is_allocated = false;
+ } heap;
+};
+
+void BaseReport::PrintAddressDescription() const {
Decorator d;
int num_descriptions_printed = 0;
- uptr untagged_addr = UntagAddr(tagged_addr);
if (MemIsShadow(untagged_addr)) {
Printf("%s%p is HWAsan shadow memory.\n%s", d.Location(), untagged_addr,
@@ -387,61 +509,54 @@ void PrintAddressDescription(
}
// Print some very basic information about the address, if it's a heap.
- HwasanChunkView chunk = FindHeapChunkByAddress(untagged_addr);
- if (uptr beg = chunk.Beg()) {
- uptr size = chunk.ActualSize();
- Printf("%s[%p,%p) is a %s %s heap chunk; "
- "size: %zd offset: %zd\n%s",
- d.Location(),
- beg, beg + size,
- chunk.FromSmallHeap() ? "small" : "large",
- chunk.IsAllocated() ? "allocated" : "unallocated",
- size, untagged_addr - beg,
- d.Default());
+ if (heap.begin) {
+ Printf(
+ "%s[%p,%p) is a %s %s heap chunk; "
+ "size: %zd offset: %zd\n%s",
+ d.Location(), heap.begin, heap.begin + heap.size,
+ heap.from_small_heap ? "small" : "large",
+ heap.is_allocated ? "allocated" : "unallocated", heap.size,
+ untagged_addr - heap.begin, d.Default());
}
- tag_t addr_tag = GetTagFromPointer(tagged_addr);
-
- bool on_stack = false;
// Check stack first. If the address is on the stack of a live thread, we
// know it cannot be a heap / global overflow.
- hwasanThreadList().VisitAllLiveThreads([&](Thread *t) {
- if (t->AddrIsInStack(untagged_addr)) {
- on_stack = true;
- // 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("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 (stack_count) {
+ hwasanThreadList().VisitAllLiveThreads([&](Thread *t) {
+ for (auto &allocations : stack_allocations) {
+ if (allocations.thread_id() == t->unique_id()) {
+ // 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("Address %p is located in stack of thread T%zd\n",
+ untagged_addr, t->unique_id());
+ Printf("%s", d.Default());
+ t->Announce();
+
+ PrintStackAllocations(allocations.get(), ptr_tag, untagged_addr);
+ num_descriptions_printed++;
+ }
+ }
+ });
+ }
// Check if this looks like a heap buffer overflow by scanning
// the shadow left and right and looking for the first adjacent
- // object with a different memory tag. If that tag matches addr_tag,
+ // object with a different memory tag. If that tag matches ptr_tag,
// check the allocator if it has a live chunk there.
tag_t *tag_ptr = reinterpret_cast<tag_t*>(MemToShadow(untagged_addr));
tag_t *candidate = nullptr, *left = tag_ptr, *right = tag_ptr;
uptr candidate_distance = 0;
for (; candidate_distance < 1000; candidate_distance++) {
- if (MemIsShadow(reinterpret_cast<uptr>(left)) &&
- TagsEqual(addr_tag, left)) {
+ if (MemIsShadow(reinterpret_cast<uptr>(left)) && TagsEqual(ptr_tag, left)) {
candidate = left;
break;
}
--left;
if (MemIsShadow(reinterpret_cast<uptr>(right)) &&
- TagsEqual(addr_tag, right)) {
+ TagsEqual(ptr_tag, right)) {
candidate = right;
break;
}
@@ -450,7 +565,8 @@ void PrintAddressDescription(
constexpr auto kCloseCandidateDistance = 1;
- if (!on_stack && candidate && candidate_distance <= kCloseCandidateDistance) {
+ if (!stack_count && candidate &&
+ candidate_distance <= kCloseCandidateDistance) {
ShowHeapOrGlobalCandidate(untagged_addr, candidate, left, right);
num_descriptions_printed++;
}
@@ -512,68 +628,16 @@ void PrintAddressDescription(
}
}
-void ReportStats() {}
-
-static void PrintTagInfoAroundAddr(tag_t *tag_ptr, uptr num_rows,
- void (*print_tag)(InternalScopedString &s,
- tag_t *tag)) {
- const uptr row_len = 16; // better be power of two.
- 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 + 1) / 2);
- InternalScopedString s;
- for (tag_t *row = beg_row; row < end_row; row += row_len) {
- s.Append(row == center_row_beg ? "=>" : " ");
- s.AppendF("%p:", (void *)ShadowToMem(reinterpret_cast<uptr>(row)));
- for (uptr i = 0; i < row_len; i++) {
- s.Append(row + i == tag_ptr ? "[" : " ");
- print_tag(s, &row[i]);
- s.Append(row + i == tag_ptr ? "]" : " ");
- }
- s.AppendF("\n");
- }
- Printf("%s", s.data());
-}
-
-static void PrintTagsAroundAddr(tag_t *tag_ptr) {
- Printf(
- "Memory tags around the buggy address (one tag corresponds to %zd "
- "bytes):\n", kShadowAlignment);
- PrintTagInfoAroundAddr(tag_ptr, 17, [](InternalScopedString &s, tag_t *tag) {
- s.AppendF("%02x", *tag);
- });
-
- Printf(
- "Tags for short granules around the buggy address (one tag corresponds "
- "to %zd bytes):\n",
- kShadowAlignment);
- PrintTagInfoAroundAddr(tag_ptr, 3, [](InternalScopedString &s, tag_t *tag) {
- if (*tag >= 1 && *tag <= kShadowAlignment) {
- uptr granule_addr = ShadowToMem(reinterpret_cast<uptr>(tag));
- s.AppendF("%02x",
- *reinterpret_cast<u8 *>(granule_addr + kShadowAlignment - 1));
- } else {
- s.AppendF("..");
- }
- });
- Printf(
- "See "
- "https://clang.llvm.org/docs/"
- "HardwareAssistedAddressSanitizerDesign.html#short-granules for a "
- "description of short granule tags\n");
-}
-
-uptr GetTopPc(StackTrace *stack) {
- return stack->size ? StackTrace::GetPreviousInstructionPc(stack->trace[0])
- : 0;
-}
+class InvalidFreeReport : public BaseReport {
+ public:
+ InvalidFreeReport(StackTrace *stack, uptr tagged_addr)
+ : BaseReport(stack, flags()->halt_on_error, tagged_addr, 0) {}
+ ~InvalidFreeReport();
-void ReportInvalidFree(StackTrace *stack, uptr tagged_addr) {
- ScopedReport R(flags()->halt_on_error);
+ private:
+};
- uptr untagged_addr = UntagAddr(tagged_addr);
- tag_t ptr_tag = GetTagFromPointer(tagged_addr);
+InvalidFreeReport::~InvalidFreeReport() {
tag_t *tag_ptr = nullptr;
tag_t mem_tag = 0;
if (MemIsApp(untagged_addr)) {
@@ -602,7 +666,7 @@ void ReportInvalidFree(StackTrace *stack, uptr tagged_addr) {
stack->Print();
- PrintAddressDescription(tagged_addr, 0, nullptr);
+ PrintAddressDescription();
if (tag_ptr)
PrintTagsAroundAddr(tag_ptr);
@@ -611,21 +675,31 @@ void ReportInvalidFree(StackTrace *stack, uptr tagged_addr) {
ReportErrorSummary(bug_type, stack);
}
-void ReportTailOverwritten(StackTrace *stack, uptr tagged_addr, uptr orig_size,
- const u8 *expected) {
+class TailOverwrittenReport : public BaseReport {
+ public:
+ explicit TailOverwrittenReport(StackTrace *stack, uptr tagged_addr,
+ uptr orig_size, const u8 *expected)
+ : BaseReport(stack, flags()->halt_on_error, tagged_addr, 0),
+ orig_size(orig_size),
+ expected(expected) {}
+ ~TailOverwrittenReport();
+
+ private:
+ uptr orig_size;
+ const u8 *expected;
+};
+
+TailOverwrittenReport::~TailOverwrittenReport() {
uptr tail_size = kShadowAlignment - (orig_size % kShadowAlignment);
u8 actual_expected[kShadowAlignment];
internal_memcpy(actual_expected, expected, tail_size);
- tag_t ptr_tag = GetTagFromPointer(tagged_addr);
// Short granule is stashed in the last byte of the magic string. To avoid
// confusion, make the expected magic string contain the short granule tag.
if (orig_size % kShadowAlignment != 0) {
actual_expected[tail_size - 1] = ptr_tag;
}
- ScopedReport R(flags()->halt_on_error);
Decorator d;
- uptr untagged_addr = UntagAddr(tagged_addr);
Printf("%s", d.Error());
const char *bug_type = "allocation-tail-overwritten";
Report("ERROR: %s: %s; heap object [%p,%p) of size %zd\n", SanitizerToolName,
@@ -683,14 +757,25 @@ void ReportTailOverwritten(StackTrace *stack, uptr tagged_addr, uptr orig_size,
ReportErrorSummary(bug_type, stack);
}
-void ReportTagMismatch(StackTrace *stack, uptr tagged_addr, uptr access_size,
- bool is_store, bool fatal, uptr *registers_frame) {
- ScopedReport R(fatal);
- SavedStackAllocations current_stack_allocations(
- GetCurrentThread()->stack_allocations());
+class TagMismatchReport : public BaseReport {
+ public:
+ explicit TagMismatchReport(StackTrace *stack, uptr tagged_addr,
+ uptr access_size, bool is_store, bool fatal,
+ uptr *registers_frame)
+ : BaseReport(stack, fatal, tagged_addr, access_size),
+ access_size(access_size),
+ is_store(is_store),
+ registers_frame(registers_frame) {}
+ ~TagMismatchReport();
+ private:
+ uptr access_size;
+ bool is_store;
+ uptr *registers_frame;
+};
+
+TagMismatchReport::~TagMismatchReport() {
Decorator d;
- uptr untagged_addr = UntagAddr(tagged_addr);
// TODO: when possible, try to print heap-use-after-free, etc.
const char *bug_type = "tag-mismatch";
uptr pc = GetTopPc(stack);
@@ -704,7 +789,6 @@ void ReportTagMismatch(StackTrace *stack, uptr tagged_addr, uptr access_size,
__hwasan_test_shadow(reinterpret_cast<void *>(tagged_addr), access_size);
CHECK_GE(offset, 0);
CHECK_LT(offset, static_cast<sptr>(access_size));
- tag_t ptr_tag = GetTagFromPointer(tagged_addr);
tag_t *tag_ptr =
reinterpret_cast<tag_t *>(MemToShadow(untagged_addr + offset));
tag_t mem_tag = *tag_ptr;
@@ -741,8 +825,7 @@ void ReportTagMismatch(StackTrace *stack, uptr tagged_addr, uptr access_size,
stack->Print();
- PrintAddressDescription(tagged_addr, access_size,
- current_stack_allocations.get());
+ PrintAddressDescription();
t->Announce();
PrintTagsAroundAddr(tag_ptr);
@@ -753,6 +836,22 @@ void ReportTagMismatch(StackTrace *stack, uptr tagged_addr, uptr access_size,
MaybePrintAndroidHelpUrl();
ReportErrorSummary(bug_type, stack);
}
+} // namespace
+
+void ReportInvalidFree(StackTrace *stack, uptr tagged_addr) {
+ InvalidFreeReport R(stack, tagged_addr);
+}
+
+void ReportTailOverwritten(StackTrace *stack, uptr tagged_addr, uptr orig_size,
+ const u8 *expected) {
+ TailOverwrittenReport R(stack, tagged_addr, orig_size, expected);
+}
+
+void ReportTagMismatch(StackTrace *stack, uptr tagged_addr, uptr access_size,
+ bool is_store, bool fatal, uptr *registers_frame) {
+ TagMismatchReport R(stack, tagged_addr, access_size, is_store, fatal,
+ registers_frame);
+}
// See the frame breakdown defined in __hwasan_tag_mismatch (from
// hwasan_tag_mismatch_{aarch64,riscv64}.S).
|
LGTM |
This prepare the code for rework to collect all nececcecary data before symbolization. Symbolization as any untrivial computations may affect hwasan metadata.
bf116b4
to
624793f
Compare
@fmayer Please continue with these: 624793f88d063ae9fbdc72028bef6c01ee427198 [NFC][hwasan] Collect heap allocations early (#66682) |
This prepare the code for rework to collect all nececcecary data before symbolization. Symbolization as any untrivial computations may affect hwasan metadata.
f3c0aab
to
61289c0
Compare
@fmayer PTAL 61289c01fa2972bb842c08706316e9fa1c898041 [NFC][hwasan] Stored shadow bytes early (#66682) |
} | ||
} | ||
|
||
tag_t BaseReport::GetTagCopy(uptr addr) const { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we expect for those to be called out of range? Should the checks be assert instead? 0
tag is not the same as "we don't know" really.
InvalidFreeReport prints invalid ptr/mem. This reverts commit 7641c22.
61289c0
to
b405e72
Compare
b405e72
to
04087c2
Compare
Please review them one by one in order, and let me know which are OK.
It's mostly code shuffling.
The goal is to make HWASA collect all needed info the first, and printout later.
This way we avoid the printing changes HWASAN metadata.