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
GlobalsByIndicator(uptr odr_indicator)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.
DynInitGlobals()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
PoisonShadowForGlobal(const Global * g,u8 value)92 ALWAYS_INLINE void PoisonShadowForGlobal(const Global *g, u8 value) {
93 FastPoisonShadow(g->beg, g->size_with_redzone, value);
94 }
95
PoisonRedZones(const Global & g)96 ALWAYS_INLINE void PoisonRedZones(const Global &g) {
97 uptr aligned_size = RoundUpTo(g.size, ASAN_SHADOW_GRANULARITY);
98 FastPoisonShadow(g.beg + aligned_size, g.size_with_redzone - aligned_size,
99 kAsanGlobalRedzoneMagic);
100 if (g.size != aligned_size) {
101 FastPoisonShadowPartialRightRedzone(
102 g.beg + RoundDownTo(g.size, ASAN_SHADOW_GRANULARITY),
103 g.size % ASAN_SHADOW_GRANULARITY, ASAN_SHADOW_GRANULARITY,
104 kAsanGlobalRedzoneMagic);
105 }
106 }
107
108 const uptr kMinimalDistanceFromAnotherGlobal = 64;
109
AddGlobalToList(ListOfGlobals & list,const Global * g)110 static void AddGlobalToList(ListOfGlobals &list, const Global *g) {
111 list.push_front(new (GetGlobalLowLevelAllocator()) GlobalListNode{g});
112 }
113
UnpoisonDynamicGlobals(IntrusiveList<DynInitGlobal> & dyn_globals,bool mark_initialized)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, 0);
122 // Poison redzones back.
123 PoisonRedZones(*g);
124 if (mark_initialized)
125 dyn_g.initialized = true;
126 }
127 }
128
PoisonDynamicGlobals(const IntrusiveList<DynInitGlobal> & dyn_globals)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, kAsanInitializationOrderMagic);
136 }
137 }
138
IsAddressNearGlobal(uptr addr,const __asan_global & g)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
ReportGlobal(const Global & g,const char * prefix)145 static void ReportGlobal(const Global &g, const char *prefix) {
146 DataInfo info;
147 bool symbolized = Symbolizer::GetOrInit()->SymbolizeData(g.beg, &info);
148 Report(
149 "%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(" 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(" location: name=%s, %d\n", g.gcc_location->filename, g.gcc_location->line_no);
161 }
162 }
163
FindRegistrationSite(const Global * g)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
GetGlobalsForAddress(uptr addr,Global * globals,u32 * reg_sites,int max_globals)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, "Search");
184 if (IsAddressNearGlobal(addr, g)) {
185 internal_memcpy(&globals[res], &g, sizeof(g));
186 if (reg_sites)
187 reg_sites[res] = FindRegistrationSite(&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.
CheckODRViolationViaIndicator(const Global * g)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(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(g->name))
219 ReportODRViolation(g, FindRegistrationSite(g), l.g,
220 FindRegistrationSite(l.g));
221 }
222 } else { // UNREGISTERED
223 *odr_indicator = REGISTERED;
224 }
225
226 AddGlobalToList(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.
CheckODRViolationViaPoisoning(const Global * g)232 static void CheckODRViolationViaPoisoning(const Global *g)
233 SANITIZER_REQUIRES(mu_for_globals) {
234 if (__asan_region_is_poisoned(g->beg, 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(g->name)) {
241 ReportODRViolation(g, FindRegistrationSite(g), l.g,
242 FindRegistrationSite(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.
UseODRIndicator(const Global * g)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.
RegisterGlobal(const Global * g)271 static void RegisterGlobal(const Global *g) SANITIZER_REQUIRES(mu_for_globals) {
272 CHECK(AsanInited());
273 if (flags()->report_globals >= 2)
274 ReportGlobal(*g, "Added");
275 CHECK(flags()->report_globals);
276 CHECK(AddrIsInMem(g->beg));
277 if (!AddrIsAlignedByGranularity(g->beg)) {
278 Report("The following global variable is not properly aligned.\n");
279 Report("This may happen if another global with the same name\n");
280 Report("resides in another non-instrumented module.\n");
281 Report("Or the global comes from a C file built w/o -fno-common.\n");
282 Report("In either case this is likely an ODR violation bug,\n");
283 Report("but AddressSanitizer can not provide more details.\n");
284 ReportODRViolation(g, FindRegistrationSite(g), g, 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);
298
299 AddGlobalToList(list_of_all_globals, g);
300
301 if (g->has_dynamic_init) {
302 DynInitGlobals()[g->module_name].push_back(
303 new (GetGlobalLowLevelAllocator()) DynInitGlobal{*g, false});
304 }
305 }
306
UnregisterGlobal(const Global * g)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, "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, 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
StopInitOrderChecking()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([&](auto &kv) {
335 UnpoisonDynamicGlobals(kv.second, /*mark_initialized=*/false);
336 return true;
337 });
338 }
339
IsASCII(unsigned char c)340 static bool IsASCII(unsigned char c) { return /*0x00 <= c &&*/ c <= 0x7F; }
341
MaybeDemangleGlobalName(const char * name)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.
PrintGlobalNameIfASCII(InternalScopedString * str,const __asan_global & g)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(" '%s' is ascii string '%s'\n", MaybeDemangleGlobalName(g.name),
362 (char *)g.beg);
363 }
364
PrintGlobalLocation(InternalScopedString * str,const __asan_global & g,bool print_module_name)365 void PrintGlobalLocation(InternalScopedString *str, const __asan_global &g,
366 bool print_module_name) {
367 DataInfo info;
368 if (Symbolizer::GetOrInit()->SymbolizeData(g.beg, &info) && info.line != 0) {
369 str->AppendF("%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("%s", g.gcc_location->filename ? g.gcc_location->filename
373 : g.module_name);
374 if (g.gcc_location->line_no)
375 str->AppendF(":%d", g.gcc_location->line_no);
376 if (g.gcc_location->column_no)
377 str->AppendF(":%d", g.gcc_location->column_no);
378 } else {
379 str->AppendF("%s", g.module_name);
380 }
381 if (print_module_name && info.module)
382 str->AppendF(" 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.
__asan_register_image_globals(uptr * flag)393 void __asan_register_image_globals(uptr *flag) {
394 if (*flag)
395 return;
396 AsanApplyToGlobals(__asan_register_globals, flag);
397 *flag = 1;
398 }
399
400 // This mirrors __asan_register_image_globals.
__asan_unregister_image_globals(uptr * flag)401 void __asan_unregister_image_globals(uptr *flag) {
402 if (!*flag)
403 return;
404 AsanApplyToGlobals(__asan_unregister_globals, flag);
405 *flag = 0;
406 }
407
__asan_register_elf_globals(uptr * flag,void * start,void * stop)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_start, globals_stop - globals_start);
415 *flag = 1;
416 }
417
__asan_unregister_elf_globals(uptr * flag,void * start,void * stop)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_start, globals_stop - globals_start);
425 *flag = 0;
426 }
427
428 // Register an array of globals.
__asan_register_globals(__asan_global * globals,uptr n)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(128);
438 }
439 GlobalRegistrationSite site = {stack_id, &globals[0], &globals[n - 1]};
440 global_registration_site_vector->push_back(site);
441 if (flags()->report_globals >= 2) {
442 PRINT_CURRENT_STACK();
443 Printf("=== 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(&globals[i]);
462 }
463
464 // Poison the metadata. It should not be accessible to user code.
465 PoisonShadow(reinterpret_cast<uptr>(globals), n * sizeof(__asan_global),
466 kAsanGlobalRedzoneMagic);
467 }
468
469 // Unregister an array of globals.
470 // We must do this when a shared objects gets dlclosed.
__asan_unregister_globals(__asan_global * globals,uptr n)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(&globals[i]);
481 }
482
483 // Unpoison the metadata.
484 PoisonShadow(reinterpret_cast<uptr>(globals), n * sizeof(__asan_global), 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.
__asan_before_dynamic_init(const char * module_name)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("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([&](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(DynInitGlobals()[current_dynamic_init_module_name]);
517 UnpoisonDynamicGlobals(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
UnpoisonBeforeMain(void)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("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.
__asan_after_dynamic_init()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("DynInitUnpoison\n");
575
576 DynInitGlobals().forEach([&](auto &kv) {
577 UnpoisonDynamicGlobals(kv.second, /*mark_initialized=*/false);
578 return true;
579 });
580
581 current_dynamic_init_module_name = nullptr;
582 }
583