Skip to content

Commit fb65131

Browse files
Add API to temporalily suppress usage of ASAN's fake stack (#160135)
Intended use-case is for threads that use (or switch to) stack with special properties e.g. backed by MADV_DONTDUMP memory. --------- Co-authored-by: Vitaly Buka <vitalybuka@google.com>
1 parent 74f49a2 commit fb65131

File tree

10 files changed

+141
-1
lines changed

10 files changed

+141
-1
lines changed

compiler-rt/include/sanitizer/asan_interface.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,14 @@ void SANITIZER_CDECL __asan_handle_no_return(void);
333333
/// trace. Returns 1 if successful, 0 if not.
334334
int SANITIZER_CDECL __asan_update_allocation_context(void *addr);
335335

336+
/// Suppresses fake stack for the current thread.
337+
/// Temporarily disables use-after-return detection for current thread.
338+
void SANITIZER_CDECL __asan_suppress_fake_stack(void);
339+
340+
/// Unsupresses fake stack for the current thread.
341+
/// Should be paired with a previous __asan_suppress_fake_stack() call.
342+
void SANITIZER_CDECL __asan_unsuppress_fake_stack(void);
343+
336344
#ifdef __cplusplus
337345
} // extern "C"
338346
#endif

compiler-rt/lib/asan/asan_fake_stack.cpp

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,9 +225,23 @@ static void SetTLSFakeStack(FakeStack*) {}
225225
void ResetTLSFakeStack() {}
226226
#endif // (SANITIZER_LINUX && !SANITIZER_ANDROID) || SANITIZER_FUCHSIA
227227

228+
static void SuppressFakeStack() {
229+
AsanThread* t = GetCurrentThread();
230+
if (t) {
231+
t->SuppressFakeStack();
232+
}
233+
}
234+
235+
static void UnsuppressFakeStack() {
236+
AsanThread* t = GetCurrentThread();
237+
if (t) {
238+
t->UnsuppressFakeStack();
239+
}
240+
}
241+
228242
static FakeStack* GetFakeStack() {
229243
AsanThread* t = GetCurrentThread();
230-
if (!t)
244+
if (!t || t->IsFakeStackSuppressed())
231245
return nullptr;
232246
return t->get_or_create_fake_stack();
233247
}
@@ -362,4 +376,9 @@ void __asan_allocas_unpoison(uptr top, uptr bottom) {
362376
REAL(memset)(reinterpret_cast<void*>(MemToShadow(top)), 0,
363377
(bottom - top) / ASAN_SHADOW_GRANULARITY);
364378
}
379+
380+
SANITIZER_INTERFACE_ATTRIBUTE
381+
void __asan_suppress_fake_stack() { return SuppressFakeStack(); }
382+
SANITIZER_INTERFACE_ATTRIBUTE
383+
void __asan_unsuppress_fake_stack() { return UnsuppressFakeStack(); }
365384
} // extern "C"

compiler-rt/lib/asan/asan_interface.inc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,12 +165,14 @@ INTERFACE_FUNCTION(__asan_store4_noabort)
165165
INTERFACE_FUNCTION(__asan_store8_noabort)
166166
INTERFACE_FUNCTION(__asan_store16_noabort)
167167
INTERFACE_FUNCTION(__asan_storeN_noabort)
168+
INTERFACE_FUNCTION(__asan_suppress_fake_stack)
168169
INTERFACE_FUNCTION(__asan_unpoison_intra_object_redzone)
169170
INTERFACE_FUNCTION(__asan_unpoison_memory_region)
170171
INTERFACE_FUNCTION(__asan_unpoison_stack_memory)
171172
INTERFACE_FUNCTION(__asan_unregister_globals)
172173
INTERFACE_FUNCTION(__asan_unregister_elf_globals)
173174
INTERFACE_FUNCTION(__asan_unregister_image_globals)
175+
INTERFACE_FUNCTION(__asan_unsuppress_fake_stack)
174176
INTERFACE_FUNCTION(__asan_version_mismatch_check_v8)
175177
INTERFACE_FUNCTION(__sanitizer_finish_switch_fiber)
176178
INTERFACE_FUNCTION(__sanitizer_print_stack_trace)

