xref: /openbsd-src/gnu/llvm/compiler-rt/lib/lsan/lsan_common_fuchsia.cpp (revision 810390e339a5425391477d5d41c78d7cab2424ac)
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, &params->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, &params->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, &params->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(&params.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                            &params->argument->frontier, "TLS", kReachable);
1261f9cb04fSpatrick       uptr begin2 = params->allocator_caches[i] + sizeof(AllocatorCache);
1271f9cb04fSpatrick       ScanRangeForPointers(begin2, end, &params->argument->frontier, "TLS",
1281f9cb04fSpatrick                            kReachable);
1291f9cb04fSpatrick     } else {
1301f9cb04fSpatrick       ScanRangeForPointers(begin, end, &params->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, &params->argument->frontier);
1531f9cb04fSpatrick         }
154d89ec533Spatrick         params->callback(SuspendedThreadsListFuchsia(), params->argument);
1551f9cb04fSpatrick       },
1561f9cb04fSpatrick       &params);
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