| //===-- asan_globals.cc ---------------------------------------------------===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file is a part of AddressSanitizer, an address sanity checker. |
| // |
| // Handle globals. |
| //===----------------------------------------------------------------------===// |
| #include "asan_interceptors.h" |
| #include "asan_internal.h" |
| #include "asan_mapping.h" |
| #include "asan_report.h" |
| #include "asan_stack.h" |
| #include "asan_stats.h" |
| #include "asan_thread.h" |
| #include "sanitizer_common/sanitizer_mutex.h" |
| |
| namespace __asan { |
| |
| typedef __asan_global Global; |
| |
| struct ListOfGlobals { |
| const Global *g; |
| ListOfGlobals *next; |
| }; |
| |
| static BlockingMutex mu_for_globals(LINKER_INITIALIZED); |
| static LowLevelAllocator allocator_for_globals; |
| static ListOfGlobals *list_of_all_globals; |
| static ListOfGlobals *list_of_dynamic_init_globals; |
| |
| void PoisonRedZones(const Global &g) { |
| uptr aligned_size = RoundUpTo(g.size, SHADOW_GRANULARITY); |
| PoisonShadow(g.beg + aligned_size, g.size_with_redzone - aligned_size, |
| kAsanGlobalRedzoneMagic); |
| if (g.size != aligned_size) { |
| // partial right redzone |
| PoisonShadowPartialRightRedzone( |
| g.beg + RoundDownTo(g.size, SHADOW_GRANULARITY), |
| g.size % SHADOW_GRANULARITY, |
| SHADOW_GRANULARITY, |
| kAsanGlobalRedzoneMagic); |
| } |
| } |
| |
| bool DescribeAddressIfGlobal(uptr addr, uptr size) { |
| if (!flags()->report_globals) return false; |
| BlockingMutexLock lock(&mu_for_globals); |
| bool res = false; |
| 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", |
| (void*)g.beg, g.size, (char*)g.name); |
| res |= DescribeAddressRelativeToGlobal(addr, size, g); |
| } |
| return res; |
| } |
| |
| // Register a global variable. |
| // This function may be called more than once for every global |
| // 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)); |
| CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); |
| PoisonRedZones(*g); |
| ListOfGlobals *l = |
| (ListOfGlobals*)allocator_for_globals.Allocate(sizeof(ListOfGlobals)); |
| l->g = g; |
| 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) { |
| CHECK(asan_inited); |
| CHECK(flags()->report_globals); |
| CHECK(AddrIsInMem(g->beg)); |
| CHECK(AddrIsAlignedByGranularity(g->beg)); |
| CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); |
| PoisonShadow(g->beg, g->size_with_redzone, 0); |
| // We unpoison the shadow memory for the global but we do not remove it from |
| // the list because that would require O(n^2) time with the current list |
| // 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 |
| using namespace __asan; // NOLINT |
| |
| // Register an array of globals. |
| void __asan_register_globals(__asan_global *globals, uptr n) { |
| if (!flags()->report_globals) return; |
| BlockingMutexLock lock(&mu_for_globals); |
| for (uptr i = 0; i < n; i++) { |
| RegisterGlobal(&globals[i]); |
| } |
| } |
| |
| // Unregister an array of globals. |
| // We must do this when a shared objects gets dlclosed. |
| void __asan_unregister_globals(__asan_global *globals, uptr n) { |
| if (!flags()->report_globals) return; |
| BlockingMutexLock lock(&mu_for_globals); |
| for (uptr i = 0; i < n; i++) { |
| 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); |
| BlockingMutexLock 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; |
| BlockingMutexLock lock(&mu_for_globals); |
| for (ListOfGlobals *l = list_of_dynamic_init_globals; l; l = l->next) |
| UnpoisonGlobal(l->g); |
| } |