Path: blob/main/contrib/llvm-project/compiler-rt/lib/lsan/lsan_common_linux.cpp
35233 views
//=-- lsan_common_linux.cpp -----------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file is a part of LeakSanitizer.9// Implementation of common leak checking functionality. Linux/NetBSD-specific10// code.11//12//===----------------------------------------------------------------------===//1314#include "sanitizer_common/sanitizer_platform.h"15#include "lsan_common.h"1617#if CAN_SANITIZE_LEAKS && (SANITIZER_LINUX || SANITIZER_NETBSD)18#include <link.h>1920#include "sanitizer_common/sanitizer_common.h"21#include "sanitizer_common/sanitizer_flags.h"22#include "sanitizer_common/sanitizer_getauxval.h"23#include "sanitizer_common/sanitizer_linux.h"24#include "sanitizer_common/sanitizer_stackdepot.h"2526namespace __lsan {2728static const char kLinkerName[] = "ld";2930alignas(64) static char linker_placeholder[sizeof(LoadedModule)];31static LoadedModule *linker = nullptr;3233static bool IsLinker(const LoadedModule& module) {34#if SANITIZER_USE_GETAUXVAL35return module.base_address() == getauxval(AT_BASE);36#else37return LibraryNameIs(module.full_name(), kLinkerName);38#endif // SANITIZER_USE_GETAUXVAL39}4041__attribute__((tls_model("initial-exec")))42THREADLOCAL int disable_counter;43bool DisabledInThisThread() { return disable_counter > 0; }44void DisableInThisThread() { disable_counter++; }45void EnableInThisThread() {46if (disable_counter == 0) {47DisableCounterUnderflow();48}49disable_counter--;50}5152void InitializePlatformSpecificModules() {53ListOfModules modules;54modules.init();55for (LoadedModule &module : modules) {56if (!IsLinker(module))57continue;58if (linker == nullptr) {59linker = reinterpret_cast<LoadedModule *>(linker_placeholder);60*linker = module;61module = LoadedModule();62} else {63VReport(1, "LeakSanitizer: Multiple modules match \"%s\". "64"TLS and other allocations originating from linker might be "65"falsely reported as leaks.\n", kLinkerName);66linker->clear();67linker = nullptr;68return;69}70}71if (linker == nullptr) {72VReport(1, "LeakSanitizer: Dynamic linker not found. TLS and other "73"allocations originating from linker might be falsely reported "74"as leaks.\n");75}76}7778static int ProcessGlobalRegionsCallback(struct dl_phdr_info *info, size_t size,79void *data) {80Frontier *frontier = reinterpret_cast<Frontier *>(data);81for (uptr j = 0; j < info->dlpi_phnum; j++) {82const ElfW(Phdr) *phdr = &(info->dlpi_phdr[j]);83// We're looking for .data and .bss sections, which reside in writeable,84// loadable segments.85if (!(phdr->p_flags & PF_W) || (phdr->p_type != PT_LOAD) ||86(phdr->p_memsz == 0))87continue;88uptr begin = info->dlpi_addr + phdr->p_vaddr;89uptr end = begin + phdr->p_memsz;90ScanGlobalRange(begin, end, frontier);91}92return 0;93}9495#if SANITIZER_ANDROID && __ANDROID_API__ < 2196extern "C" __attribute__((weak)) int dl_iterate_phdr(97int (*)(struct dl_phdr_info *, size_t, void *), void *);98#endif99100// Scans global variables for heap pointers.101void ProcessGlobalRegions(Frontier *frontier) {102if (!flags()->use_globals) return;103dl_iterate_phdr(ProcessGlobalRegionsCallback, frontier);104}105106LoadedModule *GetLinker() { return linker; }107108void ProcessPlatformSpecificAllocations(Frontier *frontier) {}109110struct DoStopTheWorldParam {111StopTheWorldCallback callback;112void *argument;113};114115// While calling Die() here is undefined behavior and can potentially116// cause race conditions, it isn't possible to intercept exit on linux,117// so we have no choice but to call Die() from the atexit handler.118void HandleLeaks() {119if (common_flags()->exitcode) Die();120}121122static int LockStuffAndStopTheWorldCallback(struct dl_phdr_info *info,123size_t size, void *data) {124ScopedStopTheWorldLock lock;125DoStopTheWorldParam *param = reinterpret_cast<DoStopTheWorldParam *>(data);126StopTheWorld(param->callback, param->argument);127return 1;128}129130// LSan calls dl_iterate_phdr() from the tracer task. This may deadlock: if one131// of the threads is frozen while holding the libdl lock, the tracer will hang132// in dl_iterate_phdr() forever.133// Luckily, (a) the lock is reentrant and (b) libc can't distinguish between the134// tracer task and the thread that spawned it. Thus, if we run the tracer task135// while holding the libdl lock in the parent thread, we can safely reenter it136// in the tracer. The solution is to run stoptheworld from a dl_iterate_phdr()137// callback in the parent thread.138void LockStuffAndStopTheWorld(StopTheWorldCallback callback,139CheckForLeaksParam *argument) {140DoStopTheWorldParam param = {callback, argument};141dl_iterate_phdr(LockStuffAndStopTheWorldCallback, ¶m);142}143144} // namespace __lsan145146#endif147148149