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