1*5ffd83dbSDimitry Andric //=-- lsan_common_fuchsia.cpp --------------------------------------------===// 2*5ffd83dbSDimitry Andric // 3*5ffd83dbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*5ffd83dbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*5ffd83dbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*5ffd83dbSDimitry Andric // 7*5ffd83dbSDimitry Andric //===---------------------------------------------------------------------===// 8*5ffd83dbSDimitry Andric // 9*5ffd83dbSDimitry Andric // This file is a part of LeakSanitizer. 10*5ffd83dbSDimitry Andric // Implementation of common leak checking functionality. Fuchsia-specific code. 11*5ffd83dbSDimitry Andric // 12*5ffd83dbSDimitry Andric //===---------------------------------------------------------------------===// 13*5ffd83dbSDimitry Andric 14*5ffd83dbSDimitry Andric #include "lsan_common.h" 15*5ffd83dbSDimitry Andric #include "sanitizer_common/sanitizer_platform.h" 16*5ffd83dbSDimitry Andric 17*5ffd83dbSDimitry Andric #if CAN_SANITIZE_LEAKS && SANITIZER_FUCHSIA 18*5ffd83dbSDimitry Andric #include <zircon/sanitizer.h> 19*5ffd83dbSDimitry Andric 20*5ffd83dbSDimitry Andric #include "lsan_allocator.h" 21*5ffd83dbSDimitry Andric #include "sanitizer_common/sanitizer_flags.h" 22*5ffd83dbSDimitry Andric #include "sanitizer_common/sanitizer_thread_registry.h" 23*5ffd83dbSDimitry Andric 24*5ffd83dbSDimitry Andric // Ensure that the Zircon system ABI is linked in. 25*5ffd83dbSDimitry Andric #pragma comment(lib, "zircon") 26*5ffd83dbSDimitry Andric 27*5ffd83dbSDimitry Andric namespace __lsan { 28*5ffd83dbSDimitry Andric 29*5ffd83dbSDimitry Andric void InitializePlatformSpecificModules() {} 30*5ffd83dbSDimitry Andric 31*5ffd83dbSDimitry Andric LoadedModule *GetLinker() { return nullptr; } 32*5ffd83dbSDimitry Andric 33*5ffd83dbSDimitry Andric __attribute__((tls_model("initial-exec"))) THREADLOCAL int disable_counter; 34*5ffd83dbSDimitry Andric bool DisabledInThisThread() { return disable_counter > 0; } 35*5ffd83dbSDimitry Andric void DisableInThisThread() { disable_counter++; } 36*5ffd83dbSDimitry Andric void EnableInThisThread() { 37*5ffd83dbSDimitry Andric if (disable_counter == 0) { 38*5ffd83dbSDimitry Andric DisableCounterUnderflow(); 39*5ffd83dbSDimitry Andric } 40*5ffd83dbSDimitry Andric disable_counter--; 41*5ffd83dbSDimitry Andric } 42*5ffd83dbSDimitry Andric 43*5ffd83dbSDimitry Andric // There is nothing left to do after the globals callbacks. 44*5ffd83dbSDimitry Andric void ProcessGlobalRegions(Frontier *frontier) {} 45*5ffd83dbSDimitry Andric 46*5ffd83dbSDimitry Andric // Nothing to do here. 47*5ffd83dbSDimitry Andric void ProcessPlatformSpecificAllocations(Frontier *frontier) {} 48*5ffd83dbSDimitry Andric 49*5ffd83dbSDimitry Andric // On Fuchsia, we can intercept _Exit gracefully, and return a failing exit 50*5ffd83dbSDimitry Andric // code if required at that point. Calling Die() here is undefined 51*5ffd83dbSDimitry Andric // behavior and causes rare race conditions. 52*5ffd83dbSDimitry Andric void HandleLeaks() {} 53*5ffd83dbSDimitry Andric 54*5ffd83dbSDimitry Andric int ExitHook(int status) { 55*5ffd83dbSDimitry Andric return status == 0 && HasReportedLeaks() ? common_flags()->exitcode : status; 56*5ffd83dbSDimitry Andric } 57*5ffd83dbSDimitry Andric 58*5ffd83dbSDimitry Andric void LockStuffAndStopTheWorld(StopTheWorldCallback callback, 59*5ffd83dbSDimitry Andric CheckForLeaksParam *argument) { 60*5ffd83dbSDimitry Andric LockThreadRegistry(); 61*5ffd83dbSDimitry Andric LockAllocator(); 62*5ffd83dbSDimitry Andric 63*5ffd83dbSDimitry Andric struct Params { 64*5ffd83dbSDimitry Andric InternalMmapVector<uptr> allocator_caches; 65*5ffd83dbSDimitry Andric StopTheWorldCallback callback; 66*5ffd83dbSDimitry Andric CheckForLeaksParam *argument; 67*5ffd83dbSDimitry Andric } params = {{}, callback, argument}; 68*5ffd83dbSDimitry Andric 69*5ffd83dbSDimitry Andric // Callback from libc for globals (data/bss modulo relro), when enabled. 70*5ffd83dbSDimitry Andric auto globals = +[](void *chunk, size_t size, void *data) { 71*5ffd83dbSDimitry Andric auto params = static_cast<const Params *>(data); 72*5ffd83dbSDimitry Andric uptr begin = reinterpret_cast<uptr>(chunk); 73*5ffd83dbSDimitry Andric uptr end = begin + size; 74*5ffd83dbSDimitry Andric ScanGlobalRange(begin, end, ¶ms->argument->frontier); 75*5ffd83dbSDimitry Andric }; 76*5ffd83dbSDimitry Andric 77*5ffd83dbSDimitry Andric // Callback from libc for thread stacks. 78*5ffd83dbSDimitry Andric auto stacks = +[](void *chunk, size_t size, void *data) { 79*5ffd83dbSDimitry Andric auto params = static_cast<const Params *>(data); 80*5ffd83dbSDimitry Andric uptr begin = reinterpret_cast<uptr>(chunk); 81*5ffd83dbSDimitry Andric uptr end = begin + size; 82*5ffd83dbSDimitry Andric ScanRangeForPointers(begin, end, ¶ms->argument->frontier, "STACK", 83*5ffd83dbSDimitry Andric kReachable); 84*5ffd83dbSDimitry Andric }; 85*5ffd83dbSDimitry Andric 86*5ffd83dbSDimitry Andric // Callback from libc for thread registers. 87*5ffd83dbSDimitry Andric auto registers = +[](void *chunk, size_t size, void *data) { 88*5ffd83dbSDimitry Andric auto params = static_cast<const Params *>(data); 89*5ffd83dbSDimitry Andric uptr begin = reinterpret_cast<uptr>(chunk); 90*5ffd83dbSDimitry Andric uptr end = begin + size; 91*5ffd83dbSDimitry Andric ScanRangeForPointers(begin, end, ¶ms->argument->frontier, "REGISTERS", 92*5ffd83dbSDimitry Andric kReachable); 93*5ffd83dbSDimitry Andric }; 94*5ffd83dbSDimitry Andric 95*5ffd83dbSDimitry Andric if (flags()->use_tls) { 96*5ffd83dbSDimitry Andric // Collect the allocator cache range from each thread so these 97*5ffd83dbSDimitry Andric // can all be excluded from the reported TLS ranges. 98*5ffd83dbSDimitry Andric GetAllThreadAllocatorCachesLocked(¶ms.allocator_caches); 99*5ffd83dbSDimitry Andric __sanitizer::Sort(params.allocator_caches.data(), 100*5ffd83dbSDimitry Andric params.allocator_caches.size()); 101*5ffd83dbSDimitry Andric } 102*5ffd83dbSDimitry Andric 103*5ffd83dbSDimitry Andric // Callback from libc for TLS regions. This includes thread_local 104*5ffd83dbSDimitry Andric // variables as well as C11 tss_set and POSIX pthread_setspecific. 105*5ffd83dbSDimitry Andric auto tls = +[](void *chunk, size_t size, void *data) { 106*5ffd83dbSDimitry Andric auto params = static_cast<const Params *>(data); 107*5ffd83dbSDimitry Andric uptr begin = reinterpret_cast<uptr>(chunk); 108*5ffd83dbSDimitry Andric uptr end = begin + size; 109*5ffd83dbSDimitry Andric auto i = __sanitizer::InternalLowerBound(params->allocator_caches, 0, 110*5ffd83dbSDimitry Andric params->allocator_caches.size(), 111*5ffd83dbSDimitry Andric begin, CompareLess<uptr>()); 112*5ffd83dbSDimitry Andric if (i < params->allocator_caches.size() && 113*5ffd83dbSDimitry Andric params->allocator_caches[i] >= begin && 114*5ffd83dbSDimitry Andric end - params->allocator_caches[i] <= sizeof(AllocatorCache)) { 115*5ffd83dbSDimitry Andric // Split the range in two and omit the allocator cache within. 116*5ffd83dbSDimitry Andric ScanRangeForPointers(begin, params->allocator_caches[i], 117*5ffd83dbSDimitry Andric ¶ms->argument->frontier, "TLS", kReachable); 118*5ffd83dbSDimitry Andric uptr begin2 = params->allocator_caches[i] + sizeof(AllocatorCache); 119*5ffd83dbSDimitry Andric ScanRangeForPointers(begin2, end, ¶ms->argument->frontier, "TLS", 120*5ffd83dbSDimitry Andric kReachable); 121*5ffd83dbSDimitry Andric } else { 122*5ffd83dbSDimitry Andric ScanRangeForPointers(begin, end, ¶ms->argument->frontier, "TLS", 123*5ffd83dbSDimitry Andric kReachable); 124*5ffd83dbSDimitry Andric } 125*5ffd83dbSDimitry Andric }; 126*5ffd83dbSDimitry Andric 127*5ffd83dbSDimitry Andric // This stops the world and then makes callbacks for various memory regions. 128*5ffd83dbSDimitry Andric // The final callback is the last thing before the world starts up again. 129*5ffd83dbSDimitry Andric __sanitizer_memory_snapshot( 130*5ffd83dbSDimitry Andric flags()->use_globals ? globals : nullptr, 131*5ffd83dbSDimitry Andric flags()->use_stacks ? stacks : nullptr, 132*5ffd83dbSDimitry Andric flags()->use_registers ? registers : nullptr, 133*5ffd83dbSDimitry Andric flags()->use_tls ? tls : nullptr, 134*5ffd83dbSDimitry Andric [](zx_status_t, void *data) { 135*5ffd83dbSDimitry Andric auto params = static_cast<const Params *>(data); 136*5ffd83dbSDimitry Andric 137*5ffd83dbSDimitry Andric // We don't use the thread registry at all for enumerating the threads 138*5ffd83dbSDimitry Andric // and their stacks, registers, and TLS regions. So use it separately 139*5ffd83dbSDimitry Andric // just for the allocator cache, and to call ForEachExtraStackRange, 140*5ffd83dbSDimitry Andric // which ASan needs. 141*5ffd83dbSDimitry Andric if (flags()->use_stacks) { 142*5ffd83dbSDimitry Andric GetThreadRegistryLocked()->RunCallbackForEachThreadLocked( 143*5ffd83dbSDimitry Andric [](ThreadContextBase *tctx, void *arg) { 144*5ffd83dbSDimitry Andric ForEachExtraStackRange(tctx->os_id, ForEachExtraStackRangeCb, 145*5ffd83dbSDimitry Andric arg); 146*5ffd83dbSDimitry Andric }, 147*5ffd83dbSDimitry Andric ¶ms->argument->frontier); 148*5ffd83dbSDimitry Andric } 149*5ffd83dbSDimitry Andric 150*5ffd83dbSDimitry Andric params->callback({}, params->argument); 151*5ffd83dbSDimitry Andric }, 152*5ffd83dbSDimitry Andric ¶ms); 153*5ffd83dbSDimitry Andric 154*5ffd83dbSDimitry Andric UnlockAllocator(); 155*5ffd83dbSDimitry Andric UnlockThreadRegistry(); 156*5ffd83dbSDimitry Andric } 157*5ffd83dbSDimitry Andric 158*5ffd83dbSDimitry Andric } // namespace __lsan 159*5ffd83dbSDimitry Andric 160*5ffd83dbSDimitry Andric // This is declared (in extern "C") by <zircon/sanitizer.h>. 161*5ffd83dbSDimitry Andric // _Exit calls this directly to intercept and change the status value. 162*5ffd83dbSDimitry Andric int __sanitizer_process_exit_hook(int status) { 163*5ffd83dbSDimitry Andric return __lsan::ExitHook(status); 164*5ffd83dbSDimitry Andric } 165*5ffd83dbSDimitry Andric 166*5ffd83dbSDimitry Andric #endif 167