Skip to content

Commit 090f0f9

Browse files
committed
[hwasan] Record and display stack history in stack-based reports.
Summary: Display a list of recent stack frames (not a stack trace!) when tag-mismatch is detected on a stack address. The implementation uses alignment tricks to get both the address of the history buffer, and the base address of the shadow with a single 8-byte load. See the comment in hwasan_thread_list.h for more details. Developed in collaboration with Kostya Serebryany. Reviewers: kcc Subscribers: srhines, kubamracek, mgorny, hiraditya, jfb, llvm-commits Differential Revision: https://reviews.llvm.org/D52249 llvm-svn: 342923
1 parent 20c4999 commit 090f0f9

File tree

21 files changed

+908
-228
lines changed

21 files changed

+908
-228
lines changed

compiler-rt/lib/hwasan/CMakeLists.txt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ set(HWASAN_RTL_SOURCES
1010
hwasan_poisoning.cc
1111
hwasan_report.cc
1212
hwasan_thread.cc
13+
hwasan_thread_list.cc
1314
)
1415

1516
set(HWASAN_RTL_CXX_SOURCES
@@ -25,8 +26,9 @@ set(HWASAN_RTL_HEADERS
2526
hwasan_mapping.h
2627
hwasan_poisoning.h
2728
hwasan_report.h
28-
hwasan_thread.h)
29-
29+
hwasan_thread.h
30+
hwasan_thread_list.h
31+
)
3032

3133
set(HWASAN_DEFINITIONS)
3234
append_list_if(COMPILER_RT_HWASAN_WITH_INTERCEPTORS HWASAN_WITH_INTERCEPTORS=1 HWASAN_DEFINITIONS)

