//===-- asan_globals.cpp --------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file is a part of AddressSanitizer, an address sanity checker. // // Handle globals. //===----------------------------------------------------------------------===// #include "asan_interceptors.h" #include "asan_internal.h" #include "asan_mapping.h" #include "asan_poisoning.h" #include "asan_report.h" #include "asan_stack.h" #include "asan_stats.h" #include "asan_suppressions.h" #include "asan_thread.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_dense_map.h" #include "sanitizer_common/sanitizer_list.h" #include "sanitizer_common/sanitizer_mutex.h" #include "sanitizer_common/sanitizer_placement_new.h" #include "sanitizer_common/sanitizer_stackdepot.h" #include "sanitizer_common/sanitizer_symbolizer.h" #include "sanitizer_common/sanitizer_thread_safety.h" namespace __asan { Global; struct GlobalListNode { … }; ListOfGlobals; static Mutex mu_for_globals; static ListOfGlobals list_of_all_globals SANITIZER_GUARDED_BY(…) …; struct DynInitGlobal { … }; // We want to remember where a certain range of globals was registered. struct GlobalRegistrationSite { … }; GlobalRegistrationSiteVector; static GlobalRegistrationSiteVector *global_registration_site_vector; static ListOfGlobals &GlobalsByIndicator(uptr odr_indicator) SANITIZER_REQUIRES(mu_for_globals) { … } static const char *current_dynamic_init_module_name SANITIZER_GUARDED_BY(mu_for_globals) = …; DynInitGlobalsByModule; // TODO: Add a NoDestroy helper, this patter is very common in sanitizers. static DynInitGlobalsByModule &DynInitGlobals() SANITIZER_REQUIRES(mu_for_globals) { … } ALWAYS_INLINE void PoisonShadowForGlobal(const Global *g, u8 value) { … } ALWAYS_INLINE void PoisonRedZones(const Global &g) { … } const uptr kMinimalDistanceFromAnotherGlobal = …; static void AddGlobalToList(ListOfGlobals &list, const Global *g) { … } static void UnpoisonDynamicGlobals(IntrusiveList<DynInitGlobal> &dyn_globals, bool mark_initialized) { … } static void PoisonDynamicGlobals( const IntrusiveList<DynInitGlobal> &dyn_globals) { … } static bool IsAddressNearGlobal(uptr addr, const __asan_global &g) { … } static void ReportGlobal(const Global &g, const char *prefix) { … } static u32 FindRegistrationSite(const Global *g) { … } int GetGlobalsForAddress(uptr addr, Global *globals, u32 *reg_sites, int max_globals) { … } enum GlobalSymbolState { … }; // Check ODR violation for given global G via special ODR indicator. We use // this method in case compiler instruments global variables through their // local aliases. static void CheckODRViolationViaIndicator(const Global *g) SANITIZER_REQUIRES(mu_for_globals) { … } // Check ODR violation for given global G by checking if it's already poisoned. // We use this method in case compiler doesn't use private aliases for global // variables. static void CheckODRViolationViaPoisoning(const Global *g) SANITIZER_REQUIRES(mu_for_globals) { … } // Clang provides two different ways for global variables protection: // it can poison the global itself or its private alias. In former // case we may poison same symbol multiple times, that can help us to // cheaply detect ODR violation: if we try to poison an already poisoned // global, we have ODR violation error. // In latter case, we poison each symbol exactly once, so we use special // indicator symbol to perform similar check. // In either case, compiler provides a special odr_indicator field to Global // structure, that can contain two kinds of values: // 1) Non-zero value. In this case, odr_indicator is an address of // corresponding indicator variable for given global. // 2) Zero. This means that we don't use private aliases for global variables // and can freely check ODR violation with the first method. // // This routine chooses between two different methods of ODR violation // detection. static inline bool UseODRIndicator(const Global *g) { … } // Register a global variable. // This function may be called more than once for every global // so we store the globals in a map. static void RegisterGlobal(const Global *g) SANITIZER_REQUIRES(mu_for_globals) { … } static void UnregisterGlobal(const Global *g) SANITIZER_REQUIRES(mu_for_globals) { … } void StopInitOrderChecking() { … } static bool IsASCII(unsigned char c) { … } const char *MaybeDemangleGlobalName(const char *name) { … } // Check if the global is a zero-terminated ASCII string. If so, print it. void PrintGlobalNameIfASCII(InternalScopedString *str, const __asan_global &g) { … } void PrintGlobalLocation(InternalScopedString *str, const __asan_global &g, bool print_module_name) { … } } // namespace __asan // ---------------------- Interface ---------------- {{{1 usingnamespace__asan; // Apply __asan_register_globals to all globals found in the same loaded // executable or shared library as `flag'. The flag tracks whether globals have // already been registered or not for this image. void __asan_register_image_globals(uptr *flag) { … } // This mirrors __asan_register_image_globals. void __asan_unregister_image_globals(uptr *flag) { … } void __asan_register_elf_globals(uptr *flag, void *start, void *stop) { … } void __asan_unregister_elf_globals(uptr *flag, void *start, void *stop) { … } // Register an array of globals. void __asan_register_globals(__asan_global *globals, uptr n) { … } // Unregister an array of globals. // We must do this when a shared objects gets dlclosed. void __asan_unregister_globals(__asan_global *globals, uptr n) { … } // This method runs immediately prior to dynamic initialization in each TU, // when all dynamically initialized globals are unpoisoned. This method // poisons all global variables not defined in this TU, so that a dynamic // initializer can only touch global variables in the same TU. void __asan_before_dynamic_init(const char *module_name) { … } // Maybe SANITIZER_CAN_USE_PREINIT_ARRAY is to conservative for `.init_array`, // however we should not make mistake here. If `UnpoisonBeforeMain` was not // executed at all we will have false reports on globals. #if SANITIZER_CAN_USE_PREINIT_ARRAY // This optimization aims to reduce the overhead of `__asan_after_dynamic_init` // calls by leveraging incremental unpoisoning/poisoning in // `__asan_before_dynamic_init`. We expect most `__asan_after_dynamic_init // calls` to be no-ops. However, to ensure all globals are unpoisoned before the // `main`, we force `UnpoisonBeforeMain` to fully execute // `__asan_after_dynamic_init`. // With lld, `UnpoisonBeforeMain` runs after standard `.init_array`, making it // the final `__asan_after_dynamic_init` call for the static runtime. In // contrast, GNU ld executes it earlier, causing subsequent // `__asan_after_dynamic_init` calls to perform full unpoisoning, losing the // optimization. bool allow_after_dynamic_init SANITIZER_GUARDED_BY(mu_for_globals) = …; static void UnpoisonBeforeMain(void) { … } __attribute__((section(".init_array.65537"), used)) static void ( *asan_after_init_array)(void) = …; #else // Incremental poisoning is disabled, unpoison globals immediately. static constexpr bool allow_after_dynamic_init = true; #endif // SANITIZER_CAN_USE_PREINIT_ARRAY // This method runs immediately after dynamic initialization in each TU, when // all dynamically initialized globals except for those defined in the current // TU are poisoned. It simply unpoisons all dynamically initialized globals. void __asan_after_dynamic_init() { … }