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 // This is defined differently in asan_fuchsia.cpp and lsan_fuchsia.cpp. 56 bool UseExitcodeOnLeak(); 57 58 int ExitHook(int status) { 59 if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit) { 60 if (UseExitcodeOnLeak()) 61 DoLeakCheck(); 62 else 63 DoRecoverableLeakCheckVoid(); 64 } 65 return status == 0 && HasReportedLeaks() ? common_flags()->exitcode : status; 66 } 67 68 void LockStuffAndStopTheWorld(StopTheWorldCallback callback, 69 CheckForLeaksParam *argument) { 70 ScopedStopTheWorldLock lock; 71 72 struct Params { 73 InternalMmapVector<uptr> allocator_caches; 74 StopTheWorldCallback callback; 75 CheckForLeaksParam *argument; 76 } params = {{}, callback, argument}; 77 78 // Callback from libc for globals (data/bss modulo relro), when enabled. 79 auto globals = +[](void *chunk, size_t size, void *data) { 80 auto params = static_cast<const Params *>(data); 81 uptr begin = reinterpret_cast<uptr>(chunk); 82 uptr end = begin + size; 83 ScanGlobalRange(begin, end, ¶ms->argument->frontier); 84 }; 85 86 // Callback from libc for thread stacks. 87 auto stacks = +[](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, "STACK", 92 kReachable); 93 }; 94 95 // Callback from libc for thread registers. 96 auto registers = +[](void *chunk, size_t size, void *data) { 97 auto params = static_cast<const Params *>(data); 98 uptr begin = reinterpret_cast<uptr>(chunk); 99 uptr end = begin + size; 100 ScanRangeForPointers(begin, end, ¶ms->argument->frontier, "REGISTERS", 101 kReachable); 102 }; 103 104 if (flags()->use_tls) { 105 // Collect the allocator cache range from each thread so these 106 // can all be excluded from the reported TLS ranges. 107 GetAllThreadAllocatorCachesLocked(¶ms.allocator_caches); 108 __sanitizer::Sort(params.allocator_caches.data(), 109 params.allocator_caches.size()); 110 } 111 112 // Callback from libc for TLS regions. This includes thread_local 113 // variables as well as C11 tss_set and POSIX pthread_setspecific. 114 auto tls = +[](void *chunk, size_t size, void *data) { 115 auto params = static_cast<const Params *>(data); 116 uptr begin = reinterpret_cast<uptr>(chunk); 117 uptr end = begin + size; 118 auto i = __sanitizer::InternalLowerBound(params->allocator_caches, begin); 119 if (i < params->allocator_caches.size() && 120 params->allocator_caches[i] >= begin && 121 end - params->allocator_caches[i] <= sizeof(AllocatorCache)) { 122 // Split the range in two and omit the allocator cache within. 123 ScanRangeForPointers(begin, params->allocator_caches[i], 124 ¶ms->argument->frontier, "TLS", kReachable); 125 uptr begin2 = params->allocator_caches[i] + sizeof(AllocatorCache); 126 ScanRangeForPointers(begin2, end, ¶ms->argument->frontier, "TLS", 127 kReachable); 128 } else { 129 ScanRangeForPointers(begin, end, ¶ms->argument->frontier, "TLS", 130 kReachable); 131 } 132 }; 133 134 // This stops the world and then makes callbacks for various memory regions. 135 // The final callback is the last thing before the world starts up again. 136 __sanitizer_memory_snapshot( 137 flags()->use_globals ? globals : nullptr, 138 flags()->use_stacks ? stacks : nullptr, 139 flags()->use_registers ? registers : nullptr, 140 flags()->use_tls ? tls : nullptr, 141 [](zx_status_t, void *data) { 142 auto params = static_cast<const Params *>(data); 143 144 // We don't use the thread registry at all for enumerating the threads 145 // and their stacks, registers, and TLS regions. So use it separately 146 // just for the allocator cache, and to call ForEachExtraStackRange, 147 // which ASan needs. 148 if (flags()->use_stacks) { 149 GetThreadRegistryLocked()->RunCallbackForEachThreadLocked( 150 [](ThreadContextBase *tctx, void *arg) { 151 ForEachExtraStackRange(tctx->os_id, ForEachExtraStackRangeCb, 152 arg); 153 }, 154 ¶ms->argument->frontier); 155 } 156 157 params->callback(SuspendedThreadsListFuchsia(), params->argument); 158 }, 159 ¶ms); 160 } 161 162 } // namespace __lsan 163 164 // This is declared (in extern "C") by <zircon/sanitizer.h>. 165 // _Exit calls this directly to intercept and change the status value. 166 int __sanitizer_process_exit_hook(int status) { 167 return __lsan::ExitHook(status); 168 } 169 170 #endif 171