xref: /llvm-project/compiler-rt/lib/asan/asan_globals.cpp (revision e185850ce735ade5924129bec56a5954c443cf17)
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(g->beg, g->size_with_redzone, value);
94 }
95 
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 
110 static void AddGlobalToList(ListOfGlobals &list, const Global *g) {
111   list.push_front(new (GetGlobalLowLevelAllocator()) GlobalListNode{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, 0);
122     // Poison redzones back.
123     PoisonRedZones(*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, 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(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 
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, "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.
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.
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.
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, "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 
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 
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 
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("  '%s' is ascii string '%s'\n", MaybeDemangleGlobalName(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(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.
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.
401 void __asan_unregister_image_globals(uptr *flag) {
402   if (!*flag)
403     return;
404   AsanApplyToGlobals(__asan_unregister_globals, 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_start, 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_start, 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(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.
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.
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 
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.
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