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
27namespace __lsan {
28
29static const char kLinkerName[] = "ld";
30
31static char linker_placeholder[sizeof(LoadedModule)] ALIGNED(64);
32static LoadedModule *linker = nullptr;
33
34static 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")))
43THREADLOCAL int disable_counter;
44bool DisabledInThisThread() { return disable_counter > 0; }
45void DisableInThisThread() { disable_counter++; }
46void EnableInThisThread() {
47  if (disable_counter == 0) {
48    DisableCounterUnderflow();
49  }
50  disable_counter--;
51}
52
53void 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
79static 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
97extern "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.
102void ProcessGlobalRegions(Frontier *frontier) {
103  if (!flags()->use_globals) return;
104  dl_iterate_phdr(ProcessGlobalRegionsCallback, frontier);
105}
106
107LoadedModule *GetLinker() { return linker; }
108
109void ProcessPlatformSpecificAllocations(Frontier *frontier) {}
110
111struct 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.
119void HandleLeaks() {
120  if (common_flags()->exitcode) Die();
121}
122
123static 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.
139void 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