From 646ec67e252f7daa5fe1f5d7719f247c89293b0c Mon Sep 17 00:00:00 2001 From: Alexey Samsonov Date: Fri, 24 May 2013 11:46:56 +0000 Subject: Disable init-order checking before destructors are run. We don't want to report initialization-order bugs when a destructor of a global variable accesses dynamically initialized global from another (not necessarily initialized) module. We do this by intercepting __cxa_atexit and registrering our own callback that unpoisons shadow for all dynamically initialized global variables. llvm-svn: 182637 --- compiler-rt/lib/asan/asan_globals.cc | 15 +++++++++++ compiler-rt/lib/asan/asan_intercepted_functions.h | 6 +++++ compiler-rt/lib/asan/asan_interceptors.cc | 20 ++++++++++++++ compiler-rt/lib/asan/asan_internal.h | 1 + .../lit_tests/Helpers/init-order-atexit-extra.cc | 16 +++++++++++ .../lib/asan/lit_tests/init-order-atexit.cc | 31 ++++++++++++++++++++++ 6 files changed, 89 insertions(+) create mode 100644 compiler-rt/lib/asan/lit_tests/Helpers/init-order-atexit-extra.cc create mode 100644 compiler-rt/lib/asan/lit_tests/init-order-atexit.cc (limited to 'compiler-rt/lib/asan') diff --git a/compiler-rt/lib/asan/asan_globals.cc b/compiler-rt/lib/asan/asan_globals.cc index 972afe6505e..301ea44f2ca 100644 --- a/compiler-rt/lib/asan/asan_globals.cc +++ b/compiler-rt/lib/asan/asan_globals.cc @@ -123,6 +123,21 @@ static void UnregisterGlobal(const Global *g) { // implementation. It might not be worth doing anyway. } +void StopInitOrderChecking() { + BlockingMutexLock lock(&mu_for_globals); + if (!flags()->check_initialization_order || !dynamic_init_globals) + return; + flags()->check_initialization_order = false; + for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) { + DynInitGlobal &dyn_g = (*dynamic_init_globals)[i]; + const Global *g = &dyn_g.g; + // Unpoison the whole global. + PoisonShadowForGlobal(g, 0); + // Poison redzones back. + PoisonRedZones(*g); + } +} + } // namespace __asan // ---------------------- Interface ---------------- {{{1 diff --git a/compiler-rt/lib/asan/asan_intercepted_functions.h b/compiler-rt/lib/asan/asan_intercepted_functions.h index 676a842a3e7..842781cdb17 100644 --- a/compiler-rt/lib/asan/asan_intercepted_functions.h +++ b/compiler-rt/lib/asan/asan_intercepted_functions.h @@ -77,6 +77,12 @@ using __sanitizer::uptr; # define ASAN_INTERCEPT___CXA_THROW 0 #endif +#if !SANITIZER_WINDOWS +# define ASAN_INTERCEPT___CXA_ATEXIT 1 +#else +# define ASAN_INTERCEPT___CXA_ATEXIT 0 +#endif + # if SANITIZER_WINDOWS extern "C" { // Windows threads. diff --git a/compiler-rt/lib/asan/asan_interceptors.cc b/compiler-rt/lib/asan/asan_interceptors.cc index 0c467b36ca9..7e7deea2963 100644 --- a/compiler-rt/lib/asan/asan_interceptors.cc +++ b/compiler-rt/lib/asan/asan_interceptors.cc @@ -636,6 +636,21 @@ INTERCEPTOR(long long, atoll, const char *nptr) { // NOLINT } #endif // ASAN_INTERCEPT_ATOLL_AND_STRTOLL +static void AtCxaAtexit(void *unused) { + (void)unused; + StopInitOrderChecking(); +} + +#if ASAN_INTERCEPT___CXA_ATEXIT +INTERCEPTOR(int, __cxa_atexit, void (*func)(void *), void *arg, + void *dso_handle) { + ENSURE_ASAN_INITED(); + int res = REAL(__cxa_atexit)(func, arg, dso_handle); + REAL(__cxa_atexit)(AtCxaAtexit, 0, 0); + return res; +} +#endif // ASAN_INTERCEPT___CXA_ATEXIT + #define ASAN_INTERCEPT_FUNC(name) do { \ if (!INTERCEPT_FUNCTION(name) && flags()->verbosity > 0) \ Report("AddressSanitizer: failed to intercept '" #name "'\n"); \ @@ -746,6 +761,11 @@ void InitializeAsanInterceptors() { ASAN_INTERCEPT_FUNC(pthread_create); #endif + // Intercept atexit function. +#if ASAN_INTERCEPT___CXA_ATEXIT + ASAN_INTERCEPT_FUNC(__cxa_atexit); +#endif + // Some Windows-specific interceptors. #if SANITIZER_WINDOWS InitializeWindowsInterceptors(); diff --git a/compiler-rt/lib/asan/asan_internal.h b/compiler-rt/lib/asan/asan_internal.h index b79acf31c17..7a4d74472bc 100644 --- a/compiler-rt/lib/asan/asan_internal.h +++ b/compiler-rt/lib/asan/asan_internal.h @@ -92,6 +92,7 @@ void UnsetAlternateSignalStack(); void InstallSignalHandlers(); void ReadContextStack(void *context, uptr *stack, uptr *ssize); void AsanPlatformThreadInit(); +void StopInitOrderChecking(); // Wrapper for TLS/TSD. void AsanTSDInit(void (*destructor)(void *tsd)); diff --git a/compiler-rt/lib/asan/lit_tests/Helpers/init-order-atexit-extra.cc b/compiler-rt/lib/asan/lit_tests/Helpers/init-order-atexit-extra.cc new file mode 100644 index 00000000000..e4189d19d09 --- /dev/null +++ b/compiler-rt/lib/asan/lit_tests/Helpers/init-order-atexit-extra.cc @@ -0,0 +1,16 @@ +#include + +class C { + public: + C() { value = 42; } + ~C() { } + int value; +}; + +C c; + +void AccessC() { + printf("C value: %d\n", c.value); +} + +int main() { return 0; } diff --git a/compiler-rt/lib/asan/lit_tests/init-order-atexit.cc b/compiler-rt/lib/asan/lit_tests/init-order-atexit.cc new file mode 100644 index 00000000000..45f4f17c0cb --- /dev/null +++ b/compiler-rt/lib/asan/lit_tests/init-order-atexit.cc @@ -0,0 +1,31 @@ +// Test for the following situation: +// (1) global A is constructed. +// (2) exit() is called during construction of global B. +// (3) destructor of A reads uninitialized global C from another module. +// We do *not* want to report init-order bug in this case. + +// RUN: %clangxx_asan -m64 -O0 %s %p/Helpers/init-order-atexit-extra.cc -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true:strict_init_order=true %t 2>&1 | FileCheck %s + +#include +#include + +void AccessC(); + +class A { + public: + A() { } + ~A() { AccessC(); printf("PASSED\n"); } + // CHECK-NOT: AddressSanitizer + // CHECK: PASSED +}; + +A a; + +class B { + public: + B() { exit(1); } + ~B() { } +}; + +B b; -- cgit v1.2.1