Skip to content

Commit 7db28dd

Browse files
authored
[hwasan] Classify stack overflow, and use after scope (#76133)
We can't distinguish UAR and UAS, but by definition UAR is already UAS.
1 parent 033ec09 commit 7db28dd

File tree

5 files changed

+91
-11
lines changed

5 files changed

+91
-11
lines changed

compiler-rt/lib/hwasan/hwasan_report.cpp

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -221,29 +221,57 @@ static void PrintStackAllocations(const StackAllocationsRingBuffer *sa,
221221
for (LocalInfo &local : frame.locals) {
222222
if (!local.has_frame_offset || !local.has_size || !local.has_tag_offset)
223223
continue;
224+
if (!(local.name && internal_strlen(local.name)) &&
225+
!(local.function_name && internal_strlen(local.name)) &&
226+
!(local.decl_file && internal_strlen(local.decl_file)))
227+
continue;
224228
tag_t obj_tag = base_tag ^ local.tag_offset;
225229
if (obj_tag != addr_tag)
226230
continue;
227-
// Calculate the offset from the object address to the faulting
228-
// address. Because we only store bits 4-19 of FP (bits 0-3 are
229-
// guaranteed to be zero), the calculation is performed mod 2^20 and may
230-
// harmlessly underflow if the address mod 2^20 is below the object
231-
// address.
232-
uptr obj_offset =
233-
(untagged_addr - fp - local.frame_offset) & (kRecordFPModulus - 1);
234-
if (obj_offset >= local.size)
235-
continue;
231+
// Guess top bits of local variable from the faulting address, because
232+
// we only store bits 4-19 of FP (bits 0-3 are guaranteed to be zero).
233+
uptr local_beg = (fp + local.frame_offset) |
234+
(untagged_addr & ~(uptr(kRecordFPModulus) - 1));
235+
uptr local_end = local_beg + local.size;
236+
236237
if (!found_local) {
237238
Printf("\nPotentially referenced stack objects:\n");
238239
found_local = true;
239240
}
241+
242+
uptr offset;
243+
const char *whence;
244+
const char *cause;
245+
if (local_beg <= untagged_addr && untagged_addr < local_end) {
246+
offset = untagged_addr - local_beg;
247+
whence = "inside";
248+
cause = "use-after-scope";
249+
} else if (untagged_addr >= local_end) {
250+
offset = untagged_addr - local_end;
251+
whence = "after";
252+
cause = "stack-buffer-overflow";
253+
} else {
254+
offset = local_beg - untagged_addr;
255+
whence = "before";
256+
cause = "stack-buffer-overflow";
257+
}
258+
Decorator d;
259+
Printf("%s", d.Error());
260+
Printf("Cause: %s\n", cause);
261+
Printf("%s", d.Default());
262+
Printf("%s", d.Location());
263+
Printf("%p is located %zd bytes %s a %zd-byte region [%p,%p)\n",
264+
untagged_addr, offset, whence, local_end - local_beg, local_beg,
265+
local_end);
266+
Printf("%s", d.Allocation());
240267
StackTracePrinter::GetOrInit()->RenderSourceLocation(
241268
&location, local.decl_file, local.decl_line, /* column= */ 0,
242269
common_flags()->symbolize_vs_style,
243270
common_flags()->strip_path_prefix);
244271
Printf(" %s in %s %s\n", local.name, local.function_name,
245272
location.data());
246273
location.clear();
274+
Printf("%s\n", d.Default());
247275
}
248276
frame.Clear();
249277
}
@@ -751,8 +779,6 @@ void BaseReport::PrintAddressDescription() const {
751779
// Check stack first. If the address is on the stack of a live thread, we
752780
// know it cannot be a heap / global overflow.
753781
for (const auto &sa : allocations.stack) {
754-
// TODO(fmayer): figure out how to distinguish use-after-return and
755-
// stack-buffer-overflow.
756782
Printf("%s", d.Error());
757783
Printf("\nCause: stack tag-mismatch\n");
758784
Printf("%s", d.Location());
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// RUN: %clang_hwasan -g %s -o %t && not %run %t 2>&1 | FileCheck %s
2+
3+
// Stack histories currently are not recorded on x86.
4+
// XFAIL: target=x86_64{{.*}}
5+
6+
__attribute((noinline)) void buggy() {
7+
char c[64];
8+
char *volatile p = c;
9+
p[65] = 0;
10+
}
11+
12+
int main() {
13+
buggy();
14+
// CHECK: WRITE of size 1 at
15+
// CHECK: #0 {{.*}} in buggy{{.*}}stack-overflow.c:[[@LINE-6]]
16+
// CHECK: Cause: stack tag-mismatch
17+
// CHECK: is located in stack of thread
18+
// CHECK: Potentially referenced stack objects:
19+
// CHECK: Cause: stack-buffer-overflow
20+
// CHECK-NEXT: 0x{{.*}} is located 1 bytes after a 64-byte region
21+
// CHECK-NEXT: c in buggy {{.*}}stack-overflow.c:
22+
// CHECK: Memory tags around the buggy address
23+
24+
// CHECK: SUMMARY: HWAddressSanitizer: tag-mismatch {{.*}} in buggy
25+
}

compiler-rt/test/hwasan/TestCases/stack-uar-dynamic.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ char *buggy(int b) {
2121
int main() {
2222
char *p = buggy(1);
2323
// CHECK: Potentially referenced stack objects:
24+
// CHECK-NEXT: use-after-scope
25+
// CHECK-NEXT: 0x{{.*}} is located 0 bytes inside a 64-byte region
2426
// CHECK-NEXT: c in buggy
2527
p[0] = 0;
2628
}

compiler-rt/test/hwasan/TestCases/stack-uar.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ int main() {
5050
// CHECK: Cause: stack tag-mismatch
5151
// CHECK: is located in stack of thread
5252
// CHECK: Potentially referenced stack objects:
53+
// CHECK: Cause: use-after-scope
54+
// CHECK-NEXT: 0x{{.*}} is located 0 bytes inside a 2048-byte region
5355
// CHECK-NEXT: {{zzz|yyy}} in buggy {{.*}}stack-uar.c:
5456
// CHECK: Memory tags around the buggy address
5557

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// RUN: %clang_hwasan -g %s -o %t && not %run %t 2>&1 | FileCheck %s
2+
3+
// Stack histories currently are not recorded on x86.
4+
// XFAIL: target=x86_64{{.*}}
5+
6+
__attribute((noinline)) void buggy() {
7+
char c[64];
8+
char *volatile p = c;
9+
p[-2] = 0;
10+
}
11+
12+
int main() {
13+
buggy();
14+
// CHECK: WRITE of size 1 at
15+
// CHECK: #0 {{.*}} in buggy{{.*}}stack-underflow.c:[[@LINE-6]]
16+
// CHECK: Cause: stack tag-mismatch
17+
// CHECK: is located in stack of thread
18+
// CHECK: Potentially referenced stack objects:
19+
// CHECK: Cause: stack-buffer-overflow
20+
// CHECK-NEXT: 0x{{.*}} is located 2 bytes before a 64-byte region
21+
// CHECK-NEXT: c in buggy {{.*}}stack-underflow.c:
22+
// CHECK: Memory tags around the buggy address
23+
24+
// CHECK: SUMMARY: HWAddressSanitizer: tag-mismatch {{.*}} in buggy
25+
}

0 commit comments

Comments
 (0)