Skip to content

[hwasan] Classify stack overflow, and use after scope #76133

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

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 37 additions & 11 deletions compiler-rt/lib/hwasan/hwasan_report.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -221,29 +221,57 @@ static void PrintStackAllocations(const StackAllocationsRingBuffer *sa,
for (LocalInfo &local : frame.locals) {
if (!local.has_frame_offset || !local.has_size || !local.has_tag_offset)
continue;
if (!(local.name && internal_strlen(local.name)) &&
!(local.function_name && internal_strlen(local.name)) &&
!(local.decl_file && internal_strlen(local.decl_file)))
continue;
tag_t obj_tag = base_tag ^ local.tag_offset;
if (obj_tag != addr_tag)
continue;
// Calculate the offset from the object address to the faulting
// address. Because we only store bits 4-19 of FP (bits 0-3 are
// guaranteed to be zero), the calculation is performed mod 2^20 and may
// harmlessly underflow if the address mod 2^20 is below the object
// address.
uptr obj_offset =
(untagged_addr - fp - local.frame_offset) & (kRecordFPModulus - 1);
if (obj_offset >= local.size)
continue;
// Guess top bits of local variable from the faulting address, because
// we only store bits 4-19 of FP (bits 0-3 are guaranteed to be zero).
uptr local_beg = (fp + local.frame_offset) |
(untagged_addr & ~(uptr(kRecordFPModulus) - 1));
uptr local_end = local_beg + local.size;

if (!found_local) {
Printf("\nPotentially referenced stack objects:\n");
found_local = true;
}

uptr offset;
const char *whence;
const char *cause;
if (local_beg <= untagged_addr && untagged_addr < local_end) {
offset = untagged_addr - local_beg;
whence = "inside";
cause = "use-after-scope";
} else if (untagged_addr >= local_end) {
offset = untagged_addr - local_end;
whence = "after";
cause = "stack-buffer-overflow";
} else {
offset = local_beg - untagged_addr;
whence = "before";
cause = "stack-buffer-overflow";
}
Decorator d;
Printf("%s", d.Error());
Printf("Cause: %s\n", cause);
Printf("%s", d.Default());
Printf("%s", d.Location());
Printf("%p is located %zd bytes %s a %zd-byte region [%p,%p)\n",
untagged_addr, offset, whence, local_end - local_beg, local_beg,
local_end);
Printf("%s", d.Allocation());
StackTracePrinter::GetOrInit()->RenderSourceLocation(
&location, local.decl_file, local.decl_line, /* column= */ 0,
common_flags()->symbolize_vs_style,
common_flags()->strip_path_prefix);
Printf(" %s in %s %s\n", local.name, local.function_name,
location.data());
location.clear();
Printf("%s\n", d.Default());
}
frame.Clear();
}
Expand Down Expand Up @@ -751,8 +779,6 @@ void BaseReport::PrintAddressDescription() const {
// Check stack first. If the address is on the stack of a live thread, we
// know it cannot be a heap / global overflow.
for (const auto &sa : allocations.stack) {
// 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());
Expand Down
25 changes: 25 additions & 0 deletions compiler-rt/test/hwasan/TestCases/stack-overflow.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// RUN: %clang_hwasan -g %s -o %t && not %run %t 2>&1 | FileCheck %s

// Stack histories currently are not recorded on x86.
// XFAIL: target=x86_64{{.*}}

__attribute((noinline)) void buggy() {
char c[64];
char *volatile p = c;
p[65] = 0;
}

int main() {
buggy();
// CHECK: WRITE of size 1 at
// CHECK: #0 {{.*}} in buggy{{.*}}stack-overflow.c:[[@LINE-6]]
// CHECK: Cause: stack tag-mismatch
// CHECK: is located in stack of thread
// CHECK: Potentially referenced stack objects:
// CHECK: Cause: stack-buffer-overflow
// CHECK-NEXT: 0x{{.*}} is located 1 bytes after a 64-byte region
// CHECK-NEXT: c in buggy {{.*}}stack-overflow.c:
// CHECK: Memory tags around the buggy address

// CHECK: SUMMARY: HWAddressSanitizer: tag-mismatch {{.*}} in buggy
}
2 changes: 2 additions & 0 deletions compiler-rt/test/hwasan/TestCases/stack-uar-dynamic.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ char *buggy(int b) {
int main() {
char *p = buggy(1);
// CHECK: Potentially referenced stack objects:
// CHECK-NEXT: use-after-scope
// CHECK-NEXT: 0x{{.*}} is located 0 bytes inside a 64-byte region
// CHECK-NEXT: c in buggy
p[0] = 0;
}
2 changes: 2 additions & 0 deletions compiler-rt/test/hwasan/TestCases/stack-uar.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ int main() {
// CHECK: Cause: stack tag-mismatch
// CHECK: is located in stack of thread
// CHECK: Potentially referenced stack objects:
// CHECK: Cause: use-after-scope
// CHECK-NEXT: 0x{{.*}} is located 0 bytes inside a 2048-byte region
// CHECK-NEXT: {{zzz|yyy}} in buggy {{.*}}stack-uar.c:
// CHECK: Memory tags around the buggy address

Expand Down
25 changes: 25 additions & 0 deletions compiler-rt/test/hwasan/TestCases/stack-underflow.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// RUN: %clang_hwasan -g %s -o %t && not %run %t 2>&1 | FileCheck %s

// Stack histories currently are not recorded on x86.
// XFAIL: target=x86_64{{.*}}

__attribute((noinline)) void buggy() {
char c[64];
char *volatile p = c;
p[-2] = 0;
}

int main() {
buggy();
// CHECK: WRITE of size 1 at
// CHECK: #0 {{.*}} in buggy{{.*}}stack-underflow.c:[[@LINE-6]]
// CHECK: Cause: stack tag-mismatch
// CHECK: is located in stack of thread
// CHECK: Potentially referenced stack objects:
// CHECK: Cause: stack-buffer-overflow
// CHECK-NEXT: 0x{{.*}} is located 2 bytes before a 64-byte region
// CHECK-NEXT: c in buggy {{.*}}stack-underflow.c:
// CHECK: Memory tags around the buggy address

// CHECK: SUMMARY: HWAddressSanitizer: tag-mismatch {{.*}} in buggy
}