xref: /netbsd-src/external/gpl3/gcc.old/dist/libsanitizer/lsan/lsan_common_linux.cc (revision 122b5006ee1bd67145794b4cde92f4fe4781a5ec)
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, &param);
134 }
135 
136 } // namespace __lsan
137 
138 #endif // CAN_SANITIZE_LEAKS && (SANITIZER_LINUX || SANITIZER_NETBSD)
139