compiler-rt/lib/asan/asan_thread.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ FakeStack *AsanThread::AsyncSignalSafeLazyInitFakeStack() {
251251
void AsanThread::Init(const InitOptions *options) {
252252
DCHECK_NE(tid(), kInvalidTid);
253253
next_stack_top_ = next_stack_bottom_ = 0;
254+
fake_stack_suppression_counter_ = 0;
254255
atomic_store(&stack_switching_, false, memory_order_release);
255256
CHECK_EQ(this->stack_size(), 0U);
256257
SetThreadStackAndTls(options);
@@ -404,6 +405,19 @@ bool AsanThread::AddrIsInStack(uptr addr) {
404405
return addr >= bounds.bottom && addr < bounds.top;
405406
}
406407

408+
void AsanThread::SuppressFakeStack() {
409+
++fake_stack_suppression_counter_;
410+
ResetTLSFakeStack();
411+
}
412+
413+
void AsanThread::UnsuppressFakeStack() {
414+
if (fake_stack_suppression_counter_ == 0) {
415+
Report("ERROR: Unmatched call to __asan_unsuppress_fake_stack().\n");
416+
Die();
417+
}
418+
--fake_stack_suppression_counter_;
419+
}
420+
407421
static bool ThreadStackContainsAddress(ThreadContextBase *tctx_base,
408422
void *addr) {
409423
AsanThreadContext *tctx = static_cast<AsanThreadContext *>(tctx_base);

compiler-rt/lib/asan/asan_thread.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,12 @@ class AsanThread {
144144
GetStartData(&data, sizeof(data));
145145
}
146146

147+
bool IsFakeStackSuppressed() const {
148+
return fake_stack_suppression_counter_ > 0;
149+
}
150+
void SuppressFakeStack();
151+
void UnsuppressFakeStack();
152+
147153
private:
148154
// NOTE: There is no AsanThread constructor. It is allocated
149155
// via mmap() and *must* be valid in zero-initialized state.
@@ -179,6 +185,7 @@ class AsanThread {
179185
DTLS *dtls_;
180186

181187
FakeStack *fake_stack_;
188+
int fake_stack_suppression_counter_;
182189
AsanThreadLocalMallocStorage malloc_storage_;
183190
AsanStats stats_;
184191
bool unwinding_;

compiler-rt/lib/asan_abi/asan_abi.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ void *__asan_abi_addr_is_in_fake_stack(void *fake_stack, void *addr, void **beg,
7373
void **end) {
7474
return NULL;
7575
}
76+
void __asan_abi_suppress_fake_stack(void) {}
77+
void __asan_abi_unsuppress_fake_stack(void) {}
7678

7779
// Functions concerning poisoning and unpoisoning fake stack alloca
7880
void __asan_abi_alloca_poison(void *addr, size_t size) {}

compiler-rt/lib/asan_abi/asan_abi.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ void *__asan_abi_load_cxx_array_cookie(void **p);
7676
void *__asan_abi_get_current_fake_stack();
7777
void *__asan_abi_addr_is_in_fake_stack(void *fake_stack, void *addr, void **beg,
7878
void **end);
79+
void *__asan_abi_suppress_fake_stack();
80+
void *__asan_abi_unsuppress_fake_stack();
81+
7982
// Functions concerning poisoning and unpoisoning fake stack alloca
8083
void __asan_abi_alloca_poison(void *addr, size_t size);
8184
void __asan_abi_allocas_unpoison(void *top, void *bottom);

compiler-rt/lib/asan_abi/asan_abi_shim.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,12 @@ void *__asan_addr_is_in_fake_stack(void *fake_stack, void *addr, void **beg,
365365
void **end) {
366366
return __asan_abi_addr_is_in_fake_stack(fake_stack, addr, beg, end);
367367
}
368+
void __asan_suppress_fake_stack(void) {
369+
return __asan_abi_suppress_fake_stack();
370+
}
371+
void __asan_unsuppress_fake_stack(void) {
372+
return __asan_abi_unsuppress_fake_stack();
373+
}
368374

369375
// Functions concerning poisoning and unpoisoning fake stack alloca
370376
void __asan_alloca_poison(uptr addr, uptr size) {
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// RUN: %clangxx_asan %s -o %t && env ASAN_OPTIONS=detect_stack_use_after_return=1 %run %t
2+
// RUN: %clangxx_asan %s -mllvm -asan-use-after-return=runtime -o %t && env ASAN_OPTIONS=detect_stack_use_after_return=1 %run %t
3+
// RUN: %clangxx_asan %s -mllvm -asan-use-after-return=always -o %t && %run %t
4+
5+
#include "defines.h"
6+
7+
#include <cassert>
8+
#include <sanitizer/asan_interface.h>
9+
10+
volatile uintptr_t saved;
11+
12+
ATTRIBUTE_NOINLINE bool IsOnRealStack(uintptr_t parent_frame,
13+
uintptr_t var_addr) {
14+
uintptr_t this_frame =
15+
reinterpret_cast<uintptr_t>(__builtin_frame_address(0));
16+
return this_frame <= var_addr && var_addr <= parent_frame;
17+
}
18+
19+
ATTRIBUTE_NOINLINE bool IsOnRealStack(uintptr_t parent_frame) {
20+
volatile char temp = ' ';
21+
saved = reinterpret_cast<uintptr_t>(&temp);
22+
return IsOnRealStack(parent_frame, saved);
23+
}
24+
25+
ATTRIBUTE_NOINLINE bool IsOnRealStack() {
26+
return IsOnRealStack(reinterpret_cast<uintptr_t>(__builtin_frame_address(0)));
27+
}
28+
29+
int main(int argc, char *argv[]) {
30+
assert(!IsOnRealStack());
31+
32+
__asan_suppress_fake_stack();
33+
assert(IsOnRealStack());
34+
35+
__asan_unsuppress_fake_stack();
36+
assert(!IsOnRealStack());
37+
38+
return 0;
39+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Check unsuppressing fake stack does not reenable it if disabled via compile or runtime options.
2+
//
3+
// RUN: %clangxx_asan %s -mllvm -asan-use-after-return=never -o %t && %run %t
4+
// RUN: %clangxx_asan %s -mllvm -asan-use-after-return=runtime -o %t && env ASAN_OPTIONS=detect_stack_use_after_return=0 %run %t
5+
6+
#include "defines.h"
7+
8+
#include <cassert>
9+
#include <sanitizer/asan_interface.h>
10+
11+
volatile uintptr_t saved;
12+
13+
ATTRIBUTE_NOINLINE bool IsOnRealStack(uintptr_t parent_frame,
14+
uintptr_t var_addr) {
15+
uintptr_t this_frame =
16+
reinterpret_cast<uintptr_t>(__builtin_frame_address(0));
17+
return this_frame <= var_addr && var_addr <= parent_frame;
18+
}
19+
20+
ATTRIBUTE_NOINLINE bool IsOnRealStack(uintptr_t parent_frame) {
21+
volatile char temp = ' ';
22+
saved = reinterpret_cast<uintptr_t>(&temp);
23+
return IsOnRealStack(parent_frame, saved);
24+
}
25+
26+
ATTRIBUTE_NOINLINE bool IsOnRealStack() {
27+
return IsOnRealStack(reinterpret_cast<uintptr_t>(__builtin_frame_address(0)));
28+
}
29+
30+
int main(int argc, char *argv[]) {
31+
assert(IsOnRealStack());
32+
33+
__asan_suppress_fake_stack();
34+
assert(IsOnRealStack());
35+
36+
__asan_unsuppress_fake_stack();
37+
assert(IsOnRealStack());
38+
39+
return 0;
40+
}

0 commit comments

Comments
 (0)