diff options
| author | Kostya Serebryany <kcc@google.com> | 2012-08-21 14:10:25 +0000 |
|---|---|---|
| committer | Kostya Serebryany <kcc@google.com> | 2012-08-21 14:10:25 +0000 |
| commit | 676bcdb8383ea20d05bf06e71523590c14b7f6a3 (patch) | |
| tree | 1b2ed99ae3cdbbb729d6c890a95feceb2ee05473 | |
| parent | 68256859ff14d122870de9dbe67a99c6b8fb795e (diff) | |
| download | bcm5719-llvm-676bcdb8383ea20d05bf06e71523590c14b7f6a3.tar.gz bcm5719-llvm-676bcdb8383ea20d05bf06e71523590c14b7f6a3.zip | |
[asan] run-time part of the initialization order checker. Patch by Reid Watson with some bits from kcc. The sub-pass is off by default for now. On simple tests it works fine.
llvm-svn: 162278
| -rw-r--r-- | compiler-rt/lib/asan/asan_flags.h | 2 | ||||
| -rw-r--r-- | compiler-rt/lib/asan/asan_globals.cc | 81 | ||||
| -rw-r--r-- | compiler-rt/lib/asan/asan_interface.h | 8 | ||||
| -rw-r--r-- | compiler-rt/lib/asan/asan_internal.h | 1 | ||||
| -rw-r--r-- | compiler-rt/lib/asan/asan_report.cc | 3 | ||||
| -rw-r--r-- | compiler-rt/lib/asan/asan_rtl.cc | 2 |
6 files changed, 89 insertions, 8 deletions
diff --git a/compiler-rt/lib/asan/asan_flags.h b/compiler-rt/lib/asan/asan_flags.h index e259f11d63b..d58a833a8bd 100644 --- a/compiler-rt/lib/asan/asan_flags.h +++ b/compiler-rt/lib/asan/asan_flags.h @@ -43,6 +43,8 @@ struct Flags { // on globals, 1 - detect buffer overflow, 2 - print data about registered // globals). int report_globals; + // If set, attempts to catch initialization order issues. + bool check_initialization_order; // Max number of stack frames kept for each allocation. int malloc_context_size; // If set, uses custom wrappers and replacements for libc string functions diff --git a/compiler-rt/lib/asan/asan_globals.cc b/compiler-rt/lib/asan/asan_globals.cc index 87e990960e2..ff403b5d57e 100644 --- a/compiler-rt/lib/asan/asan_globals.cc +++ b/compiler-rt/lib/asan/asan_globals.cc @@ -31,8 +31,9 @@ struct ListOfGlobals { }; static AsanLock mu_for_globals(LINKER_INITIALIZED); -static ListOfGlobals *list_of_globals; static LowLevelAllocator allocator_for_globals(LINKER_INITIALIZED); +static ListOfGlobals *list_of_all_globals; +static ListOfGlobals *list_of_dynamic_init_globals; void PoisonRedZones(const Global &g) { uptr shadow_rz_size = kGlobalAndStackRedzone >> SHADOW_SCALE; @@ -63,7 +64,7 @@ bool DescribeAddressIfGlobal(uptr addr) { if (!flags()->report_globals) return false; ScopedLock lock(&mu_for_globals); bool res = false; - for (ListOfGlobals *l = list_of_globals; l; l = l->next) { + for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) { const Global &g = *l->g; if (flags()->report_globals >= 2) Report("Search Global: beg=%p size=%zu name=%s\n", @@ -78,6 +79,10 @@ bool DescribeAddressIfGlobal(uptr addr) { // so we store the globals in a map. static void RegisterGlobal(const Global *g) { CHECK(asan_inited); + if (flags()->report_globals >= 2) + Report("Added Global: beg=%p size=%zu/%zu name=%s dyn.init=%zu\n", + (void*)g->beg, g->size, g->size_with_redzone, g->name, + g->has_dynamic_init); CHECK(flags()->report_globals); CHECK(AddrIsInMem(g->beg)); CHECK(AddrIsAlignedByGranularity(g->beg)); @@ -86,11 +91,14 @@ static void RegisterGlobal(const Global *g) { ListOfGlobals *l = (ListOfGlobals*)allocator_for_globals.Allocate(sizeof(ListOfGlobals)); l->g = g; - l->next = list_of_globals; - list_of_globals = l; - if (flags()->report_globals >= 2) - Report("Added Global: beg=%p size=%zu name=%s\n", - (void*)g->beg, g->size, g->name); + l->next = list_of_all_globals; + list_of_all_globals = l; + if (g->has_dynamic_init) { + l = (ListOfGlobals*)allocator_for_globals.Allocate(sizeof(ListOfGlobals)); + l->g = g; + l->next = list_of_dynamic_init_globals; + list_of_dynamic_init_globals = l; + } } static void UnregisterGlobal(const Global *g) { @@ -105,6 +113,30 @@ static void UnregisterGlobal(const Global *g) { // implementation. It might not be worth doing anyway. } +// Poison all shadow memory for a single global. +static void PoisonGlobalAndRedzones(const Global *g) { + CHECK(asan_inited); + CHECK(flags()->check_initialization_order); + CHECK(AddrIsInMem(g->beg)); + CHECK(AddrIsAlignedByGranularity(g->beg)); + CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); + if (flags()->report_globals >= 3) + Printf("DynInitPoison : %s\n", g->name); + PoisonShadow(g->beg, g->size_with_redzone, kAsanInitializationOrderMagic); +} + +static void UnpoisonGlobal(const Global *g) { + CHECK(asan_inited); + CHECK(flags()->check_initialization_order); + CHECK(AddrIsInMem(g->beg)); + CHECK(AddrIsAlignedByGranularity(g->beg)); + CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); + if (flags()->report_globals >= 3) + Printf("DynInitUnpoison: %s\n", g->name); + PoisonShadow(g->beg, g->size_with_redzone, 0); + PoisonRedZones(*g); +} + } // namespace __asan // ---------------------- Interface ---------------- {{{1 @@ -133,7 +165,7 @@ void __asan_register_globals(__asan_global *globals, uptr n) { } // Unregister an array of globals. -// We must do it when a shared objects gets dlclosed. +// We must do this when a shared objects gets dlclosed. void __asan_unregister_globals(__asan_global *globals, uptr n) { if (!flags()->report_globals) return; ScopedLock lock(&mu_for_globals); @@ -141,3 +173,36 @@ void __asan_unregister_globals(__asan_global *globals, uptr n) { UnregisterGlobal(&globals[i]); } } + +// This method runs immediately prior to dynamic initialization in each TU, +// when all dynamically initialized globals are unpoisoned. This method +// poisons all global variables not defined in this TU, so that a dynamic +// initializer can only touch global variables in the same TU. +void __asan_before_dynamic_init(uptr first_addr, uptr last_addr) { + if (!flags()->check_initialization_order) return; + CHECK(list_of_dynamic_init_globals); + ScopedLock lock(&mu_for_globals); + bool from_current_tu = false; + // The list looks like: + // a => ... => b => last_addr => ... => first_addr => c => ... + // The globals of the current TU reside between last_addr and first_addr. + for (ListOfGlobals *l = list_of_dynamic_init_globals; l; l = l->next) { + if (l->g->beg == last_addr) + from_current_tu = true; + if (!from_current_tu) + PoisonGlobalAndRedzones(l->g); + if (l->g->beg == first_addr) + from_current_tu = false; + } + CHECK(!from_current_tu); +} + +// This method runs immediately after dynamic initialization in each TU, when +// all dynamically initialized globals except for those defined in the current +// TU are poisoned. It simply unpoisons all dynamically initialized globals. +void __asan_after_dynamic_init() { + if (!flags()->check_initialization_order) return; + ScopedLock lock(&mu_for_globals); + for (ListOfGlobals *l = list_of_dynamic_init_globals; l; l = l->next) + UnpoisonGlobal(l->g); +} diff --git a/compiler-rt/lib/asan/asan_interface.h b/compiler-rt/lib/asan/asan_interface.h index 40ce3cbbbae..c19dc76b678 100644 --- a/compiler-rt/lib/asan/asan_interface.h +++ b/compiler-rt/lib/asan/asan_interface.h @@ -48,6 +48,14 @@ extern "C" { void __asan_unregister_globals(__asan_global *globals, uptr n) SANITIZER_INTERFACE_ATTRIBUTE; + // These two functions should be called before and after dynamic initializers + // run, respectively. They should be called with parameters describing all + // dynamically initialized globals defined in the calling TU. + void __asan_before_dynamic_init(uptr first_addr, uptr last_addr) + SANITIZER_INTERFACE_ATTRIBUTE; + void __asan_after_dynamic_init() + SANITIZER_INTERFACE_ATTRIBUTE; + // These two functions are used by the instrumented code in the // use-after-return mode. __asan_stack_malloc allocates size bytes of // fake stack and __asan_stack_free poisons it. real_stack is a pointer to diff --git a/compiler-rt/lib/asan/asan_internal.h b/compiler-rt/lib/asan/asan_internal.h index 6f6bb840382..d9f88667a3f 100644 --- a/compiler-rt/lib/asan/asan_internal.h +++ b/compiler-rt/lib/asan/asan_internal.h @@ -171,6 +171,7 @@ const int kAsanStackMidRedzoneMagic = 0xf2; const int kAsanStackRightRedzoneMagic = 0xf3; const int kAsanStackPartialRedzoneMagic = 0xf4; const int kAsanStackAfterReturnMagic = 0xf5; +const int kAsanInitializationOrderMagic = 0xf6; const int kAsanUserPoisonedMemoryMagic = 0xf7; const int kAsanGlobalRedzoneMagic = 0xf9; const int kAsanInternalHeapMagic = 0xfe; diff --git a/compiler-rt/lib/asan/asan_report.cc b/compiler-rt/lib/asan/asan_report.cc index 1cabbcdd91c..310cbe5e153 100644 --- a/compiler-rt/lib/asan/asan_report.cc +++ b/compiler-rt/lib/asan/asan_report.cc @@ -371,6 +371,9 @@ void __asan_report_error(uptr pc, uptr bp, uptr sp, case kAsanStackLeftRedzoneMagic: bug_descr = "stack-buffer-underflow"; break; + case kAsanInitializationOrderMagic: + bug_descr = "initialization-order-fiasco"; + break; case kAsanStackMidRedzoneMagic: case kAsanStackRightRedzoneMagic: case kAsanStackPartialRedzoneMagic: diff --git a/compiler-rt/lib/asan/asan_rtl.cc b/compiler-rt/lib/asan/asan_rtl.cc index 0f87253a4c4..14101b00705 100644 --- a/compiler-rt/lib/asan/asan_rtl.cc +++ b/compiler-rt/lib/asan/asan_rtl.cc @@ -79,6 +79,7 @@ static void ParseFlagsFromString(Flags *f, const char *str) { ParseFlag(str, &f->debug, "debug"); ParseFlag(str, &f->report_globals, "report_globals"); + ParseFlag(str, &f->check_initialization_order, "initialization_order"); ParseFlag(str, &f->malloc_context_size, "malloc_context_size"); CHECK(f->malloc_context_size <= kMallocContextSize); @@ -116,6 +117,7 @@ void InitializeFlags(Flags *f, const char *env) { f->redzone = (ASAN_LOW_MEMORY) ? 64 : 128; f->debug = false; f->report_globals = 1; + f->check_initialization_order = true; f->malloc_context_size = kMallocContextSize; f->replace_str = true; f->replace_intrin = true; |

