xref: /freebsd/contrib/llvm-project/compiler-rt/lib/lsan/lsan_common_linux.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
168d75effSDimitry Andric //=-- lsan_common_linux.cpp -----------------------------------------------===//
268d75effSDimitry Andric //
368d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
468d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
568d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
668d75effSDimitry Andric //
768d75effSDimitry Andric //===----------------------------------------------------------------------===//
868d75effSDimitry Andric //
968d75effSDimitry Andric // This file is a part of LeakSanitizer.
1068d75effSDimitry Andric // Implementation of common leak checking functionality. Linux/NetBSD-specific
1168d75effSDimitry Andric // code.
1268d75effSDimitry Andric //
1368d75effSDimitry Andric //===----------------------------------------------------------------------===//
1468d75effSDimitry Andric 
1568d75effSDimitry Andric #include "sanitizer_common/sanitizer_platform.h"
1668d75effSDimitry Andric #include "lsan_common.h"
1768d75effSDimitry Andric 
1868d75effSDimitry Andric #if CAN_SANITIZE_LEAKS && (SANITIZER_LINUX || SANITIZER_NETBSD)
1968d75effSDimitry Andric #include <link.h>
2068d75effSDimitry Andric 
2168d75effSDimitry Andric #include "sanitizer_common/sanitizer_common.h"
2268d75effSDimitry Andric #include "sanitizer_common/sanitizer_flags.h"
2368d75effSDimitry Andric #include "sanitizer_common/sanitizer_getauxval.h"
2468d75effSDimitry Andric #include "sanitizer_common/sanitizer_linux.h"
2568d75effSDimitry Andric #include "sanitizer_common/sanitizer_stackdepot.h"
2668d75effSDimitry Andric 
2768d75effSDimitry Andric namespace __lsan {
2868d75effSDimitry Andric 
2968d75effSDimitry Andric static const char kLinkerName[] = "ld";
3068d75effSDimitry Andric 
31*0fca6ea1SDimitry Andric alignas(64) static char linker_placeholder[sizeof(LoadedModule)];
3268d75effSDimitry Andric static LoadedModule *linker = nullptr;
3368d75effSDimitry Andric 
IsLinker(const LoadedModule & module)3468d75effSDimitry Andric static bool IsLinker(const LoadedModule& module) {
3568d75effSDimitry Andric #if SANITIZER_USE_GETAUXVAL
3668d75effSDimitry Andric   return module.base_address() == getauxval(AT_BASE);
3768d75effSDimitry Andric #else
3868d75effSDimitry Andric   return LibraryNameIs(module.full_name(), kLinkerName);
3968d75effSDimitry Andric #endif  // SANITIZER_USE_GETAUXVAL
4068d75effSDimitry Andric }
4168d75effSDimitry Andric 
4268d75effSDimitry Andric __attribute__((tls_model("initial-exec")))
4368d75effSDimitry Andric THREADLOCAL int disable_counter;
DisabledInThisThread()4468d75effSDimitry Andric bool DisabledInThisThread() { return disable_counter > 0; }
DisableInThisThread()4568d75effSDimitry Andric void DisableInThisThread() { disable_counter++; }
EnableInThisThread()4668d75effSDimitry Andric void EnableInThisThread() {
4768d75effSDimitry Andric   if (disable_counter == 0) {
4868d75effSDimitry Andric     DisableCounterUnderflow();
4968d75effSDimitry Andric   }
5068d75effSDimitry Andric   disable_counter--;
5168d75effSDimitry Andric }
5268d75effSDimitry Andric 
InitializePlatformSpecificModules()5368d75effSDimitry Andric void InitializePlatformSpecificModules() {
5468d75effSDimitry Andric   ListOfModules modules;
5568d75effSDimitry Andric   modules.init();
5668d75effSDimitry Andric   for (LoadedModule &module : modules) {
5768d75effSDimitry Andric     if (!IsLinker(module))
5868d75effSDimitry Andric       continue;
5968d75effSDimitry Andric     if (linker == nullptr) {
6068d75effSDimitry Andric       linker = reinterpret_cast<LoadedModule *>(linker_placeholder);
6168d75effSDimitry Andric       *linker = module;
6268d75effSDimitry Andric       module = LoadedModule();
6368d75effSDimitry Andric     } else {
6468d75effSDimitry Andric       VReport(1, "LeakSanitizer: Multiple modules match \"%s\". "
6568d75effSDimitry Andric               "TLS and other allocations originating from linker might be "
6668d75effSDimitry Andric               "falsely reported as leaks.\n", kLinkerName);
6768d75effSDimitry Andric       linker->clear();
6868d75effSDimitry Andric       linker = nullptr;
6968d75effSDimitry Andric       return;
7068d75effSDimitry Andric     }
7168d75effSDimitry Andric   }
7268d75effSDimitry Andric   if (linker == nullptr) {
7368d75effSDimitry Andric     VReport(1, "LeakSanitizer: Dynamic linker not found. TLS and other "
7468d75effSDimitry Andric                "allocations originating from linker might be falsely reported "
7568d75effSDimitry Andric                 "as leaks.\n");
7668d75effSDimitry Andric   }
7768d75effSDimitry Andric }
7868d75effSDimitry Andric 
ProcessGlobalRegionsCallback(struct dl_phdr_info * info,size_t size,void * data)7968d75effSDimitry Andric static int ProcessGlobalRegionsCallback(struct dl_phdr_info *info, size_t size,
8068d75effSDimitry Andric                                         void *data) {
8168d75effSDimitry Andric   Frontier *frontier = reinterpret_cast<Frontier *>(data);
8268d75effSDimitry Andric   for (uptr j = 0; j < info->dlpi_phnum; j++) {
8368d75effSDimitry Andric     const ElfW(Phdr) *phdr = &(info->dlpi_phdr[j]);
8468d75effSDimitry Andric     // We're looking for .data and .bss sections, which reside in writeable,
8568d75effSDimitry Andric     // loadable segments.
8668d75effSDimitry Andric     if (!(phdr->p_flags & PF_W) || (phdr->p_type != PT_LOAD) ||
8768d75effSDimitry Andric         (phdr->p_memsz == 0))
8868d75effSDimitry Andric       continue;
8968d75effSDimitry Andric     uptr begin = info->dlpi_addr + phdr->p_vaddr;
9068d75effSDimitry Andric     uptr end = begin + phdr->p_memsz;
9168d75effSDimitry Andric     ScanGlobalRange(begin, end, frontier);
9268d75effSDimitry Andric   }
9368d75effSDimitry Andric   return 0;
9468d75effSDimitry Andric }
9568d75effSDimitry Andric 
96e8d8bef9SDimitry Andric #if SANITIZER_ANDROID && __ANDROID_API__ < 21
97e8d8bef9SDimitry Andric extern "C" __attribute__((weak)) int dl_iterate_phdr(
98e8d8bef9SDimitry Andric     int (*)(struct dl_phdr_info *, size_t, void *), void *);
99e8d8bef9SDimitry Andric #endif
100e8d8bef9SDimitry Andric 
10168d75effSDimitry Andric // Scans global variables for heap pointers.
ProcessGlobalRegions(Frontier * frontier)10268d75effSDimitry Andric void ProcessGlobalRegions(Frontier *frontier) {
10368d75effSDimitry Andric   if (!flags()->use_globals) return;
10468d75effSDimitry Andric   dl_iterate_phdr(ProcessGlobalRegionsCallback, frontier);
10568d75effSDimitry Andric }
10668d75effSDimitry Andric 
GetLinker()10768d75effSDimitry Andric LoadedModule *GetLinker() { return linker; }
10868d75effSDimitry Andric 
ProcessPlatformSpecificAllocations(Frontier * frontier)10968d75effSDimitry Andric void ProcessPlatformSpecificAllocations(Frontier *frontier) {}
11068d75effSDimitry Andric 
11168d75effSDimitry Andric struct DoStopTheWorldParam {
11268d75effSDimitry Andric   StopTheWorldCallback callback;
11368d75effSDimitry Andric   void *argument;
11468d75effSDimitry Andric };
11568d75effSDimitry Andric 
11668d75effSDimitry Andric // While calling Die() here is undefined behavior and can potentially
11768d75effSDimitry Andric // cause race conditions, it isn't possible to intercept exit on linux,
11868d75effSDimitry Andric // so we have no choice but to call Die() from the atexit handler.
HandleLeaks()11968d75effSDimitry Andric void HandleLeaks() {
12068d75effSDimitry Andric   if (common_flags()->exitcode) Die();
12168d75effSDimitry Andric }
12268d75effSDimitry Andric 
LockStuffAndStopTheWorldCallback(struct dl_phdr_info * info,size_t size,void * data)12368d75effSDimitry Andric static int LockStuffAndStopTheWorldCallback(struct dl_phdr_info *info,
12468d75effSDimitry Andric                                             size_t size, void *data) {
1250eae32dcSDimitry Andric   ScopedStopTheWorldLock lock;
12668d75effSDimitry Andric   DoStopTheWorldParam *param = reinterpret_cast<DoStopTheWorldParam *>(data);
12768d75effSDimitry Andric   StopTheWorld(param->callback, param->argument);
12868d75effSDimitry Andric   return 1;
12968d75effSDimitry Andric }
13068d75effSDimitry Andric 
13168d75effSDimitry Andric // LSan calls dl_iterate_phdr() from the tracer task. This may deadlock: if one
13268d75effSDimitry Andric // of the threads is frozen while holding the libdl lock, the tracer will hang
13368d75effSDimitry Andric // in dl_iterate_phdr() forever.
13468d75effSDimitry Andric // Luckily, (a) the lock is reentrant and (b) libc can't distinguish between the
13568d75effSDimitry Andric // tracer task and the thread that spawned it. Thus, if we run the tracer task
13668d75effSDimitry Andric // while holding the libdl lock in the parent thread, we can safely reenter it
13768d75effSDimitry Andric // in the tracer. The solution is to run stoptheworld from a dl_iterate_phdr()
13868d75effSDimitry Andric // callback in the parent thread.
LockStuffAndStopTheWorld(StopTheWorldCallback callback,CheckForLeaksParam * argument)1395ffd83dbSDimitry Andric void LockStuffAndStopTheWorld(StopTheWorldCallback callback,
1405ffd83dbSDimitry Andric                               CheckForLeaksParam *argument) {
14168d75effSDimitry Andric   DoStopTheWorldParam param = {callback, argument};
14268d75effSDimitry Andric   dl_iterate_phdr(LockStuffAndStopTheWorldCallback, &param);
14368d75effSDimitry Andric }
14468d75effSDimitry Andric 
14568d75effSDimitry Andric } // namespace __lsan
14668d75effSDimitry Andric 
14768d75effSDimitry Andric #endif
148