11f9cb04fSpatrick //=-- lsan_common_fuchsia.cpp --------------------------------------------===//
21f9cb04fSpatrick //
31f9cb04fSpatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
41f9cb04fSpatrick // See https://llvm.org/LICENSE.txt for license information.
51f9cb04fSpatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
61f9cb04fSpatrick //
71f9cb04fSpatrick //===---------------------------------------------------------------------===//
81f9cb04fSpatrick //
91f9cb04fSpatrick // This file is a part of LeakSanitizer.
101f9cb04fSpatrick // Implementation of common leak checking functionality. Fuchsia-specific code.
111f9cb04fSpatrick //
121f9cb04fSpatrick //===---------------------------------------------------------------------===//
131f9cb04fSpatrick
141f9cb04fSpatrick #include "lsan_common.h"
15*810390e3Srobert #include "lsan_thread.h"
161f9cb04fSpatrick #include "sanitizer_common/sanitizer_platform.h"
171f9cb04fSpatrick
181f9cb04fSpatrick #if CAN_SANITIZE_LEAKS && SANITIZER_FUCHSIA
191f9cb04fSpatrick #include <zircon/sanitizer.h>
201f9cb04fSpatrick
211f9cb04fSpatrick #include "lsan_allocator.h"
221f9cb04fSpatrick #include "sanitizer_common/sanitizer_flags.h"
23d89ec533Spatrick #include "sanitizer_common/sanitizer_stoptheworld_fuchsia.h"
241f9cb04fSpatrick #include "sanitizer_common/sanitizer_thread_registry.h"
251f9cb04fSpatrick
261f9cb04fSpatrick // Ensure that the Zircon system ABI is linked in.
271f9cb04fSpatrick #pragma comment(lib, "zircon")
281f9cb04fSpatrick
291f9cb04fSpatrick namespace __lsan {
301f9cb04fSpatrick
InitializePlatformSpecificModules()311f9cb04fSpatrick void InitializePlatformSpecificModules() {}
321f9cb04fSpatrick
GetLinker()331f9cb04fSpatrick LoadedModule *GetLinker() { return nullptr; }
341f9cb04fSpatrick
351f9cb04fSpatrick __attribute__((tls_model("initial-exec"))) THREADLOCAL int disable_counter;
DisabledInThisThread()361f9cb04fSpatrick bool DisabledInThisThread() { return disable_counter > 0; }
DisableInThisThread()371f9cb04fSpatrick void DisableInThisThread() { disable_counter++; }
EnableInThisThread()381f9cb04fSpatrick void EnableInThisThread() {
391f9cb04fSpatrick if (disable_counter == 0) {
401f9cb04fSpatrick DisableCounterUnderflow();
411f9cb04fSpatrick }
421f9cb04fSpatrick disable_counter--;
431f9cb04fSpatrick }
441f9cb04fSpatrick
451f9cb04fSpatrick // There is nothing left to do after the globals callbacks.
ProcessGlobalRegions(Frontier * frontier)461f9cb04fSpatrick void ProcessGlobalRegions(Frontier *frontier) {}
471f9cb04fSpatrick
481f9cb04fSpatrick // Nothing to do here.
ProcessPlatformSpecificAllocations(Frontier * frontier)491f9cb04fSpatrick void ProcessPlatformSpecificAllocations(Frontier *frontier) {}
501f9cb04fSpatrick
511f9cb04fSpatrick // On Fuchsia, we can intercept _Exit gracefully, and return a failing exit
521f9cb04fSpatrick // code if required at that point. Calling Die() here is undefined
531f9cb04fSpatrick // behavior and causes rare race conditions.
HandleLeaks()541f9cb04fSpatrick void HandleLeaks() {}
551f9cb04fSpatrick
56*810390e3Srobert // This is defined differently in asan_fuchsia.cpp and lsan_fuchsia.cpp.
57*810390e3Srobert bool UseExitcodeOnLeak();
58*810390e3Srobert
ExitHook(int status)591f9cb04fSpatrick int ExitHook(int status) {
60*810390e3Srobert if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit) {
61*810390e3Srobert if (UseExitcodeOnLeak())
62*810390e3Srobert DoLeakCheck();
63*810390e3Srobert else
64*810390e3Srobert DoRecoverableLeakCheckVoid();
65*810390e3Srobert }
661f9cb04fSpatrick return status == 0 && HasReportedLeaks() ? common_flags()->exitcode : status;
671f9cb04fSpatrick }
681f9cb04fSpatrick
LockStuffAndStopTheWorld(StopTheWorldCallback callback,CheckForLeaksParam * argument)691f9cb04fSpatrick void LockStuffAndStopTheWorld(StopTheWorldCallback callback,
701f9cb04fSpatrick CheckForLeaksParam *argument) {
71*810390e3Srobert ScopedStopTheWorldLock lock;
721f9cb04fSpatrick
731f9cb04fSpatrick struct Params {
741f9cb04fSpatrick InternalMmapVector<uptr> allocator_caches;
751f9cb04fSpatrick StopTheWorldCallback callback;
761f9cb04fSpatrick CheckForLeaksParam *argument;
771f9cb04fSpatrick } params = {{}, callback, argument};
781f9cb04fSpatrick
791f9cb04fSpatrick // Callback from libc for globals (data/bss modulo relro), when enabled.
801f9cb04fSpatrick auto globals = +[](void *chunk, size_t size, void *data) {
811f9cb04fSpatrick auto params = static_cast<const Params *>(data);
821f9cb04fSpatrick uptr begin = reinterpret_cast<uptr>(chunk);
831f9cb04fSpatrick uptr end = begin + size;
841f9cb04fSpatrick ScanGlobalRange(begin, end, ¶ms->argument->frontier);
851f9cb04fSpatrick };
861f9cb04fSpatrick
871f9cb04fSpatrick // Callback from libc for thread stacks.
881f9cb04fSpatrick auto stacks = +[](void *chunk, size_t size, void *data) {
891f9cb04fSpatrick auto params = static_cast<const Params *>(data);
901f9cb04fSpatrick uptr begin = reinterpret_cast<uptr>(chunk);
911f9cb04fSpatrick uptr end = begin + size;
921f9cb04fSpatrick ScanRangeForPointers(begin, end, ¶ms->argument->frontier, "STACK",
931f9cb04fSpatrick kReachable);
941f9cb04fSpatrick };
951f9cb04fSpatrick
961f9cb04fSpatrick // Callback from libc for thread registers.
971f9cb04fSpatrick auto registers = +[](void *chunk, size_t size, void *data) {
981f9cb04fSpatrick auto params = static_cast<const Params *>(data);
991f9cb04fSpatrick uptr begin = reinterpret_cast<uptr>(chunk);
1001f9cb04fSpatrick uptr end = begin + size;
1011f9cb04fSpatrick ScanRangeForPointers(begin, end, ¶ms->argument->frontier, "REGISTERS",
1021f9cb04fSpatrick kReachable);
1031f9cb04fSpatrick };
1041f9cb04fSpatrick
1051f9cb04fSpatrick if (flags()->use_tls) {
1061f9cb04fSpatrick // Collect the allocator cache range from each thread so these
1071f9cb04fSpatrick // can all be excluded from the reported TLS ranges.
1081f9cb04fSpatrick GetAllThreadAllocatorCachesLocked(¶ms.allocator_caches);
1091f9cb04fSpatrick __sanitizer::Sort(params.allocator_caches.data(),
1101f9cb04fSpatrick params.allocator_caches.size());
1111f9cb04fSpatrick }
1121f9cb04fSpatrick
1131f9cb04fSpatrick // Callback from libc for TLS regions. This includes thread_local
1141f9cb04fSpatrick // variables as well as C11 tss_set and POSIX pthread_setspecific.
1151f9cb04fSpatrick auto tls = +[](void *chunk, size_t size, void *data) {
1161f9cb04fSpatrick auto params = static_cast<const Params *>(data);
1171f9cb04fSpatrick uptr begin = reinterpret_cast<uptr>(chunk);
1181f9cb04fSpatrick uptr end = begin + size;
119d89ec533Spatrick auto i = __sanitizer::InternalLowerBound(params->allocator_caches, begin);
1201f9cb04fSpatrick if (i < params->allocator_caches.size() &&
1211f9cb04fSpatrick params->allocator_caches[i] >= begin &&
1221f9cb04fSpatrick end - params->allocator_caches[i] <= sizeof(AllocatorCache)) {
1231f9cb04fSpatrick // Split the range in two and omit the allocator cache within.
1241f9cb04fSpatrick ScanRangeForPointers(begin, params->allocator_caches[i],
1251f9cb04fSpatrick ¶ms->argument->frontier, "TLS", kReachable);
1261f9cb04fSpatrick uptr begin2 = params->allocator_caches[i] + sizeof(AllocatorCache);
1271f9cb04fSpatrick ScanRangeForPointers(begin2, end, ¶ms->argument->frontier, "TLS",
1281f9cb04fSpatrick kReachable);
1291f9cb04fSpatrick } else {
1301f9cb04fSpatrick ScanRangeForPointers(begin, end, ¶ms->argument->frontier, "TLS",
1311f9cb04fSpatrick kReachable);
1321f9cb04fSpatrick }
1331f9cb04fSpatrick };
1341f9cb04fSpatrick
1351f9cb04fSpatrick // This stops the world and then makes callbacks for various memory regions.
1361f9cb04fSpatrick // The final callback is the last thing before the world starts up again.
1371f9cb04fSpatrick __sanitizer_memory_snapshot(
1381f9cb04fSpatrick flags()->use_globals ? globals : nullptr,
1391f9cb04fSpatrick flags()->use_stacks ? stacks : nullptr,
1401f9cb04fSpatrick flags()->use_registers ? registers : nullptr,
1411f9cb04fSpatrick flags()->use_tls ? tls : nullptr,
1421f9cb04fSpatrick [](zx_status_t, void *data) {
1431f9cb04fSpatrick auto params = static_cast<const Params *>(data);
1441f9cb04fSpatrick
1451f9cb04fSpatrick // We don't use the thread registry at all for enumerating the threads
1461f9cb04fSpatrick // and their stacks, registers, and TLS regions. So use it separately
147*810390e3Srobert // just for the allocator cache, and to call ScanExtraStackRanges,
1481f9cb04fSpatrick // which ASan needs.
1491f9cb04fSpatrick if (flags()->use_stacks) {
150*810390e3Srobert InternalMmapVector<Range> ranges;
151*810390e3Srobert GetThreadExtraStackRangesLocked(&ranges);
152*810390e3Srobert ScanExtraStackRanges(ranges, ¶ms->argument->frontier);
1531f9cb04fSpatrick }
154d89ec533Spatrick params->callback(SuspendedThreadsListFuchsia(), params->argument);
1551f9cb04fSpatrick },
1561f9cb04fSpatrick ¶ms);
1571f9cb04fSpatrick }
1581f9cb04fSpatrick
1591f9cb04fSpatrick } // namespace __lsan
1601f9cb04fSpatrick
1611f9cb04fSpatrick // This is declared (in extern "C") by <zircon/sanitizer.h>.
1621f9cb04fSpatrick // _Exit calls this directly to intercept and change the status value.
__sanitizer_process_exit_hook(int status)1631f9cb04fSpatrick int __sanitizer_process_exit_hook(int status) {
1641f9cb04fSpatrick return __lsan::ExitHook(status);
1651f9cb04fSpatrick }
1661f9cb04fSpatrick
1671f9cb04fSpatrick #endif
168