From dfeef67b23ba92bbee598293164ee20078f633a1 Mon Sep 17 00:00:00 2001 From: Alexey Samsonov Date: Fri, 19 Apr 2013 08:35:16 +0000 Subject: [ASan] Make init-order checker allow access to already initialized globals. This change adds ASan runtime option "strict-init-order" (off by default) that makes init-order checker bark if global initializer accesses any global from different translation unit (even if the latter is already initialized). strict init-order checking doesn't play well with, e.g. LLVM registration machineries, and causes issue https://code.google.com/p/address-sanitizer/issues/detail?id=178. git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@179843 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/asan/asan_flags.h | 3 ++ lib/asan/asan_globals.cc | 30 ++++++++++---- lib/asan/asan_rtl.cc | 2 + .../Linux/initialization-bug-any-order.cc | 7 ++-- .../lit_tests/SharedLibs/init-order-dlopen-so.cc | 12 ++++++ lib/asan/lit_tests/init-order-dlopen.cc | 47 ++++++++++++++++++++++ 6 files changed, 90 insertions(+), 11 deletions(-) create mode 100644 lib/asan/lit_tests/SharedLibs/init-order-dlopen-so.cc create mode 100644 lib/asan/lit_tests/init-order-dlopen.cc diff --git a/lib/asan/asan_flags.h b/lib/asan/asan_flags.h index 0fd8399cb..98909b3d7 100644 --- a/lib/asan/asan_flags.h +++ b/lib/asan/asan_flags.h @@ -113,6 +113,9 @@ struct Flags { // If true, assume that memcmp(p1, p2, n) always reads n bytes before // comparing p1 and p2. bool strict_memcmp; + // If true, assume that dynamic initializers can never access globals from + // other modules, even if the latter are already initialized. + bool strict_init_order; }; extern Flags asan_flags_dont_use_directly; diff --git a/lib/asan/asan_globals.cc b/lib/asan/asan_globals.cc index d459dcd53..972afe650 100644 --- a/lib/asan/asan_globals.cc +++ b/lib/asan/asan_globals.cc @@ -37,7 +37,11 @@ static LowLevelAllocator allocator_for_globals; static ListOfGlobals *list_of_all_globals; static const int kDynamicInitGlobalsInitialCapacity = 512; -typedef InternalVector VectorOfGlobals; +struct DynInitGlobal { + Global g; + bool initialized; +}; +typedef InternalVector VectorOfGlobals; // Lazy-initialized and never deleted. static VectorOfGlobals *dynamic_init_globals; @@ -101,7 +105,8 @@ static void RegisterGlobal(const Global *g) { dynamic_init_globals = new(mem) VectorOfGlobals(kDynamicInitGlobalsInitialCapacity); } - dynamic_init_globals->push_back(*g); + DynInitGlobal dyn_global = { *g, false }; + dynamic_init_globals->push_back(dyn_global); } } @@ -150,6 +155,7 @@ void __asan_before_dynamic_init(const char *module_name) { if (!flags()->check_initialization_order || !flags()->poison_heap) return; + bool strict_init_order = flags()->strict_init_order; CHECK(dynamic_init_globals); CHECK(module_name); CHECK(asan_inited); @@ -157,9 +163,14 @@ void __asan_before_dynamic_init(const char *module_name) { if (flags()->report_globals >= 3) Printf("DynInitPoison module: %s\n", module_name); for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) { - const Global *g = &(*dynamic_init_globals)[i]; + DynInitGlobal &dyn_g = (*dynamic_init_globals)[i]; + const Global *g = &dyn_g.g; + if (dyn_g.initialized) + continue; if (g->module_name != module_name) PoisonShadowForGlobal(g, kAsanInitializationOrderMagic); + else if (!strict_init_order) + dyn_g.initialized = true; } } @@ -174,10 +185,13 @@ void __asan_after_dynamic_init() { BlockingMutexLock lock(&mu_for_globals); // FIXME: Optionally report that we're unpoisoning globals from a module. for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) { - const Global *g = &(*dynamic_init_globals)[i]; - // Unpoison the whole global. - PoisonShadowForGlobal(g, 0); - // Poison redzones back. - PoisonRedZones(*g); + DynInitGlobal &dyn_g = (*dynamic_init_globals)[i]; + const Global *g = &dyn_g.g; + if (!dyn_g.initialized) { + // Unpoison the whole global. + PoisonShadowForGlobal(g, 0); + // Poison redzones back. + PoisonRedZones(*g); + } } } diff --git a/lib/asan/asan_rtl.cc b/lib/asan/asan_rtl.cc index 1b6a6cdae..d6f7bc6bd 100644 --- a/lib/asan/asan_rtl.cc +++ b/lib/asan/asan_rtl.cc @@ -125,6 +125,7 @@ static void ParseFlagsFromString(Flags *f, const char *str) { ParseFlag(str, &f->alloc_dealloc_mismatch, "alloc_dealloc_mismatch"); ParseFlag(str, &f->use_stack_depot, "use_stack_depot"); ParseFlag(str, &f->strict_memcmp, "strict_memcmp"); + ParseFlag(str, &f->strict_init_order, "strict_init_order"); } static const char *asan_external_symbolizer; @@ -170,6 +171,7 @@ void InitializeFlags(Flags *f, const char *env) { f->alloc_dealloc_mismatch = (SANITIZER_MAC == 0);; f->use_stack_depot = true; // Only affects allocator2. f->strict_memcmp = true; + f->strict_init_order = false; // Override from compile definition. ParseFlagsFromString(f, MaybeUseAsanDefaultOptionsCompileDefiniton()); diff --git a/lib/asan/lit_tests/Linux/initialization-bug-any-order.cc b/lib/asan/lit_tests/Linux/initialization-bug-any-order.cc index f054a8129..4f41dda18 100644 --- a/lib/asan/lit_tests/Linux/initialization-bug-any-order.cc +++ b/lib/asan/lit_tests/Linux/initialization-bug-any-order.cc @@ -1,12 +1,13 @@ // Test to make sure basic initialization order errors are caught. // Check that on Linux initialization order bugs are caught -// independently on order in which we list source files. +// independently on order in which we list source files (if we specify +// strict init-order checking). // RUN: %clangxx_asan -m64 -O0 %s %p/../Helpers/initialization-bug-extra.cc -o %t -// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 \ +// RUN: ASAN_OPTIONS=check_initialization_order=true:strict_init_order=true %t 2>&1 \ // RUN: | %symbolize | FileCheck %s // RUN: %clangxx_asan -m64 -O0 %p/../Helpers/initialization-bug-extra.cc %s -o %t -// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 \ +// RUN: ASAN_OPTIONS=check_initialization_order=true:strict_init_order=true %t 2>&1 \ // RUN: | %symbolize | FileCheck %s // Do not test with optimization -- the error may be optimized away. diff --git a/lib/asan/lit_tests/SharedLibs/init-order-dlopen-so.cc b/lib/asan/lit_tests/SharedLibs/init-order-dlopen-so.cc new file mode 100644 index 000000000..20ef2d8a0 --- /dev/null +++ b/lib/asan/lit_tests/SharedLibs/init-order-dlopen-so.cc @@ -0,0 +1,12 @@ +#include +#include + +void inc_global(); + +int slow_init() { + sleep(1); + inc_global(); + return 42; +} + +int slowly_init_glob = slow_init(); diff --git a/lib/asan/lit_tests/init-order-dlopen.cc b/lib/asan/lit_tests/init-order-dlopen.cc new file mode 100644 index 000000000..bfe4c9b55 --- /dev/null +++ b/lib/asan/lit_tests/init-order-dlopen.cc @@ -0,0 +1,47 @@ +// Regression test for +// https://code.google.com/p/address-sanitizer/issues/detail?id=178 + +// RUN: %clangxx_asan -m64 -O0 %p/SharedLibs/init-order-dlopen-so.cc \ +// RUN: -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -m64 -O0 %s -o %t -Wl,--export-dynamic +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 | FileCheck %s +#include +#include +#include + +#include + +using std::string; + +int foo() { + return 42; +} +int global = foo(); + +__attribute__((visibility("default"))) +void inc_global() { + global++; +} + +void *global_poller(void *arg) { + while (true) { + if (global != 42) + break; + usleep(100); + } + return 0; +} + +int main(int argc, char *argv[]) { + pthread_t p; + pthread_create(&p, 0, global_poller, 0); + string path = string(argv[0]) + "-so.so"; + if (0 == dlopen(path.c_str(), RTLD_NOW)) { + fprintf(stderr, "dlerror: %s\n", dlerror()); + return 1; + } + pthread_join(p, 0); + printf("PASSED\n"); + // CHECK: PASSED + return 0; +} -- cgit v1.2.1