1//=-- lsan_common_linux.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 LeakSanitizer. 10// Implementation of common leak checking functionality. Linux/NetBSD-specific 11// code. 12// 13//===----------------------------------------------------------------------===// 14 15#include "sanitizer_common/sanitizer_platform.h" 16#include "lsan_common.h" 17 18#if CAN_SANITIZE_LEAKS && (SANITIZER_LINUX || SANITIZER_NETBSD) 19#include <link.h> 20 21#include "sanitizer_common/sanitizer_common.h" 22#include "sanitizer_common/sanitizer_flags.h" 23#include "sanitizer_common/sanitizer_getauxval.h" 24#include "sanitizer_common/sanitizer_linux.h" 25#include "sanitizer_common/sanitizer_stackdepot.h" 26 27namespace __lsan { 28 29static const char kLinkerName[] = "ld"; 30 31static char linker_placeholder[sizeof(LoadedModule)] ALIGNED(64); 32static LoadedModule *linker = nullptr; 33 34static bool IsLinker(const LoadedModule& module) { 35#if SANITIZER_USE_GETAUXVAL 36 return module.base_address() == getauxval(AT_BASE); 37#else 38 return LibraryNameIs(module.full_name(), kLinkerName); 39#endif // SANITIZER_USE_GETAUXVAL 40} 41 42__attribute__((tls_model("initial-exec"))) 43THREADLOCAL int disable_counter; 44bool DisabledInThisThread() { return disable_counter > 0; } 45void DisableInThisThread() { disable_counter++; } 46void EnableInThisThread() { 47 if (disable_counter == 0) { 48 DisableCounterUnderflow(); 49 } 50 disable_counter--; 51} 52 53void InitializePlatformSpecificModules() { 54 ListOfModules modules; 55 modules.init(); 56 for (LoadedModule &module : modules) { 57 if (!IsLinker(module)) 58 continue; 59 if (linker == nullptr) { 60 linker = reinterpret_cast<LoadedModule *>(linker_placeholder); 61 *linker = module; 62 module = LoadedModule(); 63 } else { 64 VReport(1, "LeakSanitizer: Multiple modules match \"%s\". " 65 "TLS and other allocations originating from linker might be " 66 "falsely reported as leaks.\n", kLinkerName); 67 linker->clear(); 68 linker = nullptr; 69 return; 70 } 71 } 72 if (linker == nullptr) { 73 VReport(1, "LeakSanitizer: Dynamic linker not found. TLS and other " 74 "allocations originating from linker might be falsely reported " 75 "as leaks.\n"); 76 } 77} 78 79static int ProcessGlobalRegionsCallback(struct dl_phdr_info *info, size_t size, 80 void *data) { 81 Frontier *frontier = reinterpret_cast<Frontier *>(data); 82 for (uptr j = 0; j < info->dlpi_phnum; j++) { 83 const ElfW(Phdr) *phdr = &(info->dlpi_phdr[j]); 84 // We're looking for .data and .bss sections, which reside in writeable, 85 // loadable segments. 86 if (!(phdr->p_flags & PF_W) || (phdr->p_type != PT_LOAD) || 87 (phdr->p_memsz == 0)) 88 continue; 89 uptr begin = info->dlpi_addr + phdr->p_vaddr; 90 uptr end = begin + phdr->p_memsz; 91 ScanGlobalRange(begin, end, frontier); 92 } 93 return 0; 94} 95 96#if SANITIZER_ANDROID && __ANDROID_API__ < 21 97extern "C" __attribute__((weak)) int dl_iterate_phdr( 98 int (*)(struct dl_phdr_info *, size_t, void *), void *); 99#endif 100 101// Scans global variables for heap pointers. 102void ProcessGlobalRegions(Frontier *frontier) { 103 if (!flags()->use_globals) return; 104 dl_iterate_phdr(ProcessGlobalRegionsCallback, frontier); 105} 106 107LoadedModule *GetLinker() { return linker; } 108 109void ProcessPlatformSpecificAllocations(Frontier *frontier) {} 110 111struct DoStopTheWorldParam { 112 StopTheWorldCallback callback; 113 void *argument; 114}; 115 116// While calling Die() here is undefined behavior and can potentially 117// cause race conditions, it isn't possible to intercept exit on linux, 118// so we have no choice but to call Die() from the atexit handler. 119void HandleLeaks() { 120 if (common_flags()->exitcode) Die(); 121} 122 123static int LockStuffAndStopTheWorldCallback(struct dl_phdr_info *info, 124 size_t size, void *data) { 125 ScopedStopTheWorldLock lock; 126 DoStopTheWorldParam *param = reinterpret_cast<DoStopTheWorldParam *>(data); 127 StopTheWorld(param->callback, param->argument); 128 return 1; 129} 130 131// LSan calls dl_iterate_phdr() from the tracer task. This may deadlock: if one 132// of the threads is frozen while holding the libdl lock, the tracer will hang 133// in dl_iterate_phdr() forever. 134// Luckily, (a) the lock is reentrant and (b) libc can't distinguish between the 135// tracer task and the thread that spawned it. Thus, if we run the tracer task 136// while holding the libdl lock in the parent thread, we can safely reenter it 137// in the tracer. The solution is to run stoptheworld from a dl_iterate_phdr() 138// callback in the parent thread. 139void LockStuffAndStopTheWorld(StopTheWorldCallback callback, 140 CheckForLeaksParam *argument) { 141 DoStopTheWorldParam param = {callback, argument}; 142 dl_iterate_phdr(LockStuffAndStopTheWorldCallback, ¶m); 143} 144 145} // namespace __lsan 146 147#endif 148