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_poisoning.h" 16#include "asan_report.h" 17#include "asan_stack.h" 18#include "asan_stats.h" 19#include "asan_thread.h" 20#include "sanitizer_common/sanitizer_common.h" 21#include "sanitizer_common/sanitizer_mutex.h" 22#include "sanitizer_common/sanitizer_placement_new.h" 23#include "sanitizer_common/sanitizer_stackdepot.h" 24 25namespace __asan { 26 27typedef __asan_global Global; 28 29struct ListOfGlobals { 30 const Global *g; 31 ListOfGlobals *next; 32}; 33 34static BlockingMutex mu_for_globals(LINKER_INITIALIZED); 35static LowLevelAllocator allocator_for_globals; 36static ListOfGlobals *list_of_all_globals; 37 38static const int kDynamicInitGlobalsInitialCapacity = 512; 39struct DynInitGlobal { 40 Global g; 41 bool initialized; 42}; 43typedef InternalMmapVector<DynInitGlobal> VectorOfGlobals; 44// Lazy-initialized and never deleted. 45static VectorOfGlobals *dynamic_init_globals; 46 47// We want to remember where a certain range of globals was registered. 48struct GlobalRegistrationSite { 49 u32 stack_id; 50 Global *g_first, *g_last; 51}; 52typedef InternalMmapVector<GlobalRegistrationSite> GlobalRegistrationSiteVector; 53static GlobalRegistrationSiteVector *global_registration_site_vector; 54 55ALWAYS_INLINE void PoisonShadowForGlobal(const Global *g, u8 value) { 56 FastPoisonShadow(g->beg, g->size_with_redzone, value); 57} 58 59ALWAYS_INLINE void PoisonRedZones(const Global &g) { 60 uptr aligned_size = RoundUpTo(g.size, SHADOW_GRANULARITY); 61 FastPoisonShadow(g.beg + aligned_size, g.size_with_redzone - aligned_size, 62 kAsanGlobalRedzoneMagic); 63 if (g.size != aligned_size) { 64 FastPoisonShadowPartialRightRedzone( 65 g.beg + RoundDownTo(g.size, SHADOW_GRANULARITY), 66 g.size % SHADOW_GRANULARITY, 67 SHADOW_GRANULARITY, 68 kAsanGlobalRedzoneMagic); 69 } 70} 71 72const uptr kMinimalDistanceFromAnotherGlobal = 64; 73 74bool IsAddressNearGlobal(uptr addr, const __asan_global &g) { 75 if (addr <= g.beg - kMinimalDistanceFromAnotherGlobal) return false; 76 if (addr >= g.beg + g.size_with_redzone) return false; 77 return true; 78} 79 80static void ReportGlobal(const Global &g, const char *prefix) { 81 Report("%s Global[%p]: beg=%p size=%zu/%zu name=%s module=%s dyn_init=%zu\n", 82 prefix, &g, (void *)g.beg, g.size, g.size_with_redzone, g.name, 83 g.module_name, g.has_dynamic_init); 84 if (g.location) { 85 Report(" location (%p): name=%s[%p], %d %d\n", g.location, 86 g.location->filename, g.location->filename, g.location->line_no, 87 g.location->column_no); 88 } 89} 90 91static bool DescribeOrGetInfoIfGlobal(uptr addr, uptr size, bool print, 92 Global *output_global) { 93 if (!flags()->report_globals) return false; 94 BlockingMutexLock lock(&mu_for_globals); 95 bool res = false; 96 for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) { 97 const Global &g = *l->g; 98 if (print) { 99 if (flags()->report_globals >= 2) 100 ReportGlobal(g, "Search"); 101 res |= DescribeAddressRelativeToGlobal(addr, size, g); 102 } else { 103 if (IsAddressNearGlobal(addr, g)) { 104 CHECK(output_global); 105 *output_global = g; 106 return true; 107 } 108 } 109 } 110 return res; 111} 112 113bool DescribeAddressIfGlobal(uptr addr, uptr size) { 114 return DescribeOrGetInfoIfGlobal(addr, size, /* print */ true, 115 /* output_global */ nullptr); 116} 117 118bool GetInfoForAddressIfGlobal(uptr addr, AddressDescription *descr) { 119 Global g = {}; 120 if (DescribeOrGetInfoIfGlobal(addr, /* size */ 1, /* print */ false, &g)) { 121 internal_strncpy(descr->name, g.name, descr->name_size); 122 descr->region_address = g.beg; 123 descr->region_size = g.size; 124 descr->region_kind = "global"; 125 return true; 126 } 127 return false; 128} 129 130u32 FindRegistrationSite(const Global *g) { 131 CHECK(global_registration_site_vector); 132 for (uptr i = 0, n = global_registration_site_vector->size(); i < n; i++) { 133 GlobalRegistrationSite &grs = (*global_registration_site_vector)[i]; 134 if (g >= grs.g_first && g <= grs.g_last) 135 return grs.stack_id; 136 } 137 return 0; 138} 139 140// Register a global variable. 141// This function may be called more than once for every global 142// so we store the globals in a map. 143static void RegisterGlobal(const Global *g) { 144 CHECK(asan_inited); 145 if (flags()->report_globals >= 2) 146 ReportGlobal(*g, "Added"); 147 CHECK(flags()->report_globals); 148 CHECK(AddrIsInMem(g->beg)); 149 CHECK(AddrIsAlignedByGranularity(g->beg)); 150 CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); 151 // This "ODR violation" detection is fundamentally incompatible with 152 // how GCC registers globals. Disable as useless until rewritten upstream. 153 if (0 && flags()->detect_odr_violation) { 154 // Try detecting ODR (One Definition Rule) violation, i.e. the situation 155 // where two globals with the same name are defined in different modules. 156 if (__asan_region_is_poisoned(g->beg, g->size_with_redzone)) { 157 // This check may not be enough: if the first global is much larger 158 // the entire redzone of the second global may be within the first global. 159 for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) { 160 if (g->beg == l->g->beg && 161 (flags()->detect_odr_violation >= 2 || g->size != l->g->size)) 162 ReportODRViolation(g, FindRegistrationSite(g), 163 l->g, FindRegistrationSite(l->g)); 164 } 165 } 166 } 167 if (flags()->poison_heap) 168 PoisonRedZones(*g); 169 ListOfGlobals *l = new(allocator_for_globals) ListOfGlobals; 170 l->g = g; 171 l->next = list_of_all_globals; 172 list_of_all_globals = l; 173 if (g->has_dynamic_init) { 174 if (dynamic_init_globals == 0) { 175 dynamic_init_globals = new(allocator_for_globals) 176 VectorOfGlobals(kDynamicInitGlobalsInitialCapacity); 177 } 178 DynInitGlobal dyn_global = { *g, false }; 179 dynamic_init_globals->push_back(dyn_global); 180 } 181} 182 183static void UnregisterGlobal(const Global *g) { 184 CHECK(asan_inited); 185 CHECK(flags()->report_globals); 186 CHECK(AddrIsInMem(g->beg)); 187 CHECK(AddrIsAlignedByGranularity(g->beg)); 188 CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); 189 if (flags()->poison_heap) 190 PoisonShadowForGlobal(g, 0); 191 // We unpoison the shadow memory for the global but we do not remove it from 192 // the list because that would require O(n^2) time with the current list 193 // implementation. It might not be worth doing anyway. 194} 195 196void StopInitOrderChecking() { 197 BlockingMutexLock lock(&mu_for_globals); 198 if (!flags()->check_initialization_order || !dynamic_init_globals) 199 return; 200 flags()->check_initialization_order = false; 201 for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) { 202 DynInitGlobal &dyn_g = (*dynamic_init_globals)[i]; 203 const Global *g = &dyn_g.g; 204 // Unpoison the whole global. 205 PoisonShadowForGlobal(g, 0); 206 // Poison redzones back. 207 PoisonRedZones(*g); 208 } 209} 210 211} // namespace __asan 212 213// ---------------------- Interface ---------------- {{{1 214using namespace __asan; // NOLINT 215 216// Register an array of globals. 217void __asan_register_globals(__asan_global *globals, uptr n) { 218 if (!flags()->report_globals) return; 219 GET_STACK_TRACE_FATAL_HERE; 220 u32 stack_id = StackDepotPut(stack); 221 BlockingMutexLock lock(&mu_for_globals); 222 if (!global_registration_site_vector) 223 global_registration_site_vector = 224 new(allocator_for_globals) GlobalRegistrationSiteVector(128); 225 GlobalRegistrationSite site = {stack_id, &globals[0], &globals[n - 1]}; 226 global_registration_site_vector->push_back(site); 227 if (flags()->report_globals >= 2) { 228 PRINT_CURRENT_STACK(); 229 Printf("=== ID %d; %p %p\n", stack_id, &globals[0], &globals[n - 1]); 230 } 231 for (uptr i = 0; i < n; i++) { 232 RegisterGlobal(&globals[i]); 233 } 234} 235 236// Unregister an array of globals. 237// We must do this when a shared objects gets dlclosed. 238void __asan_unregister_globals(__asan_global *globals, uptr n) { 239 if (!flags()->report_globals) return; 240 BlockingMutexLock lock(&mu_for_globals); 241 for (uptr i = 0; i < n; i++) { 242 UnregisterGlobal(&globals[i]); 243 } 244} 245 246// This method runs immediately prior to dynamic initialization in each TU, 247// when all dynamically initialized globals are unpoisoned. This method 248// poisons all global variables not defined in this TU, so that a dynamic 249// initializer can only touch global variables in the same TU. 250void __asan_before_dynamic_init(const char *module_name) { 251 if (!flags()->check_initialization_order || 252 !flags()->poison_heap) 253 return; 254 bool strict_init_order = flags()->strict_init_order; 255 CHECK(dynamic_init_globals); 256 CHECK(module_name); 257 CHECK(asan_inited); 258 BlockingMutexLock lock(&mu_for_globals); 259 if (flags()->report_globals >= 3) 260 Printf("DynInitPoison module: %s\n", module_name); 261 for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) { 262 DynInitGlobal &dyn_g = (*dynamic_init_globals)[i]; 263 const Global *g = &dyn_g.g; 264 if (dyn_g.initialized) 265 continue; 266 if (g->module_name != module_name) 267 PoisonShadowForGlobal(g, kAsanInitializationOrderMagic); 268 else if (!strict_init_order) 269 dyn_g.initialized = true; 270 } 271} 272 273// This method runs immediately after dynamic initialization in each TU, when 274// all dynamically initialized globals except for those defined in the current 275// TU are poisoned. It simply unpoisons all dynamically initialized globals. 276void __asan_after_dynamic_init() { 277 if (!flags()->check_initialization_order || 278 !flags()->poison_heap) 279 return; 280 CHECK(asan_inited); 281 BlockingMutexLock lock(&mu_for_globals); 282 // FIXME: Optionally report that we're unpoisoning globals from a module. 283 for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) { 284 DynInitGlobal &dyn_g = (*dynamic_init_globals)[i]; 285 const Global *g = &dyn_g.g; 286 if (!dyn_g.initialized) { 287 // Unpoison the whole global. 288 PoisonShadowForGlobal(g, 0); 289 // Poison redzones back. 290 PoisonRedZones(*g); 291 } 292 } 293} 294