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, ¶m);
14368d75effSDimitry Andric }
14468d75effSDimitry Andric
14568d75effSDimitry Andric } // namespace __lsan
14668d75effSDimitry Andric
14768d75effSDimitry Andric #endif
148