xref: /freebsd/contrib/llvm-project/compiler-rt/lib/lsan/lsan_common_linux.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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 
IsLinker(const LoadedModule & module)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;
DisabledInThisThread()44 bool DisabledInThisThread() { return disable_counter > 0; }
DisableInThisThread()45 void DisableInThisThread() { disable_counter++; }
EnableInThisThread()46 void EnableInThisThread() {
47   if (disable_counter == 0) {
48     DisableCounterUnderflow();
49   }
50   disable_counter--;
51 }
52 
InitializePlatformSpecificModules()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 
ProcessGlobalRegionsCallback(struct dl_phdr_info * info,size_t size,void * data)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.
ProcessGlobalRegions(Frontier * frontier)102 void ProcessGlobalRegions(Frontier *frontier) {
103   if (!flags()->use_globals) return;
104   dl_iterate_phdr(ProcessGlobalRegionsCallback, frontier);
105 }
106 
GetLinker()107 LoadedModule *GetLinker() { return linker; }
108 
ProcessPlatformSpecificAllocations(Frontier * frontier)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.
HandleLeaks()119 void HandleLeaks() {
120   if (common_flags()->exitcode) Die();
121 }
122 
LockStuffAndStopTheWorldCallback(struct dl_phdr_info * info,size_t size,void * data)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(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.
LockStuffAndStopTheWorld(StopTheWorldCallback callback,CheckForLeaksParam * argument)139 void LockStuffAndStopTheWorld(StopTheWorldCallback callback,
140                               CheckForLeaksParam *argument) {
141   DoStopTheWorldParam param = {callback, argument};
142   dl_iterate_phdr(LockStuffAndStopTheWorldCallback, &param);
143 }
144 
145 } // namespace __lsan
146 
147 #endif
148