compiler-rt/lib/hwasan/hwasan.cc

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "hwasan_poisoning.h"
1818
#include "hwasan_report.h"
1919
#include "hwasan_thread.h"
20+
#include "hwasan_thread_list.h"
2021
#include "sanitizer_common/sanitizer_atomic.h"
2122
#include "sanitizer_common/sanitizer_common.h"
2223
#include "sanitizer_common/sanitizer_flags.h"
@@ -174,7 +175,8 @@ static void HWAsanCheckFailed(const char *file, int line, const char *cond,
174175
static constexpr uptr kMemoryUsageBufferSize = 4096;
175176

176177
static void HwasanFormatMemoryUsage(InternalScopedString &s) {
177-
auto thread_stats = Thread::GetThreadStats();
178+
HwasanThreadList &thread_list = hwasanThreadList();
179+
auto thread_stats = thread_list.GetThreadStats();
178180
auto *sds = StackDepotGetStats();
179181
AllocatorStatCounters asc;
180182
GetAllocatorStats(asc);
@@ -184,7 +186,7 @@ static void HwasanFormatMemoryUsage(InternalScopedString &s) {
184186
" heap: %zd",
185187
internal_getpid(), GetRSS(), thread_stats.n_live_threads,
186188
thread_stats.total_stack_size,
187-
thread_stats.n_live_threads * Thread::MemoryUsedPerThread(),
189+
thread_stats.n_live_threads * thread_list.MemoryUsedPerThread(),
188190
sds->allocated, sds->n_uniq_ids, asc[AllocatorStatMapped]);
189191
}
190192

@@ -253,7 +255,12 @@ void __hwasan_init() {
253255
__sanitizer_set_report_path(common_flags()->log_path);
254256

255257
DisableCoreDumperIfNecessary();
258+
256259
__hwasan_shadow_init();
260+
261+
InitThreads();
262+
hwasanThreadList().CreateCurrentThread();
263+
257264
MadviseShadow();
258265

259266
// This may call libc -> needs initialized shadow.
@@ -268,11 +275,10 @@ void __hwasan_init() {
268275
InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir);
269276

270277
HwasanTSDInit();
278+
HwasanTSDThreadInit();
271279

272280
HwasanAllocatorInit();
273281

274-
Thread::Create();
275-
276282
#if HWASAN_CONTAINS_UBSAN
277283
__ubsan::InitAsPlugin();
278284
#endif

compiler-rt/lib/hwasan/hwasan.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ typedef u8 tag_t;
4141
const unsigned kAddressTagShift = 56;
4242
const uptr kAddressTagMask = 0xFFUL << kAddressTagShift;
4343

44+
// Minimal alignment of the shadow base address. Determines the space available
45+
// for threads and stack histories. This is an ABI constant.
46+
const unsigned kShadowBaseAlignment = 32;
47+
4448
static inline tag_t GetTagFromPointer(uptr p) {
4549
return p >> kAddressTagShift;
4650
}
@@ -66,6 +70,7 @@ extern int hwasan_report_count;
6670

6771
bool ProtectRange(uptr beg, uptr end);
6872
bool InitShadow();
73+
void InitThreads();
6974
void MadviseShadow();
7075
char *GetProcSelfMaps();
7176
void InitializeInterceptors();
@@ -142,6 +147,7 @@ class ScopedThreadLocalStateBackup {
142147
};
143148

144149
void HwasanTSDInit();
150+
void HwasanTSDThreadInit();
145151

146152
void HwasanOnDeadlySignal(int signo, void *info, void *context);
147153

compiler-rt/lib/hwasan/hwasan_dynamic_shadow.cc

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
///
1414
//===----------------------------------------------------------------------===//
1515

16+
#include "hwasan.h"
1617
#include "hwasan_dynamic_shadow.h"
1718
#include "hwasan_mapping.h"
1819
#include "sanitizer_common/sanitizer_common.h"
@@ -35,12 +36,16 @@ static void UnmapFromTo(uptr from, uptr to) {
3536
}
3637
}
3738

38-
// Returns an address aligned to 8 pages, such that one page on the left and
39-
// shadow_size_bytes bytes on the right of it are mapped r/o.
39+
// Returns an address aligned to kShadowBaseAlignment, such that
40+
// 2**kShadowBaseAlingment on the left and shadow_size_bytes bytes on the right
41+
// of it are mapped no access.
4042
static uptr MapDynamicShadow(uptr shadow_size_bytes) {
4143
const uptr granularity = GetMmapGranularity();
42-
const uptr alignment = granularity << kShadowScale;
43-
const uptr left_padding = granularity;
44+
const uptr min_alignment = granularity << kShadowScale;
45+
const uptr alignment = 1ULL << kShadowBaseAlignment;
46+
CHECK_GE(alignment, min_alignment);
47+
48+
const uptr left_padding = 1ULL << kShadowBaseAlignment;
4449
const uptr shadow_size =
4550
RoundUpTo(shadow_size_bytes, granularity);
4651
const uptr map_size = shadow_size + left_padding + alignment;

compiler-rt/lib/hwasan/hwasan_flags.inc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,7 @@ HWASAN_FLAG(int, heap_history_size, 1023,
5151
"to find bugs.")
5252
HWASAN_FLAG(bool, export_memory_stats, true,
5353
"Export up-to-date memory stats through /proc")
54+
HWASAN_FLAG(int, stack_history_size, 1024,
55+
"The number of stack frames remembered per thread. "
56+
"Affects the quality of stack-related reports, but not the ability "
57+
"to find bugs.")

compiler-rt/lib/hwasan/hwasan_linux.cc

Lines changed: 44 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "hwasan_mapping.h"
2323
#include "hwasan_report.h"
2424
#include "hwasan_thread.h"
25+
#include "hwasan_thread_list.h"
2526

2627
#include <elf.h>
2728
#include <link.h>
@@ -37,6 +38,10 @@
3738
#include "sanitizer_common/sanitizer_common.h"
3839
#include "sanitizer_common/sanitizer_procmaps.h"
3940

41+
#if HWASAN_WITH_INTERCEPTORS && !SANITIZER_ANDROID
42+
THREADLOCAL uptr __hwasan_tls;
43+
#endif
44+
4045
namespace __hwasan {
4146

4247
static void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name) {
@@ -179,6 +184,20 @@ bool InitShadow() {
179184
return true;
180185
}
181186

187+
void InitThreads() {
188+
CHECK(__hwasan_shadow_memory_dynamic_address);
189+
uptr guard_page_size = GetMmapGranularity();
190+
uptr thread_space_start =
191+
__hwasan_shadow_memory_dynamic_address - (1ULL << kShadowBaseAlignment);
192+
uptr thread_space_end =
193+
__hwasan_shadow_memory_dynamic_address - guard_page_size;
194+
ReserveShadowMemoryRange(thread_space_start, thread_space_end - 1,
195+
"hwasan threads");
196+
ProtectGap(thread_space_end,
197+
__hwasan_shadow_memory_dynamic_address - thread_space_end);
198+
InitThreadList(thread_space_start, thread_space_end - thread_space_start);
199+
}
200+
182201
static void MadviseShadowRegion(uptr beg, uptr end) {
183202
uptr size = end - beg + 1;
184203
if (common_flags()->no_huge_pages_for_shadow)
@@ -214,29 +233,33 @@ void InstallAtExitHandler() {
214233
// ---------------------- TSD ---------------- {{{1
215234

216235
extern "C" void __hwasan_thread_enter() {
217-
Thread::Create();
236+
hwasanThreadList().CreateCurrentThread();
218237
}
219238

220239
extern "C" void __hwasan_thread_exit() {
221240
Thread *t = GetCurrentThread();
222241
// Make sure that signal handler can not see a stale current thread pointer.
223242
atomic_signal_fence(memory_order_seq_cst);
224243
if (t)
225-
t->Destroy();
244+
hwasanThreadList().ReleaseThread(t);
226245
}
227246

228247
#if HWASAN_WITH_INTERCEPTORS
229248
static pthread_key_t tsd_key;
230249
static bool tsd_key_inited = false;
231250

251+
void HwasanTSDThreadInit() {
252+
if (tsd_key_inited)
253+
CHECK_EQ(0, pthread_setspecific(tsd_key,
254+
(void *)GetPthreadDestructorIterations()));
255+
}
256+
232257
void HwasanTSDDtor(void *tsd) {
233-
Thread *t = (Thread*)tsd;
234-
if (t->destructor_iterations_ > 1) {
235-
t->destructor_iterations_--;
236-
CHECK_EQ(0, pthread_setspecific(tsd_key, tsd));
258+
uptr iterations = (uptr)tsd;
259+
if (iterations > 1) {
260+
CHECK_EQ(0, pthread_setspecific(tsd_key, (void *)(iterations - 1)));
237261
return;
238262
}
239-
t->Destroy();
240263
__hwasan_thread_exit();
241264
}
242265

@@ -245,31 +268,26 @@ void HwasanTSDInit() {
245268
tsd_key_inited = true;
246269
CHECK_EQ(0, pthread_key_create(&tsd_key, HwasanTSDDtor));
247270
}
248-
249-
Thread *GetCurrentThread() {
250-
return (Thread *)pthread_getspecific(tsd_key);
251-
}
252-
253-
void SetCurrentThread(Thread *t) {
254-
// Make sure that HwasanTSDDtor gets called at the end.
255-
CHECK(tsd_key_inited);
256-
// Make sure we do not reset the current Thread.
257-
CHECK_EQ(0, pthread_getspecific(tsd_key));
258-
pthread_setspecific(tsd_key, (void *)t);
259-
}
260-
#elif SANITIZER_ANDROID
271+
#else
261272
void HwasanTSDInit() {}
262-
Thread *GetCurrentThread() {
263-
return (Thread*)*get_android_tls_ptr();
264-
}
273+
void HwasanTSDThreadInit() {}
274+
#endif
265275

266-
void SetCurrentThread(Thread *t) {
267-
*get_android_tls_ptr() = (uptr)t;
276+
#if SANITIZER_ANDROID
277+
uptr *GetCurrentThreadLongPtr() {
278+
return (uptr *)get_android_tls_ptr();
268279
}
269280
#else
270-
#error unsupported configuration !HWASAN_WITH_INTERCEPTORS && !SANITIZER_ANDROID
281+
uptr *GetCurrentThreadLongPtr() {
282+
return &__hwasan_tls;
283+
}
271284
#endif
272285

286+
Thread *GetCurrentThread() {
287+
auto *R = (StackAllocationsRingBuffer*)GetCurrentThreadLongPtr();
288+
return hwasanThreadList().GetThreadByBufferAddress((uptr)(R->Next()));
289+
}
290+
273291
struct AccessInfo {
274292
uptr addr;
275293
uptr size;

compiler-rt/lib/hwasan/hwasan_report.cc

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "hwasan_allocator.h"
1717
#include "hwasan_mapping.h"
1818
#include "hwasan_thread.h"
19+
#include "hwasan_thread_list.h"
1920
#include "sanitizer_common/sanitizer_allocator_internal.h"
2021
#include "sanitizer_common/sanitizer_common.h"
2122
#include "sanitizer_common/sanitizer_flags.h"
@@ -35,6 +36,31 @@ static StackTrace GetStackTraceFromId(u32 id) {
3536
return res;
3637
}
3738

39+
// A RAII object that holds a copy of the current thread stack ring buffer.
40+
// The actual stack buffer may change while we are iterating over it (for
41+
// example, Printf may call syslog() which can itself be built with hwasan).
42+
class SavedStackAllocations {
43+
public:
44+
SavedStackAllocations(StackAllocationsRingBuffer *rb) {
45+
uptr size = rb->size() * sizeof(uptr);
46+
void *storage =
47+
MmapAlignedOrDieOnFatalError(size, size * 2, "saved stack allocations");
48+
new (&rb_) StackAllocationsRingBuffer(*rb, storage);
49+
}
50+
51+
~SavedStackAllocations() {
52+
StackAllocationsRingBuffer *rb = get();
53+
UnmapOrDie(rb->StartOfStorage(), rb->size() * sizeof(uptr));
54+
}
55+
56+
StackAllocationsRingBuffer *get() {
57+
return (StackAllocationsRingBuffer *)&rb_;
58+
}
59+
60+
private:
61+
uptr rb_;
62+
};
63+
3864
class Decorator: public __sanitizer::SanitizerCommonDecorator {
3965
public:
4066
Decorator() : SanitizerCommonDecorator() { }
@@ -63,7 +89,9 @@ uptr FindHeapAllocation(HeapAllocationsRingBuffer *rb,
6389
return 0;
6490
}
6591

66-
void PrintAddressDescription(uptr tagged_addr, uptr access_size) {
92+
void PrintAddressDescription(
93+
uptr tagged_addr, uptr access_size,
94+
StackAllocationsRingBuffer *current_stack_allocations) {
6795
Decorator d;
6896
int num_descriptions_printed = 0;
6997
uptr untagged_addr = UntagAddr(tagged_addr);
@@ -109,7 +137,7 @@ void PrintAddressDescription(uptr tagged_addr, uptr access_size) {
109137
}
110138
}
111139

112-
Thread::VisitAllLiveThreads([&](Thread *t) {
140+
hwasanThreadList().VisitAllLiveThreads([&](Thread *t) {
113141
// Scan all threads' ring buffers to find if it's a heap-use-after-free.
114142
HeapAllocationRecord har;
115143
if (uptr D = FindHeapAllocation(t->heap_allocations(), tagged_addr, &har)) {
@@ -145,6 +173,25 @@ void PrintAddressDescription(uptr tagged_addr, uptr access_size) {
145173
Printf("%s", d.Default());
146174
t->Announce();
147175

176+
// Temporary report section, needs to be improved.
177+
Printf("Previosly allocated frames:\n");
178+
auto *sa = (t == GetCurrentThread() && current_stack_allocations)
179+
? current_stack_allocations
180+
: t->stack_allocations();
181+
uptr frames = Min((uptr)flags()->stack_history_size, sa->size());
182+
for (uptr i = 0; i < frames; i++) {
183+
uptr record = (*sa)[i];
184+
if (!record)
185+
break;
186+
uptr sp = (record >> 48) << 4;
187+
uptr pc_mask = (1ULL << 48) - 1;
188+
uptr pc = record & pc_mask;
189+
uptr fixed_pc = StackTrace::GetNextInstructionPc(pc);
190+
StackTrace stack(&fixed_pc, 1);
191+
Printf("record: %p pc: %p sp: %p", record, pc, sp);
192+
stack.Print();
193+
}
194+
148195
num_descriptions_printed++;
149196
}
150197
});
@@ -170,13 +217,16 @@ void ReportStats() {}
170217
void ReportInvalidAccessInsideAddressRange(const char *what, const void *start,
171218
uptr size, uptr offset) {
172219
ScopedErrorReportLock l;
220+
SavedStackAllocations current_stack_allocations(
221+
GetCurrentThread()->stack_allocations());
173222

174223
Decorator d;
175224
Printf("%s", d.Warning());
176225
Printf("%sTag mismatch in %s%s%s at offset %zu inside [%p, %zu)%s\n",
177226
d.Warning(), d.Name(), what, d.Warning(), offset, start, size,
178227
d.Default());
179-
PrintAddressDescription((uptr)start + offset, 1);
228+
PrintAddressDescription((uptr)start + offset, 1,
229+
current_stack_allocations.get());
180230
// if (__sanitizer::Verbosity())
181231
// DescribeMemoryRange(start, size);
182232
}
@@ -224,7 +274,7 @@ void ReportInvalidFree(StackTrace *stack, uptr tagged_addr) {
224274

225275
stack->Print();
226276

227-
PrintAddressDescription(tagged_addr, 0);
277+
PrintAddressDescription(tagged_addr, 0, nullptr);
228278

229279
PrintTagsAroundAddr(tag_ptr);
230280

@@ -235,6 +285,8 @@ void ReportInvalidFree(StackTrace *stack, uptr tagged_addr) {
235285
void ReportTagMismatch(StackTrace *stack, uptr tagged_addr, uptr access_size,
236286
bool is_store) {
237287
ScopedErrorReportLock l;
288+
SavedStackAllocations current_stack_allocations(
289+
GetCurrentThread()->stack_allocations());
238290

239291
Decorator d;
240292
Printf("%s", d.Error());
@@ -258,7 +310,8 @@ void ReportTagMismatch(StackTrace *stack, uptr tagged_addr, uptr access_size,
258310

259311
stack->Print();
260312

261-
PrintAddressDescription(tagged_addr, access_size);
313+
PrintAddressDescription(tagged_addr, access_size,
314+
current_stack_allocations.get());
262315
t->Announce();
263316

264317
PrintTagsAroundAddr(tag_ptr);

0 commit comments

Comments
 (0)