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 "lsan_thread.h" 16 #include "sanitizer_common/sanitizer_platform.h" 17 18 #if CAN_SANITIZE_LEAKS && SANITIZER_FUCHSIA 19 #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 29 namespace __lsan { 30 31 void InitializePlatformSpecificModules() {} 32 33 LoadedModule *GetLinker() { return nullptr; } 34 35 __attribute__((tls_model("initial-exec"))) THREADLOCAL int disable_counter; 36 bool DisabledInThisThread() { return disable_counter > 0; } 37 void DisableInThisThread() { disable_counter++; } 38 void 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. 46 void ProcessGlobalRegions(Frontier *frontier) {} 47 48 // Nothing to do here. 49 void ProcessPlatformSpecificAllocations(Frontier *frontier) {} 50 51 // On Fuchsia, we can intercept _Exit gracefully, and return a failing exit 52 // code if required at that point. Calling Die() here is undefined 53 // behavior and causes rare race conditions. 54 void HandleLeaks() {} 55 56 // This is defined differently in asan_fuchsia.cpp and lsan_fuchsia.cpp. 57 bool UseExitcodeOnLeak(); 58 59 int ExitHook(int status) { 60 if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit) { 61 if (UseExitcodeOnLeak()) 62 DoLeakCheck(); 63 else 64 DoRecoverableLeakCheckVoid(); 65 } 66 return status == 0 && HasReportedLeaks() ? common_flags()->exitcode : status; 67 } 68 69 void 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 these 107 // 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_local 114 // 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 threads 147 // and their stacks, registers, and TLS regions. So use it separately 148 // 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 __lsan 161 162 // This is declared (in extern "C") by <zircon/sanitizer.h>. 163 // _Exit calls this directly to intercept and change the status value. 164 int __sanitizer_process_exit_hook(int status) { 165 return __lsan::ExitHook(status); 166 } 167 168 #endif 169