1 //===-- asan_globals.cc ---------------------------------------------------===// 2 // 3 // This file is distributed under the University of Illinois Open Source 4 // License. See LICENSE.TXT for details. 5 // 6 //===----------------------------------------------------------------------===// 7 // 8 // This file is a part of AddressSanitizer, an address sanity checker. 9 // 10 // Handle globals. 11 //===----------------------------------------------------------------------===// 12 #include "asan_interceptors.h" 13 #include "asan_internal.h" 14 #include "asan_mapping.h" 15 #include "asan_report.h" 16 #include "asan_stack.h" 17 #include "asan_stats.h" 18 #include "asan_thread.h" 19 #include "sanitizer_common/sanitizer_mutex.h" 20 21 namespace __asan { 22 23 typedef __asan_global Global; 24 25 struct ListOfGlobals { 26 const Global *g; 27 ListOfGlobals *next; 28 }; 29 30 static BlockingMutex mu_for_globals(LINKER_INITIALIZED); 31 static LowLevelAllocator allocator_for_globals; 32 static ListOfGlobals *list_of_all_globals; 33 static ListOfGlobals *list_of_dynamic_init_globals; 34 35 void PoisonRedZones(const Global &g) { 36 uptr aligned_size = RoundUpTo(g.size, SHADOW_GRANULARITY); 37 PoisonShadow(g.beg + aligned_size, g.size_with_redzone - aligned_size, 38 kAsanGlobalRedzoneMagic); 39 if (g.size != aligned_size) { 40 // partial right redzone 41 PoisonShadowPartialRightRedzone( 42 g.beg + RoundDownTo(g.size, SHADOW_GRANULARITY), 43 g.size % SHADOW_GRANULARITY, 44 SHADOW_GRANULARITY, 45 kAsanGlobalRedzoneMagic); 46 } 47 } 48 49 bool DescribeAddressIfGlobal(uptr addr, uptr size) { 50 if (!flags()->report_globals) return false; 51 BlockingMutexLock lock(&mu_for_globals); 52 bool res = false; 53 for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) { 54 const Global &g = *l->g; 55 if (flags()->report_globals >= 2) 56 Report("Search Global: beg=%p size=%zu name=%s\n", 57 (void*)g.beg, g.size, (char*)g.name); 58 res |= DescribeAddressRelativeToGlobal(addr, size, g); 59 } 60 return res; 61 } 62 63 // Register a global variable. 64 // This function may be called more than once for every global 65 // so we store the globals in a map. 66 static void RegisterGlobal(const Global *g) { 67 CHECK(asan_inited); 68 if (flags()->report_globals >= 2) 69 Report("Added Global: beg=%p size=%zu/%zu name=%s dyn.init=%zu\n", 70 (void*)g->beg, g->size, g->size_with_redzone, g->name, 71 g->has_dynamic_init); 72 CHECK(flags()->report_globals); 73 CHECK(AddrIsInMem(g->beg)); 74 CHECK(AddrIsAlignedByGranularity(g->beg)); 75 CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); 76 PoisonRedZones(*g); 77 ListOfGlobals *l = 78 (ListOfGlobals*)allocator_for_globals.Allocate(sizeof(ListOfGlobals)); 79 l->g = g; 80 l->next = list_of_all_globals; 81 list_of_all_globals = l; 82 if (g->has_dynamic_init) { 83 l = (ListOfGlobals*)allocator_for_globals.Allocate(sizeof(ListOfGlobals)); 84 l->g = g; 85 l->next = list_of_dynamic_init_globals; 86 list_of_dynamic_init_globals = l; 87 } 88 } 89 90 static void UnregisterGlobal(const Global *g) { 91 CHECK(asan_inited); 92 CHECK(flags()->report_globals); 93 CHECK(AddrIsInMem(g->beg)); 94 CHECK(AddrIsAlignedByGranularity(g->beg)); 95 CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); 96 PoisonShadow(g->beg, g->size_with_redzone, 0); 97 // We unpoison the shadow memory for the global but we do not remove it from 98 // the list because that would require O(n^2) time with the current list 99 // implementation. It might not be worth doing anyway. 100 } 101 102 // Poison all shadow memory for a single global. 103 static void PoisonGlobalAndRedzones(const Global *g) { 104 CHECK(asan_inited); 105 CHECK(flags()->check_initialization_order); 106 CHECK(AddrIsInMem(g->beg)); 107 CHECK(AddrIsAlignedByGranularity(g->beg)); 108 CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); 109 if (flags()->report_globals >= 3) 110 Printf("DynInitPoison : %s\n", g->name); 111 PoisonShadow(g->beg, g->size_with_redzone, kAsanInitializationOrderMagic); 112 } 113 114 static void UnpoisonGlobal(const Global *g) { 115 CHECK(asan_inited); 116 CHECK(flags()->check_initialization_order); 117 CHECK(AddrIsInMem(g->beg)); 118 CHECK(AddrIsAlignedByGranularity(g->beg)); 119 CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); 120 if (flags()->report_globals >= 3) 121 Printf("DynInitUnpoison: %s\n", g->name); 122 PoisonShadow(g->beg, g->size_with_redzone, 0); 123 PoisonRedZones(*g); 124 } 125 126 } // namespace __asan 127 128 // ---------------------- Interface ---------------- {{{1 129 using namespace __asan; // NOLINT 130 131 // Register an array of globals. 132 void __asan_register_globals(__asan_global *globals, uptr n) { 133 if (!flags()->report_globals) return; 134 BlockingMutexLock lock(&mu_for_globals); 135 for (uptr i = 0; i < n; i++) { 136 RegisterGlobal(&globals[i]); 137 } 138 } 139 140 // Unregister an array of globals. 141 // We must do this when a shared objects gets dlclosed. 142 void __asan_unregister_globals(__asan_global *globals, uptr n) { 143 if (!flags()->report_globals) return; 144 BlockingMutexLock lock(&mu_for_globals); 145 for (uptr i = 0; i < n; i++) { 146 UnregisterGlobal(&globals[i]); 147 } 148 } 149 150 // This method runs immediately prior to dynamic initialization in each TU, 151 // when all dynamically initialized globals are unpoisoned. This method 152 // poisons all global variables not defined in this TU, so that a dynamic 153 // initializer can only touch global variables in the same TU. 154 void __asan_before_dynamic_init(uptr first_addr, uptr last_addr) { 155 if (!flags()->check_initialization_order) return; 156 CHECK(list_of_dynamic_init_globals); 157 BlockingMutexLock lock(&mu_for_globals); 158 bool from_current_tu = false; 159 // The list looks like: 160 // a => ... => b => last_addr => ... => first_addr => c => ... 161 // The globals of the current TU reside between last_addr and first_addr. 162 for (ListOfGlobals *l = list_of_dynamic_init_globals; l; l = l->next) { 163 if (l->g->beg == last_addr) 164 from_current_tu = true; 165 if (!from_current_tu) 166 PoisonGlobalAndRedzones(l->g); 167 if (l->g->beg == first_addr) 168 from_current_tu = false; 169 } 170 CHECK(!from_current_tu); 171 } 172 173 // This method runs immediately after dynamic initialization in each TU, when 174 // all dynamically initialized globals except for those defined in the current 175 // TU are poisoned. It simply unpoisons all dynamically initialized globals. 176 void __asan_after_dynamic_init() { 177 if (!flags()->check_initialization_order) return; 178 BlockingMutexLock lock(&mu_for_globals); 179 for (ListOfGlobals *l = list_of_dynamic_init_globals; l; l = l->next) 180 UnpoisonGlobal(l->g); 181 } 182