169 lines · cpp
1//=-- lsan_common_fuchsia.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. Fuchsia-specific code.11//12//===---------------------------------------------------------------------===//13 14#include "lsan_common.h"15#include "lsan_thread.h"16#include "sanitizer_common/sanitizer_platform.h"17 18#if CAN_SANITIZE_LEAKS && SANITIZER_FUCHSIA19#include <zircon/sanitizer.h>20 21#include "lsan_allocator.h"22#include "sanitizer_common/sanitizer_flags.h"23#include "sanitizer_common/sanitizer_stoptheworld_fuchsia.h"24#include "sanitizer_common/sanitizer_thread_registry.h"25 26// Ensure that the Zircon system ABI is linked in.27#pragma comment(lib, "zircon")28 29namespace __lsan {30 31void InitializePlatformSpecificModules() {}32 33LoadedModule *GetLinker() { return nullptr; }34 35__attribute__((tls_model("initial-exec"))) THREADLOCAL int disable_counter;36bool DisabledInThisThread() { return disable_counter > 0; }37void DisableInThisThread() { disable_counter++; }38void EnableInThisThread() {39 if (disable_counter == 0) {40 DisableCounterUnderflow();41 }42 disable_counter--;43}44 45// There is nothing left to do after the globals callbacks.46void ProcessGlobalRegions(Frontier *frontier) {}47 48// Nothing to do here.49void ProcessPlatformSpecificAllocations(Frontier *frontier) {}50 51// On Fuchsia, we can intercept _Exit gracefully, and return a failing exit52// code if required at that point. Calling Die() here is undefined53// behavior and causes rare race conditions.54void HandleLeaks() {}55 56// This is defined differently in asan_fuchsia.cpp and lsan_fuchsia.cpp.57bool UseExitcodeOnLeak();58 59int ExitHook(int status) {60 if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit) {61 if (UseExitcodeOnLeak())62 DoLeakCheck();63 else64 DoRecoverableLeakCheckVoid();65 }66 return status == 0 && HasReportedLeaks() ? common_flags()->exitcode : status;67}68 69void LockStuffAndStopTheWorld(StopTheWorldCallback callback,70 CheckForLeaksParam *argument) {71 ScopedStopTheWorldLock lock;72 73 struct Params {74 InternalMmapVector<uptr> allocator_caches;75 StopTheWorldCallback callback;76 CheckForLeaksParam *argument;77 } params = {{}, callback, argument};78 79 // Callback from libc for globals (data/bss modulo relro), when enabled.80 auto globals = +[](void *chunk, size_t size, void *data) {81 auto params = static_cast<const Params *>(data);82 uptr begin = reinterpret_cast<uptr>(chunk);83 uptr end = begin + size;84 ScanGlobalRange(begin, end, ¶ms->argument->frontier);85 };86 87 // Callback from libc for thread stacks.88 auto stacks = +[](void *chunk, size_t size, void *data) {89 auto params = static_cast<const Params *>(data);90 uptr begin = reinterpret_cast<uptr>(chunk);91 uptr end = begin + size;92 ScanRangeForPointers(begin, end, ¶ms->argument->frontier, "STACK",93 kReachable);94 };95 96 // Callback from libc for thread registers.97 auto registers = +[](void *chunk, size_t size, void *data) {98 auto params = static_cast<const Params *>(data);99 uptr begin = reinterpret_cast<uptr>(chunk);100 uptr end = begin + size;101 ScanRangeForPointers(begin, end, ¶ms->argument->frontier, "REGISTERS",102 kReachable);103 };104 105 if (flags()->use_tls) {106 // Collect the allocator cache range from each thread so these107 // can all be excluded from the reported TLS ranges.108 GetAllThreadAllocatorCachesLocked(¶ms.allocator_caches);109 __sanitizer::Sort(params.allocator_caches.data(),110 params.allocator_caches.size());111 }112 113 // Callback from libc for TLS regions. This includes thread_local114 // variables as well as C11 tss_set and POSIX pthread_setspecific.115 auto tls = +[](void *chunk, size_t size, void *data) {116 auto params = static_cast<const Params *>(data);117 uptr begin = reinterpret_cast<uptr>(chunk);118 uptr end = begin + size;119 auto i = __sanitizer::InternalLowerBound(params->allocator_caches, begin);120 if (i < params->allocator_caches.size() &&121 params->allocator_caches[i] >= begin &&122 params->allocator_caches[i] <= end &&123 end - params->allocator_caches[i] >= sizeof(AllocatorCache)) {124 // Split the range in two and omit the allocator cache within.125 ScanRangeForPointers(begin, params->allocator_caches[i],126 ¶ms->argument->frontier, "TLS", kReachable);127 uptr begin2 = params->allocator_caches[i] + sizeof(AllocatorCache);128 ScanRangeForPointers(begin2, end, ¶ms->argument->frontier, "TLS",129 kReachable);130 } else {131 ScanRangeForPointers(begin, end, ¶ms->argument->frontier, "TLS",132 kReachable);133 }134 };135 136 // This stops the world and then makes callbacks for various memory regions.137 // The final callback is the last thing before the world starts up again.138 __sanitizer_memory_snapshot(139 flags()->use_globals ? globals : nullptr,140 flags()->use_stacks ? stacks : nullptr,141 flags()->use_registers ? registers : nullptr,142 flags()->use_tls ? tls : nullptr,143 [](zx_status_t, void *data) {144 auto params = static_cast<const Params *>(data);145 146 // We don't use the thread registry at all for enumerating the threads147 // and their stacks, registers, and TLS regions. So use it separately148 // just for the allocator cache, and to call ScanExtraStackRanges,149 // which ASan needs.150 if (flags()->use_stacks) {151 InternalMmapVector<Range> ranges;152 GetThreadExtraStackRangesLocked(&ranges);153 ScanExtraStackRanges(ranges, ¶ms->argument->frontier);154 }155 params->callback(SuspendedThreadsListFuchsia(), params->argument);156 },157 ¶ms);158}159 160} // namespace __lsan161 162// This is declared (in extern "C") by <zircon/sanitizer.h>.163// _Exit calls this directly to intercept and change the status value.164int __sanitizer_process_exit_hook(int status) {165 return __lsan::ExitHook(status);166}167 168#endif169