Skip to content

Commit 39e32b4

Browse files
authored
[hwasan] Fix stack tag mismatch report (#81939)
Existing code worked only for local, recorder FP, and the faulty address are the same 1 MiB page. Now, instead of guessing FP, we guess variable address. We need to try just two cases of addresses around of faulty one. Fixes google/sanitizers#1723
1 parent 0065161 commit 39e32b4

File tree

2 files changed

+69
-27
lines changed

2 files changed

+69
-27
lines changed

compiler-rt/lib/hwasan/hwasan_report.cpp

Lines changed: 60 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -230,33 +230,68 @@ static void PrintStackAllocations(const StackAllocationsRingBuffer *sa,
230230
tag_t obj_tag = base_tag ^ local.tag_offset;
231231
if (obj_tag != addr_tag)
232232
continue;
233-
// Guess top bits of local variable from the faulting address, because
234-
// we only store bits 4-19 of FP (bits 0-3 are guaranteed to be zero).
235-
uptr local_beg = (fp + local.frame_offset) |
236-
(untagged_addr & ~(uptr(kRecordFPModulus) - 1));
237-
uptr local_end = local_beg + local.size;
233+
234+
// We only store bits 4-19 of FP (bits 0-3 are guaranteed to be zero).
235+
// So we know only `FP % kRecordFPModulus`, and we can only calculate
236+
// `local_beg % kRecordFPModulus`.
237+
// Out of all possible `local_beg` we will only consider 2 candidates
238+
// nearest to the `untagged_addr`.
239+
uptr local_beg_mod = (fp + local.frame_offset) % kRecordFPModulus;
240+
// Pick `local_beg` in the same 1 MiB block as `untagged_addr`.
241+
uptr local_beg =
242+
RoundDownTo(untagged_addr, kRecordFPModulus) + local_beg_mod;
243+
// Pick the largest `local_beg <= untagged_addr`. It's either the current
244+
// one or the one before.
245+
if (local_beg > untagged_addr)
246+
local_beg -= kRecordFPModulus;
247+
248+
uptr offset = -1ull;
249+
const char *whence;
250+
const char *cause = nullptr;
251+
uptr best_beg;
252+
253+
// Try two 1 MiB blocks options and pick nearest one.
254+
for (uptr i = 0; i < 2; ++i, local_beg += kRecordFPModulus) {
255+
uptr local_end = local_beg + local.size;
256+
if (local_beg > local_end)
257+
continue; // This is a wraparound.
258+
if (local_beg <= untagged_addr && untagged_addr < local_end) {
259+
offset = untagged_addr - local_beg;
260+
whence = "inside";
261+
cause = "use-after-scope";
262+
best_beg = local_beg;
263+
break; // This is as close at it can be.
264+
}
265+
266+
if (untagged_addr >= local_end) {
267+
uptr new_offset = untagged_addr - local_end;
268+
if (new_offset < offset) {
269+
offset = new_offset;
270+
whence = "after";
271+
cause = "stack-buffer-overflow";
272+
best_beg = local_beg;
273+
}
274+
} else {
275+
uptr new_offset = local_beg - untagged_addr;
276+
if (new_offset < offset) {
277+
offset = new_offset;
278+
whence = "before";
279+
cause = "stack-buffer-overflow";
280+
best_beg = local_beg;
281+
}
282+
}
283+
}
284+
285+
// To fail the `untagged_addr` must be near nullptr, which is impossible
286+
// with Linux user space memory layout.
287+
if (!cause)
288+
continue;
238289

239290
if (!found_local) {
240291
Printf("\nPotentially referenced stack objects:\n");
241292
found_local = true;
242293
}
243294

244-
uptr offset;
245-
const char *whence;
246-
const char *cause;
247-
if (local_beg <= untagged_addr && untagged_addr < local_end) {
248-
offset = untagged_addr - local_beg;
249-
whence = "inside";
250-
cause = "use-after-scope";
251-
} else if (untagged_addr >= local_end) {
252-
offset = untagged_addr - local_end;
253-
whence = "after";
254-
cause = "stack-buffer-overflow";
255-
} else {
256-
offset = local_beg - untagged_addr;
257-
whence = "before";
258-
cause = "stack-buffer-overflow";
259-
}
260295
Decorator d;
261296
Printf("%s", d.Error());
262297
Printf("Cause: %s\n", cause);
@@ -267,10 +302,11 @@ static void PrintStackAllocations(const StackAllocationsRingBuffer *sa,
267302
common_flags()->symbolize_vs_style,
268303
common_flags()->strip_path_prefix);
269304
Printf(
270-
"%p is located %zd bytes %s a %zd-byte local variable %s [%p,%p) "
305+
"%p is located %zd bytes %s a %zd-byte local variable %s "
306+
"[%p,%p) "
271307
"in %s %s\n",
272-
untagged_addr, offset, whence, local_end - local_beg, local.name,
273-
local_beg, local_end, local.function_name, location.data());
308+
untagged_addr, offset, whence, local.size, local.name, best_beg,
309+
best_beg + local.size, local.function_name, location.data());
274310
location.clear();
275311
Printf("%s\n", d.Default());
276312
}

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

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Tests use-after-scope detection and reporting.
22
// RUN: %clang_hwasan -O0 -g %s -o %t && not %run %t 2>&1 | FileCheck %s
33
// RUN: %clang_hwasan -O2 -g %s -o %t && not %run %t 2>&1 | FileCheck %s
4+
// RUN: %clang_hwasan -O2 -g %s -DBUFFER_SIZE=1000000 -o %t && not %run %t 2>&1 | FileCheck %s
5+
// RUN: %clang_hwasan -O2 -g %s -DBUFFER_SIZE=2000000 -o %t && not %run %t 2>&1 | FileCheck %s
46
// RUN: %clang_hwasan -g %s -o %t && not %env_hwasan_opts=symbolize=0 %run %t 2>&1 | FileCheck %s --check-prefix=NOSYM
57

