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 | |
27 | namespace __lsan { |
28 | |
29 | static const char kLinkerName[] = "ld" ; |
30 | |
31 | alignas(64) static char linker_placeholder[sizeof(LoadedModule)]; |
32 | static LoadedModule *linker = nullptr; |
33 | |
34 | static 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" ))) |
43 | THREADLOCAL int disable_counter; |
44 | bool DisabledInThisThread() { return disable_counter > 0; } |
45 | void DisableInThisThread() { disable_counter++; } |
46 | void EnableInThisThread() { |
47 | if (disable_counter == 0) { |
48 | DisableCounterUnderflow(); |
49 | } |
50 | disable_counter--; |
51 | } |
52 | |
53 | void 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 | |
79 | static 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 |
97 | extern "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. |
102 | void ProcessGlobalRegions(Frontier *frontier) { |
103 | if (!flags()->use_globals) return; |
104 | dl_iterate_phdr(callback: ProcessGlobalRegionsCallback, data: frontier); |
105 | } |
106 | |
107 | LoadedModule *GetLinker() { return linker; } |
108 | |
109 | void ProcessPlatformSpecificAllocations(Frontier *frontier) {} |
110 | |
111 | struct 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. |
119 | void HandleLeaks() { |
120 | if (common_flags()->exitcode) Die(); |
121 | } |
122 | |
123 | static 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(callback: param->callback, argument: 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. |
139 | void LockStuffAndStopTheWorld(StopTheWorldCallback callback, |
140 | CheckForLeaksParam *argument) { |
141 | DoStopTheWorldParam param = {.callback: callback, .argument: argument}; |
142 | dl_iterate_phdr(callback: LockStuffAndStopTheWorldCallback, data: ¶m); |
143 | } |
144 | |
145 | } // namespace __lsan |
146 | |
147 | #endif |
148 | |