Path: blob/main/contrib/llvm-project/compiler-rt/lib/lsan/lsan_common_fuchsia.cpp
35233 views
//=-- lsan_common_fuchsia.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. Fuchsia-specific code.10//11//===---------------------------------------------------------------------===//1213#include "lsan_common.h"14#include "lsan_thread.h"15#include "sanitizer_common/sanitizer_platform.h"1617#if CAN_SANITIZE_LEAKS && SANITIZER_FUCHSIA18#include <zircon/sanitizer.h>1920#include "lsan_allocator.h"21#include "sanitizer_common/sanitizer_flags.h"22#include "sanitizer_common/sanitizer_stoptheworld_fuchsia.h"23#include "sanitizer_common/sanitizer_thread_registry.h"2425// Ensure that the Zircon system ABI is linked in.26#pragma comment(lib, "zircon")2728namespace __lsan {2930void InitializePlatformSpecificModules() {}3132LoadedModule *GetLinker() { return nullptr; }3334__attribute__((tls_model("initial-exec"))) THREADLOCAL int disable_counter;35bool DisabledInThisThread() { return disable_counter > 0; }36void DisableInThisThread() { disable_counter++; }37void EnableInThisThread() {38if (disable_counter == 0) {39DisableCounterUnderflow();40}41disable_counter--;42}4344// There is nothing left to do after the globals callbacks.45void ProcessGlobalRegions(Frontier *frontier) {}4647// Nothing to do here.48void ProcessPlatformSpecificAllocations(Frontier *frontier) {}4950// On Fuchsia, we can intercept _Exit gracefully, and return a failing exit51// code if required at that point. Calling Die() here is undefined52// behavior and causes rare race conditions.53void HandleLeaks() {}5455// This is defined differently in asan_fuchsia.cpp and lsan_fuchsia.cpp.56bool UseExitcodeOnLeak();5758int ExitHook(int status) {59if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit) {60if (UseExitcodeOnLeak())61DoLeakCheck();62else63DoRecoverableLeakCheckVoid();64}65return status == 0 && HasReportedLeaks() ? common_flags()->exitcode : status;66}6768void LockStuffAndStopTheWorld(StopTheWorldCallback callback,69CheckForLeaksParam *argument) {70ScopedStopTheWorldLock lock;7172struct Params {73InternalMmapVector<uptr> allocator_caches;74StopTheWorldCallback callback;75CheckForLeaksParam *argument;76} params = {{}, callback, argument};7778// Callback from libc for globals (data/bss modulo relro), when enabled.79auto globals = +[](void *chunk, size_t size, void *data) {80auto params = static_cast<const Params *>(data);81uptr begin = reinterpret_cast<uptr>(chunk);82uptr end = begin + size;83ScanGlobalRange(begin, end, ¶ms->argument->frontier);84};8586// Callback from libc for thread stacks.87auto stacks = +[](void *chunk, size_t size, void *data) {88auto params = static_cast<const Params *>(data);89uptr begin = reinterpret_cast<uptr>(chunk);90uptr end = begin + size;91ScanRangeForPointers(begin, end, ¶ms->argument->frontier, "STACK",92kReachable);93};9495// Callback from libc for thread registers.96auto registers = +[](void *chunk, size_t size, void *data) {97auto params = static_cast<const Params *>(data);98uptr begin = reinterpret_cast<uptr>(chunk);99uptr end = begin + size;100ScanRangeForPointers(begin, end, ¶ms->argument->frontier, "REGISTERS",101kReachable);102};103104if (flags()->use_tls) {105// Collect the allocator cache range from each thread so these106// can all be excluded from the reported TLS ranges.107GetAllThreadAllocatorCachesLocked(¶ms.allocator_caches);108__sanitizer::Sort(params.allocator_caches.data(),109params.allocator_caches.size());110}111112// Callback from libc for TLS regions. This includes thread_local113// variables as well as C11 tss_set and POSIX pthread_setspecific.114auto tls = +[](void *chunk, size_t size, void *data) {115auto params = static_cast<const Params *>(data);116uptr begin = reinterpret_cast<uptr>(chunk);117uptr end = begin + size;118auto i = __sanitizer::InternalLowerBound(params->allocator_caches, begin);119if (i < params->allocator_caches.size() &&120params->allocator_caches[i] >= begin &&121params->allocator_caches[i] <= end &&122end - params->allocator_caches[i] >= sizeof(AllocatorCache)) {123// Split the range in two and omit the allocator cache within.124ScanRangeForPointers(begin, params->allocator_caches[i],125¶ms->argument->frontier, "TLS", kReachable);126uptr begin2 = params->allocator_caches[i] + sizeof(AllocatorCache);127ScanRangeForPointers(begin2, end, ¶ms->argument->frontier, "TLS",128kReachable);129} else {130ScanRangeForPointers(begin, end, ¶ms->argument->frontier, "TLS",131kReachable);132}133};134135// This stops the world and then makes callbacks for various memory regions.136// The final callback is the last thing before the world starts up again.137__sanitizer_memory_snapshot(138flags()->use_globals ? globals : nullptr,139flags()->use_stacks ? stacks : nullptr,140flags()->use_registers ? registers : nullptr,141flags()->use_tls ? tls : nullptr,142[](zx_status_t, void *data) {143auto params = static_cast<const Params *>(data);144145// We don't use the thread registry at all for enumerating the threads146// and their stacks, registers, and TLS regions. So use it separately147// just for the allocator cache, and to call ScanExtraStackRanges,148// which ASan needs.149if (flags()->use_stacks) {150InternalMmapVector<Range> ranges;151GetThreadExtraStackRangesLocked(&ranges);152ScanExtraStackRanges(ranges, ¶ms->argument->frontier);153}154params->callback(SuspendedThreadsListFuchsia(), params->argument);155},156¶ms);157}158159} // namespace __lsan160161// This is declared (in extern "C") by <zircon/sanitizer.h>.162// _Exit calls this directly to intercept and change the status value.163int __sanitizer_process_exit_hook(int status) {164return __lsan::ExitHook(status);165}166167#endif168169170