diff --git a/compiler-rt/include/sanitizer/asan_interface.h b/compiler-rt/include/sanitizer/asan_interface.h index 37b6d08f4db19..544f2e4b32687 100644 --- a/compiler-rt/include/sanitizer/asan_interface.h +++ b/compiler-rt/include/sanitizer/asan_interface.h @@ -333,6 +333,14 @@ void SANITIZER_CDECL __asan_handle_no_return(void); /// trace. Returns 1 if successful, 0 if not. int SANITIZER_CDECL __asan_update_allocation_context(void *addr); +/// Suppresses fake stack for the current thread. +/// Temporarily disables use-after-return detection for current thread. +void SANITIZER_CDECL __asan_suppress_fake_stack(void); + +/// Unsupresses fake stack for the current thread. +/// Should be paired with a previous __asan_suppress_fake_stack() call. +void SANITIZER_CDECL __asan_unsuppress_fake_stack(void); + #ifdef __cplusplus } // extern "C" #endif diff --git a/compiler-rt/lib/asan/asan_fake_stack.cpp b/compiler-rt/lib/asan/asan_fake_stack.cpp index d3fa953f31005..af73d31b8a5dc 100644 --- a/compiler-rt/lib/asan/asan_fake_stack.cpp +++ b/compiler-rt/lib/asan/asan_fake_stack.cpp @@ -225,9 +225,23 @@ static void SetTLSFakeStack(FakeStack*) {} void ResetTLSFakeStack() {} #endif // (SANITIZER_LINUX && !SANITIZER_ANDROID) || SANITIZER_FUCHSIA +static void SuppressFakeStack() { + AsanThread* t = GetCurrentThread(); + if (t) { + t->SuppressFakeStack(); + } +} + +static void UnsuppressFakeStack() { + AsanThread* t = GetCurrentThread(); + if (t) { + t->UnsuppressFakeStack(); + } +} + static FakeStack* GetFakeStack() { AsanThread* t = GetCurrentThread(); - if (!t) + if (!t || t->IsFakeStackSuppressed()) return nullptr; return t->get_or_create_fake_stack(); } @@ -362,4 +376,9 @@ void __asan_allocas_unpoison(uptr top, uptr bottom) { REAL(memset)(reinterpret_cast(MemToShadow(top)), 0, (bottom - top) / ASAN_SHADOW_GRANULARITY); } + +SANITIZER_INTERFACE_ATTRIBUTE +void __asan_suppress_fake_stack() { return SuppressFakeStack(); } +SANITIZER_INTERFACE_ATTRIBUTE +void __asan_unsuppress_fake_stack() { return UnsuppressFakeStack(); } } // extern "C" diff --git a/compiler-rt/lib/asan/asan_interface.inc b/compiler-rt/lib/asan/asan_interface.inc index bfc44b4619623..b465356b1e443 100644 --- a/compiler-rt/lib/asan/asan_interface.inc +++ b/compiler-rt/lib/asan/asan_interface.inc @@ -165,12 +165,14 @@ INTERFACE_FUNCTION(__asan_store4_noabort) INTERFACE_FUNCTION(__asan_store8_noabort) INTERFACE_FUNCTION(__asan_store16_noabort) INTERFACE_FUNCTION(__asan_storeN_noabort) +INTERFACE_FUNCTION(__asan_suppress_fake_stack) INTERFACE_FUNCTION(__asan_unpoison_intra_object_redzone) INTERFACE_FUNCTION(__asan_unpoison_memory_region) INTERFACE_FUNCTION(__asan_unpoison_stack_memory) INTERFACE_FUNCTION(__asan_unregister_globals) INTERFACE_FUNCTION(__asan_unregister_elf_globals) INTERFACE_FUNCTION(__asan_unregister_image_globals) +INTERFACE_FUNCTION(__asan_unsuppress_fake_stack) INTERFACE_FUNCTION(__asan_version_mismatch_check_v8) INTERFACE_FUNCTION(__sanitizer_finish_switch_fiber) INTERFACE_FUNCTION(__sanitizer_print_stack_trace) diff --git a/compiler-rt/lib/asan/asan_thread.cpp b/compiler-rt/lib/asan/asan_thread.cpp index 0ed58bbe2a73a..32ab723e89001 100644 --- a/compiler-rt/lib/asan/asan_thread.cpp +++ b/compiler-rt/lib/asan/asan_thread.cpp @@ -251,6 +251,7 @@ FakeStack *AsanThread::AsyncSignalSafeLazyInitFakeStack() { void AsanThread::Init(const InitOptions *options) { DCHECK_NE(tid(), kInvalidTid); next_stack_top_ = next_stack_bottom_ = 0; + fake_stack_suppression_counter_ = 0; atomic_store(&stack_switching_, false, memory_order_release); CHECK_EQ(this->stack_size(), 0U); SetThreadStackAndTls(options); @@ -404,6 +405,19 @@ bool AsanThread::AddrIsInStack(uptr addr) { return addr >= bounds.bottom && addr < bounds.top; } +void AsanThread::SuppressFakeStack() { + ++fake_stack_suppression_counter_; + ResetTLSFakeStack(); +} + +void AsanThread::UnsuppressFakeStack() { + if (fake_stack_suppression_counter_ == 0) { + Report("ERROR: Unmatched call to __asan_unsuppress_fake_stack().\n"); + Die(); + } + --fake_stack_suppression_counter_; +} + static bool ThreadStackContainsAddress(ThreadContextBase *tctx_base, void *addr) { AsanThreadContext *tctx = static_cast(tctx_base); diff --git a/compiler-rt/lib/asan/asan_thread.h b/compiler-rt/lib/asan/asan_thread.h index 19b7f342e1712..e9ca6b6a59016 100644 --- a/compiler-rt/lib/asan/asan_thread.h +++ b/compiler-rt/lib/asan/asan_thread.h @@ -144,6 +144,12 @@ class AsanThread { GetStartData(&data, sizeof(data)); } + bool IsFakeStackSuppressed() const { + return fake_stack_suppression_counter_ > 0; + } + void SuppressFakeStack(); + void UnsuppressFakeStack(); + private: // NOTE: There is no AsanThread constructor. It is allocated // via mmap() and *must* be valid in zero-initialized state. @@ -179,6 +185,7 @@ class AsanThread { DTLS *dtls_; FakeStack *fake_stack_; + int fake_stack_suppression_counter_; AsanThreadLocalMallocStorage malloc_storage_; AsanStats stats_; bool unwinding_; diff --git a/compiler-rt/lib/asan_abi/asan_abi.cpp b/compiler-rt/lib/asan_abi/asan_abi.cpp index cf8663024eb73..6199bbed09dfc 100644 --- a/compiler-rt/lib/asan_abi/asan_abi.cpp +++ b/compiler-rt/lib/asan_abi/asan_abi.cpp @@ -73,6 +73,8 @@ void *__asan_abi_addr_is_in_fake_stack(void *fake_stack, void *addr, void **beg, void **end) { return NULL; } +void __asan_abi_suppress_fake_stack(void) {} +void __asan_abi_unsuppress_fake_stack(void) {} // Functions concerning poisoning and unpoisoning fake stack alloca void __asan_abi_alloca_poison(void *addr, size_t size) {} diff --git a/compiler-rt/lib/asan_abi/asan_abi.h b/compiler-rt/lib/asan_abi/asan_abi.h index 8702bcd133919..cb59976c8506c 100644 --- a/compiler-rt/lib/asan_abi/asan_abi.h +++ b/compiler-rt/lib/asan_abi/asan_abi.h @@ -76,6 +76,9 @@ void *__asan_abi_load_cxx_array_cookie(void **p); void *__asan_abi_get_current_fake_stack(); void *__asan_abi_addr_is_in_fake_stack(void *fake_stack, void *addr, void **beg, void **end); +void *__asan_abi_suppress_fake_stack(); +void *__asan_abi_unsuppress_fake_stack(); + // Functions concerning poisoning and unpoisoning fake stack alloca void __asan_abi_alloca_poison(void *addr, size_t size); void __asan_abi_allocas_unpoison(void *top, void *bottom); diff --git a/compiler-rt/lib/asan_abi/asan_abi_shim.cpp b/compiler-rt/lib/asan_abi/asan_abi_shim.cpp index 2512abc641250..7ae7e77aea4a2 100644 --- a/compiler-rt/lib/asan_abi/asan_abi_shim.cpp +++ b/compiler-rt/lib/asan_abi/asan_abi_shim.cpp @@ -365,6 +365,12 @@ void *__asan_addr_is_in_fake_stack(void *fake_stack, void *addr, void **beg, void **end) { return __asan_abi_addr_is_in_fake_stack(fake_stack, addr, beg, end); } +void __asan_suppress_fake_stack(void) { + return __asan_abi_suppress_fake_stack(); +} +void __asan_unsuppress_fake_stack(void) { + return __asan_abi_unsuppress_fake_stack(); +} // Functions concerning poisoning and unpoisoning fake stack alloca void __asan_alloca_poison(uptr addr, uptr size) { diff --git a/compiler-rt/test/asan/TestCases/suppress_fake_stack.cpp b/compiler-rt/test/asan/TestCases/suppress_fake_stack.cpp new file mode 100644 index 0000000000000..f072c6ad3b034 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/suppress_fake_stack.cpp @@ -0,0 +1,39 @@ +// RUN: %clangxx_asan %s -o %t && env ASAN_OPTIONS=detect_stack_use_after_return=1 %run %t +// RUN: %clangxx_asan %s -mllvm -asan-use-after-return=runtime -o %t && env ASAN_OPTIONS=detect_stack_use_after_return=1 %run %t +// RUN: %clangxx_asan %s -mllvm -asan-use-after-return=always -o %t && %run %t + +#include "defines.h" + +#include +#include + +volatile uintptr_t saved; + +ATTRIBUTE_NOINLINE bool IsOnRealStack(uintptr_t parent_frame, + uintptr_t var_addr) { + uintptr_t this_frame = + reinterpret_cast(__builtin_frame_address(0)); + return this_frame <= var_addr && var_addr <= parent_frame; +} + +ATTRIBUTE_NOINLINE bool IsOnRealStack(uintptr_t parent_frame) { + volatile char temp = ' '; + saved = reinterpret_cast(&temp); + return IsOnRealStack(parent_frame, saved); +} + +ATTRIBUTE_NOINLINE bool IsOnRealStack() { + return IsOnRealStack(reinterpret_cast(__builtin_frame_address(0))); +} + +int main(int argc, char *argv[]) { + assert(!IsOnRealStack()); + + __asan_suppress_fake_stack(); + assert(IsOnRealStack()); + + __asan_unsuppress_fake_stack(); + assert(!IsOnRealStack()); + + return 0; +} diff --git a/compiler-rt/test/asan/TestCases/suppress_fake_stack_force_disabled.cpp b/compiler-rt/test/asan/TestCases/suppress_fake_stack_force_disabled.cpp new file mode 100644 index 0000000000000..c549f08a7f0a8 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/suppress_fake_stack_force_disabled.cpp @@ -0,0 +1,40 @@ +// Check unsuppressing fake stack does not reenable it if disabled via compile or runtime options. +// +// RUN: %clangxx_asan %s -mllvm -asan-use-after-return=never -o %t && %run %t +// RUN: %clangxx_asan %s -mllvm -asan-use-after-return=runtime -o %t && env ASAN_OPTIONS=detect_stack_use_after_return=0 %run %t + +#include "defines.h" + +#include +#include + +volatile uintptr_t saved; + +ATTRIBUTE_NOINLINE bool IsOnRealStack(uintptr_t parent_frame, + uintptr_t var_addr) { + uintptr_t this_frame = + reinterpret_cast(__builtin_frame_address(0)); + return this_frame <= var_addr && var_addr <= parent_frame; +} + +ATTRIBUTE_NOINLINE bool IsOnRealStack(uintptr_t parent_frame) { + volatile char temp = ' '; + saved = reinterpret_cast(&temp); + return IsOnRealStack(parent_frame, saved); +} + +ATTRIBUTE_NOINLINE bool IsOnRealStack() { + return IsOnRealStack(reinterpret_cast(__builtin_frame_address(0))); +} + +int main(int argc, char *argv[]) { + assert(IsOnRealStack()); + + __asan_suppress_fake_stack(); + assert(IsOnRealStack()); + + __asan_unsuppress_fake_stack(); + assert(IsOnRealStack()); + + return 0; +}