brintos

brintos / llvm-project-archived public Read only

0
0
Text · 4.8 KiB · 6fd54bb Raw
143 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9// This file is a part of LeakSanitizer.10// Implementation of common leak checking functionality. Linux/NetBSD-specific11// 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 31alignas(64) static char linker_placeholder[sizeof(LoadedModule)];32static LoadedModule *linker = nullptr;33 34static bool IsLinker(const LoadedModule& module) {35#if SANITIZER_USE_GETAUXVAL36  return module.base_address() == getauxval(AT_BASE);37#else38  return LibraryNameIs(module.full_name(), kLinkerName);39#endif  // SANITIZER_USE_GETAUXVAL40}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// Scans global variables for heap pointers.97void ProcessGlobalRegions(Frontier *frontier) {98  if (!flags()->use_globals) return;99  dl_iterate_phdr(ProcessGlobalRegionsCallback, frontier);100}101 102LoadedModule *GetLinker() { return linker; }103 104void ProcessPlatformSpecificAllocations(Frontier *frontier) {}105 106struct DoStopTheWorldParam {107  StopTheWorldCallback callback;108  void *argument;109};110 111// While calling Die() here is undefined behavior and can potentially112// cause race conditions, it isn't possible to intercept exit on linux,113// so we have no choice but to call Die() from the atexit handler.114void HandleLeaks() {115  if (common_flags()->exitcode) Die();116}117 118static int LockStuffAndStopTheWorldCallback(struct dl_phdr_info *info,119                                            size_t size, void *data) {120  ScopedStopTheWorldLock lock;121  DoStopTheWorldParam *param = reinterpret_cast<DoStopTheWorldParam *>(data);122  StopTheWorld(param->callback, param->argument);123  return 1;124}125 126// LSan calls dl_iterate_phdr() from the tracer task. This may deadlock: if one127// of the threads is frozen while holding the libdl lock, the tracer will hang128// in dl_iterate_phdr() forever.129// Luckily, (a) the lock is reentrant and (b) libc can't distinguish between the130// tracer task and the thread that spawned it. Thus, if we run the tracer task131// while holding the libdl lock in the parent thread, we can safely reenter it132// in the tracer. The solution is to run stoptheworld from a dl_iterate_phdr()133// callback in the parent thread.134void LockStuffAndStopTheWorld(StopTheWorldCallback callback,135                              CheckForLeaksParam *argument) {136  DoStopTheWorldParam param = {callback, argument};137  dl_iterate_phdr(LockStuffAndStopTheWorldCallback, &param);138}139 140} // namespace __lsan141 142#endif143