| 1 | //===-- asan_globals.cpp --------------------------------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // This file is a part of AddressSanitizer, an address sanity checker. |
| 10 | // |
| 11 | // Handle globals. |
| 12 | //===----------------------------------------------------------------------===// |
| 13 | |
| 14 | #include "asan_interceptors.h" |
| 15 | #include "asan_internal.h" |
| 16 | #include "asan_mapping.h" |
| 17 | #include "asan_poisoning.h" |
| 18 | #include "asan_report.h" |
| 19 | #include "asan_stack.h" |
| 20 | #include "asan_stats.h" |
| 21 | #include "asan_suppressions.h" |
| 22 | #include "asan_thread.h" |
| 23 | #include "sanitizer_common/sanitizer_common.h" |
| 24 | #include "sanitizer_common/sanitizer_dense_map.h" |
| 25 | #include "sanitizer_common/sanitizer_list.h" |
| 26 | #include "sanitizer_common/sanitizer_mutex.h" |
| 27 | #include "sanitizer_common/sanitizer_placement_new.h" |
| 28 | #include "sanitizer_common/sanitizer_stackdepot.h" |
| 29 | #include "sanitizer_common/sanitizer_symbolizer.h" |
| 30 | #include "sanitizer_common/sanitizer_thread_safety.h" |
| 31 | |
| 32 | namespace __asan { |
| 33 | |
| 34 | typedef __asan_global Global; |
| 35 | |
| 36 | struct GlobalListNode { |
| 37 | const Global *g = nullptr; |
| 38 | GlobalListNode *next = nullptr; |
| 39 | }; |
| 40 | typedef IntrusiveList<GlobalListNode> ListOfGlobals; |
| 41 | |
| 42 | static Mutex mu_for_globals; |
| 43 | static ListOfGlobals list_of_all_globals SANITIZER_GUARDED_BY(mu_for_globals); |
| 44 | |
| 45 | struct DynInitGlobal { |
| 46 | Global g = {}; |
| 47 | bool initialized = false; |
| 48 | DynInitGlobal *next = nullptr; |
| 49 | }; |
| 50 | |
| 51 | // We want to remember where a certain range of globals was registered. |
| 52 | struct GlobalRegistrationSite { |
| 53 | u32 stack_id; |
| 54 | Global *g_first, *g_last; |
| 55 | }; |
| 56 | typedef InternalMmapVector<GlobalRegistrationSite> GlobalRegistrationSiteVector; |
| 57 | static GlobalRegistrationSiteVector *global_registration_site_vector; |
| 58 | |
| 59 | static ListOfGlobals &GlobalsByIndicator(uptr odr_indicator) |
| 60 | SANITIZER_REQUIRES(mu_for_globals) { |
| 61 | using MapOfGlobals = DenseMap<uptr, ListOfGlobals>; |
| 62 | |
| 63 | static MapOfGlobals *globals_by_indicator = nullptr; |
| 64 | if (!globals_by_indicator) { |
| 65 | alignas( |
| 66 | alignof(MapOfGlobals)) static char placeholder[sizeof(MapOfGlobals)]; |
| 67 | globals_by_indicator = new (placeholder) MapOfGlobals(); |
| 68 | } |
| 69 | |
| 70 | return (*globals_by_indicator)[odr_indicator]; |
| 71 | } |
| 72 | |
| 73 | static const char *current_dynamic_init_module_name |
| 74 | SANITIZER_GUARDED_BY(mu_for_globals) = nullptr; |
| 75 | |
| 76 | using DynInitGlobalsByModule = |
| 77 | DenseMap<const char *, IntrusiveList<DynInitGlobal>>; |
| 78 | |
| 79 | // TODO: Add a NoDestroy helper, this patter is very common in sanitizers. |
| 80 | static DynInitGlobalsByModule &DynInitGlobals() |
| 81 | SANITIZER_REQUIRES(mu_for_globals) { |
| 82 | static DynInitGlobalsByModule *globals_by_module = nullptr; |
| 83 | if (!globals_by_module) { |
| 84 | alignas(alignof(DynInitGlobalsByModule)) static char |
| 85 | placeholder[sizeof(DynInitGlobalsByModule)]; |
| 86 | globals_by_module = new (placeholder) DynInitGlobalsByModule(); |
| 87 | } |
| 88 | |
| 89 | return *globals_by_module; |
| 90 | } |
| 91 | |
| 92 | ALWAYS_INLINE void PoisonShadowForGlobal(const Global *g, u8 value) { |
| 93 | FastPoisonShadow(aligned_beg: g->beg, aligned_size: g->size_with_redzone, value); |
| 94 | } |
| 95 | |
| 96 | ALWAYS_INLINE void PoisonRedZones(const Global &g) { |
| 97 | uptr aligned_size = RoundUpTo(size: g.size, ASAN_SHADOW_GRANULARITY); |
| 98 | FastPoisonShadow(aligned_beg: g.beg + aligned_size, aligned_size: g.size_with_redzone - aligned_size, |
| 99 | value: kAsanGlobalRedzoneMagic); |
| 100 | if (g.size != aligned_size) { |
| 101 | FastPoisonShadowPartialRightRedzone( |
| 102 | aligned_addr: g.beg + RoundDownTo(x: g.size, ASAN_SHADOW_GRANULARITY), |
| 103 | size: g.size % ASAN_SHADOW_GRANULARITY, ASAN_SHADOW_GRANULARITY, |
| 104 | value: kAsanGlobalRedzoneMagic); |
| 105 | } |
| 106 | } |
| 107 | |
| 108 | const uptr kMinimalDistanceFromAnotherGlobal = 64; |
| 109 | |
| 110 | static void AddGlobalToList(ListOfGlobals &list, const Global *g) { |
| 111 | list.push_front(x: new (GetGlobalLowLevelAllocator()) GlobalListNode{.g: g}); |
| 112 | } |
| 113 | |
| 114 | static void UnpoisonDynamicGlobals(IntrusiveList<DynInitGlobal> &dyn_globals, |
| 115 | bool mark_initialized) { |
| 116 | for (auto &dyn_g : dyn_globals) { |
| 117 | const Global *g = &dyn_g.g; |
| 118 | if (dyn_g.initialized) |
| 119 | continue; |
| 120 | // Unpoison the whole global. |
| 121 | PoisonShadowForGlobal(g, value: 0); |
| 122 | // Poison redzones back. |
| 123 | PoisonRedZones(g: *g); |
| 124 | if (mark_initialized) |
| 125 | dyn_g.initialized = true; |
| 126 | } |
| 127 | } |
| 128 | |
| 129 | static void PoisonDynamicGlobals( |
| 130 | const IntrusiveList<DynInitGlobal> &dyn_globals) { |
| 131 | for (auto &dyn_g : dyn_globals) { |
| 132 | const Global *g = &dyn_g.g; |
| 133 | if (dyn_g.initialized) |
| 134 | continue; |
| 135 | PoisonShadowForGlobal(g, value: kAsanInitializationOrderMagic); |
| 136 | } |
| 137 | } |
| 138 | |
| 139 | static bool IsAddressNearGlobal(uptr addr, const __asan_global &g) { |
| 140 | if (addr <= g.beg - kMinimalDistanceFromAnotherGlobal) return false; |
| 141 | if (addr >= g.beg + g.size_with_redzone) return false; |
| 142 | return true; |
| 143 | } |
| 144 | |
| 145 | static void ReportGlobal(const Global &g, const char *prefix) { |
| 146 | DataInfo info; |
| 147 | bool symbolized = Symbolizer::GetOrInit()->SymbolizeData(address: g.beg, info: &info); |
| 148 | Report( |
| 149 | format: "%s Global[%p]: beg=%p size=%zu/%zu name=%s source=%s module=%s " |
| 150 | "dyn_init=%zu " |
| 151 | "odr_indicator=%p\n" , |
| 152 | prefix, (void *)&g, (void *)g.beg, g.size, g.size_with_redzone, g.name, |
| 153 | g.module_name, (symbolized ? info.module : "?" ), g.has_dynamic_init, |
| 154 | (void *)g.odr_indicator); |
| 155 | |
| 156 | if (symbolized && info.line != 0) { |
| 157 | Report(format: " location: name=%s, %d\n" , info.file, static_cast<int>(info.line)); |
| 158 | } else if (g.gcc_location != 0) { |
| 159 | // Fallback to Global::gcc_location |
| 160 | Report(format: " location: name=%s, %d\n" , g.gcc_location->filename, g.gcc_location->line_no); |
| 161 | } |
| 162 | } |
| 163 | |
| 164 | static u32 FindRegistrationSite(const Global *g) { |
| 165 | mu_for_globals.CheckLocked(); |
| 166 | CHECK(global_registration_site_vector); |
| 167 | for (uptr i = 0, n = global_registration_site_vector->size(); i < n; i++) { |
| 168 | GlobalRegistrationSite &grs = (*global_registration_site_vector)[i]; |
| 169 | if (g >= grs.g_first && g <= grs.g_last) |
| 170 | return grs.stack_id; |
| 171 | } |
| 172 | return 0; |
| 173 | } |
| 174 | |
| 175 | int GetGlobalsForAddress(uptr addr, Global *globals, u32 *reg_sites, |
| 176 | int max_globals) { |
| 177 | if (!flags()->report_globals) return 0; |
| 178 | Lock lock(&mu_for_globals); |
| 179 | int res = 0; |
| 180 | for (const auto &l : list_of_all_globals) { |
| 181 | const Global &g = *l.g; |
| 182 | if (flags()->report_globals >= 2) |
| 183 | ReportGlobal(g, prefix: "Search" ); |
| 184 | if (IsAddressNearGlobal(addr, g)) { |
| 185 | internal_memcpy(dest: &globals[res], src: &g, n: sizeof(g)); |
| 186 | if (reg_sites) |
| 187 | reg_sites[res] = FindRegistrationSite(g: &g); |
| 188 | res++; |
| 189 | if (res == max_globals) |
| 190 | break; |
| 191 | } |
| 192 | } |
| 193 | return res; |
| 194 | } |
| 195 | |
| 196 | enum GlobalSymbolState { |
| 197 | UNREGISTERED = 0, |
| 198 | REGISTERED = 1 |
| 199 | }; |
| 200 | |
| 201 | // Check ODR violation for given global G via special ODR indicator. We use |
| 202 | // this method in case compiler instruments global variables through their |
| 203 | // local aliases. |
| 204 | static void CheckODRViolationViaIndicator(const Global *g) |
| 205 | SANITIZER_REQUIRES(mu_for_globals) { |
| 206 | // Instrumentation requests to skip ODR check. |
| 207 | if (g->odr_indicator == UINTPTR_MAX) |
| 208 | return; |
| 209 | |
| 210 | ListOfGlobals &relevant_globals = GlobalsByIndicator(odr_indicator: g->odr_indicator); |
| 211 | |
| 212 | u8 *odr_indicator = reinterpret_cast<u8 *>(g->odr_indicator); |
| 213 | if (*odr_indicator == REGISTERED) { |
| 214 | // If *odr_indicator is REGISTERED, some module have already registered |
| 215 | // externally visible symbol with the same name. This is an ODR violation. |
| 216 | for (const auto &l : relevant_globals) { |
| 217 | if ((flags()->detect_odr_violation >= 2 || g->size != l.g->size) && |
| 218 | !IsODRViolationSuppressed(global_var_name: g->name)) |
| 219 | ReportODRViolation(g1: g, stack_id1: FindRegistrationSite(g), g2: l.g, |
| 220 | stack_id2: FindRegistrationSite(g: l.g)); |
| 221 | } |
| 222 | } else { // UNREGISTERED |
| 223 | *odr_indicator = REGISTERED; |
| 224 | } |
| 225 | |
| 226 | AddGlobalToList(list&: relevant_globals, g); |
| 227 | } |
| 228 | |
| 229 | // Check ODR violation for given global G by checking if it's already poisoned. |
| 230 | // We use this method in case compiler doesn't use private aliases for global |
| 231 | // variables. |
| 232 | static void CheckODRViolationViaPoisoning(const Global *g) |
| 233 | SANITIZER_REQUIRES(mu_for_globals) { |
| 234 | if (__asan_region_is_poisoned(beg: g->beg, size: g->size_with_redzone)) { |
| 235 | // This check may not be enough: if the first global is much larger |
| 236 | // the entire redzone of the second global may be within the first global. |
| 237 | for (const auto &l : list_of_all_globals) { |
| 238 | if (g->beg == l.g->beg && |
| 239 | (flags()->detect_odr_violation >= 2 || g->size != l.g->size) && |
| 240 | !IsODRViolationSuppressed(global_var_name: g->name)) { |
| 241 | ReportODRViolation(g1: g, stack_id1: FindRegistrationSite(g), g2: l.g, |
| 242 | stack_id2: FindRegistrationSite(g: l.g)); |
| 243 | } |
| 244 | } |
| 245 | } |
| 246 | } |
| 247 | |
| 248 | // Clang provides two different ways for global variables protection: |
| 249 | // it can poison the global itself or its private alias. In former |
| 250 | // case we may poison same symbol multiple times, that can help us to |
| 251 | // cheaply detect ODR violation: if we try to poison an already poisoned |
| 252 | // global, we have ODR violation error. |
| 253 | // In latter case, we poison each symbol exactly once, so we use special |
| 254 | // indicator symbol to perform similar check. |
| 255 | // In either case, compiler provides a special odr_indicator field to Global |
| 256 | // structure, that can contain two kinds of values: |
| 257 | // 1) Non-zero value. In this case, odr_indicator is an address of |
| 258 | // corresponding indicator variable for given global. |
| 259 | // 2) Zero. This means that we don't use private aliases for global variables |
| 260 | // and can freely check ODR violation with the first method. |
| 261 | // |
| 262 | // This routine chooses between two different methods of ODR violation |
| 263 | // detection. |
| 264 | static inline bool UseODRIndicator(const Global *g) { |
| 265 | return g->odr_indicator > 0; |
| 266 | } |
| 267 | |
| 268 | // Register a global variable. |
| 269 | // This function may be called more than once for every global |
| 270 | // so we store the globals in a map. |
| 271 | static void RegisterGlobal(const Global *g) SANITIZER_REQUIRES(mu_for_globals) { |
| 272 | CHECK(AsanInited()); |
| 273 | if (flags()->report_globals >= 2) |
| 274 | ReportGlobal(g: *g, prefix: "Added" ); |
| 275 | CHECK(flags()->report_globals); |
| 276 | CHECK(AddrIsInMem(g->beg)); |
| 277 | if (!AddrIsAlignedByGranularity(a: g->beg)) { |
| 278 | Report(format: "The following global variable is not properly aligned.\n" ); |
| 279 | Report(format: "This may happen if another global with the same name\n" ); |
| 280 | Report(format: "resides in another non-instrumented module.\n" ); |
| 281 | Report(format: "Or the global comes from a C file built w/o -fno-common.\n" ); |
| 282 | Report(format: "In either case this is likely an ODR violation bug,\n" ); |
| 283 | Report(format: "but AddressSanitizer can not provide more details.\n" ); |
| 284 | ReportODRViolation(g1: g, stack_id1: FindRegistrationSite(g), g2: g, stack_id2: FindRegistrationSite(g)); |
| 285 | CHECK(AddrIsAlignedByGranularity(g->beg)); |
| 286 | } |
| 287 | CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); |
| 288 | if (flags()->detect_odr_violation) { |
| 289 | // Try detecting ODR (One Definition Rule) violation, i.e. the situation |
| 290 | // where two globals with the same name are defined in different modules. |
| 291 | if (UseODRIndicator(g)) |
| 292 | CheckODRViolationViaIndicator(g); |
| 293 | else |
| 294 | CheckODRViolationViaPoisoning(g); |
| 295 | } |
| 296 | if (CanPoisonMemory()) |
| 297 | PoisonRedZones(g: *g); |
| 298 | |
| 299 | AddGlobalToList(list&: list_of_all_globals, g); |
| 300 | |
| 301 | if (g->has_dynamic_init) { |
| 302 | DynInitGlobals()[g->module_name].push_back( |
| 303 | x: new (GetGlobalLowLevelAllocator()) DynInitGlobal{.g: *g, .initialized: false}); |
| 304 | } |
| 305 | } |
| 306 | |
| 307 | static void UnregisterGlobal(const Global *g) |
| 308 | SANITIZER_REQUIRES(mu_for_globals) { |
| 309 | CHECK(AsanInited()); |
| 310 | if (flags()->report_globals >= 2) |
| 311 | ReportGlobal(g: *g, prefix: "Removed" ); |
| 312 | CHECK(flags()->report_globals); |
| 313 | CHECK(AddrIsInMem(g->beg)); |
| 314 | CHECK(AddrIsAlignedByGranularity(g->beg)); |
| 315 | CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); |
| 316 | if (CanPoisonMemory()) |
| 317 | PoisonShadowForGlobal(g, value: 0); |
| 318 | // We unpoison the shadow memory for the global but we do not remove it from |
| 319 | // the list because that would require O(n^2) time with the current list |
| 320 | // implementation. It might not be worth doing anyway. |
| 321 | |
| 322 | // Release ODR indicator. |
| 323 | if (UseODRIndicator(g) && g->odr_indicator != UINTPTR_MAX) { |
| 324 | u8 *odr_indicator = reinterpret_cast<u8 *>(g->odr_indicator); |
| 325 | *odr_indicator = UNREGISTERED; |
| 326 | } |
| 327 | } |
| 328 | |
| 329 | void StopInitOrderChecking() { |
| 330 | if (!flags()->check_initialization_order) |
| 331 | return; |
| 332 | Lock lock(&mu_for_globals); |
| 333 | flags()->check_initialization_order = false; |
| 334 | DynInitGlobals().forEach(fn: [&](auto &kv) { |
| 335 | UnpoisonDynamicGlobals(kv.second, /*mark_initialized=*/false); |
| 336 | return true; |
| 337 | }); |
| 338 | } |
| 339 | |
| 340 | static bool IsASCII(unsigned char c) { return /*0x00 <= c &&*/ c <= 0x7F; } |
| 341 | |
| 342 | const char *MaybeDemangleGlobalName(const char *name) { |
| 343 | // We can spoil names of globals with C linkage, so use an heuristic |
| 344 | // approach to check if the name should be demangled. |
| 345 | bool should_demangle = false; |
| 346 | if (name[0] == '_' && name[1] == 'Z') |
| 347 | should_demangle = true; |
| 348 | else if (SANITIZER_WINDOWS && name[0] == '\01' && name[1] == '?') |
| 349 | should_demangle = true; |
| 350 | |
| 351 | return should_demangle ? Symbolizer::GetOrInit()->Demangle(name) : name; |
| 352 | } |
| 353 | |
| 354 | // Check if the global is a zero-terminated ASCII string. If so, print it. |
| 355 | void PrintGlobalNameIfASCII(InternalScopedString *str, const __asan_global &g) { |
| 356 | for (uptr p = g.beg; p < g.beg + g.size - 1; p++) { |
| 357 | unsigned char c = *(unsigned char *)p; |
| 358 | if (c == '\0' || !IsASCII(c)) return; |
| 359 | } |
| 360 | if (*(char *)(g.beg + g.size - 1) != '\0') return; |
| 361 | str->AppendF(format: " '%s' is ascii string '%s'\n" , MaybeDemangleGlobalName(name: g.name), |
| 362 | (char *)g.beg); |
| 363 | } |
| 364 | |
| 365 | void PrintGlobalLocation(InternalScopedString *str, const __asan_global &g, |
| 366 | bool print_module_name) { |
| 367 | DataInfo info; |
| 368 | if (Symbolizer::GetOrInit()->SymbolizeData(address: g.beg, info: &info) && info.line != 0) { |
| 369 | str->AppendF(format: "%s:%d" , info.file, static_cast<int>(info.line)); |
| 370 | } else if (g.gcc_location != 0) { |
| 371 | // Fallback to Global::gcc_location |
| 372 | str->AppendF(format: "%s" , g.gcc_location->filename ? g.gcc_location->filename |
| 373 | : g.module_name); |
| 374 | if (g.gcc_location->line_no) |
| 375 | str->AppendF(format: ":%d" , g.gcc_location->line_no); |
| 376 | if (g.gcc_location->column_no) |
| 377 | str->AppendF(format: ":%d" , g.gcc_location->column_no); |
| 378 | } else { |
| 379 | str->AppendF(format: "%s" , g.module_name); |
| 380 | } |
| 381 | if (print_module_name && info.module) |
| 382 | str->AppendF(format: " in %s" , info.module); |
| 383 | } |
| 384 | |
| 385 | } // namespace __asan |
| 386 | |
| 387 | // ---------------------- Interface ---------------- {{{1 |
| 388 | using namespace __asan; |
| 389 | |
| 390 | // Apply __asan_register_globals to all globals found in the same loaded |
| 391 | // executable or shared library as `flag'. The flag tracks whether globals have |
| 392 | // already been registered or not for this image. |
| 393 | void __asan_register_image_globals(uptr *flag) { |
| 394 | if (*flag) |
| 395 | return; |
| 396 | AsanApplyToGlobals(op: __asan_register_globals, needle: flag); |
| 397 | *flag = 1; |
| 398 | } |
| 399 | |
| 400 | // This mirrors __asan_register_image_globals. |
| 401 | void __asan_unregister_image_globals(uptr *flag) { |
| 402 | if (!*flag) |
| 403 | return; |
| 404 | AsanApplyToGlobals(op: __asan_unregister_globals, needle: flag); |
| 405 | *flag = 0; |
| 406 | } |
| 407 | |
| 408 | void __asan_register_elf_globals(uptr *flag, void *start, void *stop) { |
| 409 | if (*flag || start == stop) |
| 410 | return; |
| 411 | CHECK_EQ(0, ((uptr)stop - (uptr)start) % sizeof(__asan_global)); |
| 412 | __asan_global *globals_start = (__asan_global*)start; |
| 413 | __asan_global *globals_stop = (__asan_global*)stop; |
| 414 | __asan_register_globals(globals: globals_start, n: globals_stop - globals_start); |
| 415 | *flag = 1; |
| 416 | } |
| 417 | |
| 418 | void __asan_unregister_elf_globals(uptr *flag, void *start, void *stop) { |
| 419 | if (!*flag || start == stop) |
| 420 | return; |
| 421 | CHECK_EQ(0, ((uptr)stop - (uptr)start) % sizeof(__asan_global)); |
| 422 | __asan_global *globals_start = (__asan_global*)start; |
| 423 | __asan_global *globals_stop = (__asan_global*)stop; |
| 424 | __asan_unregister_globals(globals: globals_start, n: globals_stop - globals_start); |
| 425 | *flag = 0; |
| 426 | } |
| 427 | |
| 428 | // Register an array of globals. |
| 429 | void __asan_register_globals(__asan_global *globals, uptr n) { |
| 430 | if (!flags()->report_globals) return; |
| 431 | GET_STACK_TRACE_MALLOC; |
| 432 | u32 stack_id = StackDepotPut(stack); |
| 433 | Lock lock(&mu_for_globals); |
| 434 | if (!global_registration_site_vector) { |
| 435 | global_registration_site_vector = |
| 436 | new (GetGlobalLowLevelAllocator()) GlobalRegistrationSiteVector; |
| 437 | global_registration_site_vector->reserve(new_size: 128); |
| 438 | } |
| 439 | GlobalRegistrationSite site = {.stack_id: stack_id, .g_first: &globals[0], .g_last: &globals[n - 1]}; |
| 440 | global_registration_site_vector->push_back(element: site); |
| 441 | if (flags()->report_globals >= 2) { |
| 442 | PRINT_CURRENT_STACK(); |
| 443 | Printf(format: "=== ID %d; %p %p\n" , stack_id, (void *)&globals[0], |
| 444 | (void *)&globals[n - 1]); |
| 445 | } |
| 446 | for (uptr i = 0; i < n; i++) { |
| 447 | if (SANITIZER_WINDOWS && globals[i].beg == 0) { |
| 448 | // The MSVC incremental linker may pad globals out to 256 bytes. As long |
| 449 | // as __asan_global is less than 256 bytes large and its size is a power |
| 450 | // of two, we can skip over the padding. |
| 451 | static_assert( |
| 452 | sizeof(__asan_global) < 256 && |
| 453 | (sizeof(__asan_global) & (sizeof(__asan_global) - 1)) == 0, |
| 454 | "sizeof(__asan_global) incompatible with incremental linker padding" ); |
| 455 | // If these are padding bytes, the rest of the global should be zero. |
| 456 | CHECK(globals[i].size == 0 && globals[i].size_with_redzone == 0 && |
| 457 | globals[i].name == nullptr && globals[i].module_name == nullptr && |
| 458 | globals[i].odr_indicator == 0); |
| 459 | continue; |
| 460 | } |
| 461 | RegisterGlobal(g: &globals[i]); |
| 462 | } |
| 463 | |
| 464 | // Poison the metadata. It should not be accessible to user code. |
| 465 | PoisonShadow(addr: reinterpret_cast<uptr>(globals), size: n * sizeof(__asan_global), |
| 466 | value: kAsanGlobalRedzoneMagic); |
| 467 | } |
| 468 | |
| 469 | // Unregister an array of globals. |
| 470 | // We must do this when a shared objects gets dlclosed. |
| 471 | void __asan_unregister_globals(__asan_global *globals, uptr n) { |
| 472 | if (!flags()->report_globals) return; |
| 473 | Lock lock(&mu_for_globals); |
| 474 | for (uptr i = 0; i < n; i++) { |
| 475 | if (SANITIZER_WINDOWS && globals[i].beg == 0) { |
| 476 | // Skip globals that look like padding from the MSVC incremental linker. |
| 477 | // See comment in __asan_register_globals. |
| 478 | continue; |
| 479 | } |
| 480 | UnregisterGlobal(g: &globals[i]); |
| 481 | } |
| 482 | |
| 483 | // Unpoison the metadata. |
| 484 | PoisonShadow(addr: reinterpret_cast<uptr>(globals), size: n * sizeof(__asan_global), value: 0); |
| 485 | } |
| 486 | |
| 487 | // This method runs immediately prior to dynamic initialization in each TU, |
| 488 | // when all dynamically initialized globals are unpoisoned. This method |
| 489 | // poisons all global variables not defined in this TU, so that a dynamic |
| 490 | // initializer can only touch global variables in the same TU. |
| 491 | void __asan_before_dynamic_init(const char *module_name) { |
| 492 | if (!flags()->check_initialization_order || !CanPoisonMemory()) |
| 493 | return; |
| 494 | bool strict_init_order = flags()->strict_init_order; |
| 495 | CHECK(module_name); |
| 496 | CHECK(AsanInited()); |
| 497 | Lock lock(&mu_for_globals); |
| 498 | if (current_dynamic_init_module_name == module_name) |
| 499 | return; |
| 500 | if (flags()->report_globals >= 3) |
| 501 | Printf(format: "DynInitPoison module: %s\n" , module_name); |
| 502 | |
| 503 | if (current_dynamic_init_module_name == nullptr) { |
| 504 | // First call, poison all globals from other modules. |
| 505 | DynInitGlobals().forEach(fn: [&](auto &kv) { |
| 506 | if (kv.first != module_name) { |
| 507 | PoisonDynamicGlobals(kv.second); |
| 508 | } else { |
| 509 | UnpoisonDynamicGlobals(kv.second, |
| 510 | /*mark_initialized=*/!strict_init_order); |
| 511 | } |
| 512 | return true; |
| 513 | }); |
| 514 | } else { |
| 515 | // Module changed. |
| 516 | PoisonDynamicGlobals(dyn_globals: DynInitGlobals()[current_dynamic_init_module_name]); |
| 517 | UnpoisonDynamicGlobals(dyn_globals&: DynInitGlobals()[module_name], |
| 518 | /*mark_initialized=*/!strict_init_order); |
| 519 | } |
| 520 | current_dynamic_init_module_name = module_name; |
| 521 | } |
| 522 | |
| 523 | // Maybe SANITIZER_CAN_USE_PREINIT_ARRAY is to conservative for `.init_array`, |
| 524 | // however we should not make mistake here. If `UnpoisonBeforeMain` was not |
| 525 | // executed at all we will have false reports on globals. |
| 526 | #if SANITIZER_CAN_USE_PREINIT_ARRAY |
| 527 | // This optimization aims to reduce the overhead of `__asan_after_dynamic_init` |
| 528 | // calls by leveraging incremental unpoisoning/poisoning in |
| 529 | // `__asan_before_dynamic_init`. We expect most `__asan_after_dynamic_init |
| 530 | // calls` to be no-ops. However, to ensure all globals are unpoisoned before the |
| 531 | // `main`, we force `UnpoisonBeforeMain` to fully execute |
| 532 | // `__asan_after_dynamic_init`. |
| 533 | |
| 534 | // With lld, `UnpoisonBeforeMain` runs after standard `.init_array`, making it |
| 535 | // the final `__asan_after_dynamic_init` call for the static runtime. In |
| 536 | // contrast, GNU ld executes it earlier, causing subsequent |
| 537 | // `__asan_after_dynamic_init` calls to perform full unpoisoning, losing the |
| 538 | // optimization. |
| 539 | bool allow_after_dynamic_init SANITIZER_GUARDED_BY(mu_for_globals) = false; |
| 540 | |
| 541 | static void UnpoisonBeforeMain(void) { |
| 542 | { |
| 543 | Lock lock(&mu_for_globals); |
| 544 | if (allow_after_dynamic_init) |
| 545 | return; |
| 546 | allow_after_dynamic_init = true; |
| 547 | } |
| 548 | if (flags()->report_globals >= 3) |
| 549 | Printf(format: "UnpoisonBeforeMain\n" ); |
| 550 | __asan_after_dynamic_init(); |
| 551 | } |
| 552 | |
| 553 | __attribute__((section(".init_array.65537" ), used)) static void ( |
| 554 | *asan_after_init_array)(void) = UnpoisonBeforeMain; |
| 555 | #else |
| 556 | // Incremental poisoning is disabled, unpoison globals immediately. |
| 557 | static constexpr bool allow_after_dynamic_init = true; |
| 558 | #endif // SANITIZER_CAN_USE_PREINIT_ARRAY |
| 559 | |
| 560 | // This method runs immediately after dynamic initialization in each TU, when |
| 561 | // all dynamically initialized globals except for those defined in the current |
| 562 | // TU are poisoned. It simply unpoisons all dynamically initialized globals. |
| 563 | void __asan_after_dynamic_init() { |
| 564 | if (!flags()->check_initialization_order || !CanPoisonMemory()) |
| 565 | return; |
| 566 | CHECK(AsanInited()); |
| 567 | Lock lock(&mu_for_globals); |
| 568 | if (!allow_after_dynamic_init) |
| 569 | return; |
| 570 | if (!current_dynamic_init_module_name) |
| 571 | return; |
| 572 | |
| 573 | if (flags()->report_globals >= 3) |
| 574 | Printf(format: "DynInitUnpoison\n" ); |
| 575 | |
| 576 | DynInitGlobals().forEach(fn: [&](auto &kv) { |
| 577 | UnpoisonDynamicGlobals(kv.second, /*mark_initialized=*/false); |
| 578 | return true; |
| 579 | }); |
| 580 | |
| 581 | current_dynamic_init_module_name = nullptr; |
| 582 | } |
| 583 | |