68
// RUN: %clang_hwasan -mllvm -hwasan-use-after-scope=false -g %s -o %t && %run %t 2>&1
@@ -18,6 +20,10 @@
1820
#include <sanitizer/hwasan_interface.h>
1921
#include <stdio.h>
2022

23+
#ifndef BUFFER_SIZE
24+
# define BUFFER_SIZE 0x800
25+
#endif
26+
2127
void USE(void *x) { // pretend_to_do_something(void *x)
2228
__asm__ __volatile__(""
2329
:
@@ -41,8 +47,8 @@ __attribute__((noinline)) void Unrelated3() {
4147
__attribute__((noinline)) char buggy() {
4248
char *volatile p;
4349
{
44-
char zzz[0x800] = {};
45-
char yyy[0x800] = {};
50+
char zzz[BUFFER_SIZE] = {};
51+
char yyy[BUFFER_SIZE] = {};
4652
// With -hwasan-generate-tags-with-calls=false, stack tags can occasionally
4753
// be zero, leading to a false negative
4854
// (https://github.com/llvm/llvm-project/issues/69221). Work around it by
@@ -71,7 +77,7 @@ int main() {
7177
// CHECK: is located in stack of thread
7278
// CHECK: Potentially referenced stack objects:
7379
// CHECK: Cause: use-after-scope
74-
// CHECK-NEXT: 0x{{.*}} is located 0 bytes inside a 2048-byte local variable {{zzz|yyy}} [0x{{.*}}) in buggy {{.*}}stack-uas.c:
80+
// CHECK-NEXT: 0x{{.*}} is located 0 bytes inside a {{.*}}-byte local variable {{zzz|yyy}} [0x{{.*}}) in buggy {{.*}}stack-uas.c:
7581
// CHECK: Memory tags around the buggy address
7682

7783
// NOSYM: Previously allocated frames:

0 commit comments

Comments
 (0)