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