xref: /freebsd-src/contrib/llvm-project/compiler-rt/lib/lsan/lsan_common_fuchsia.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
15ffd83dbSDimitry Andric //=-- lsan_common_fuchsia.cpp --------------------------------------------===//
25ffd83dbSDimitry Andric //
35ffd83dbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45ffd83dbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
55ffd83dbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
65ffd83dbSDimitry Andric //
75ffd83dbSDimitry Andric //===---------------------------------------------------------------------===//
85ffd83dbSDimitry Andric //
95ffd83dbSDimitry Andric // This file is a part of LeakSanitizer.
105ffd83dbSDimitry Andric // Implementation of common leak checking functionality. Fuchsia-specific code.
115ffd83dbSDimitry Andric //
125ffd83dbSDimitry Andric //===---------------------------------------------------------------------===//
135ffd83dbSDimitry Andric 
145ffd83dbSDimitry Andric #include "lsan_common.h"
15bdd1243dSDimitry Andric #include "lsan_thread.h"
165ffd83dbSDimitry Andric #include "sanitizer_common/sanitizer_platform.h"
175ffd83dbSDimitry Andric 
185ffd83dbSDimitry Andric #if CAN_SANITIZE_LEAKS && SANITIZER_FUCHSIA
195ffd83dbSDimitry Andric #include <zircon/sanitizer.h>
205ffd83dbSDimitry Andric 
215ffd83dbSDimitry Andric #include "lsan_allocator.h"
225ffd83dbSDimitry Andric #include "sanitizer_common/sanitizer_flags.h"
23e8d8bef9SDimitry Andric #include "sanitizer_common/sanitizer_stoptheworld_fuchsia.h"
245ffd83dbSDimitry Andric #include "sanitizer_common/sanitizer_thread_registry.h"
255ffd83dbSDimitry Andric 
265ffd83dbSDimitry Andric // Ensure that the Zircon system ABI is linked in.
275ffd83dbSDimitry Andric #pragma comment(lib, "zircon")
285ffd83dbSDimitry Andric 
295ffd83dbSDimitry Andric namespace __lsan {
305ffd83dbSDimitry Andric 
InitializePlatformSpecificModules()315ffd83dbSDimitry Andric void InitializePlatformSpecificModules() {}
325ffd83dbSDimitry Andric 
GetLinker()335ffd83dbSDimitry Andric LoadedModule *GetLinker() { return nullptr; }
345ffd83dbSDimitry Andric 
355ffd83dbSDimitry Andric __attribute__((tls_model("initial-exec"))) THREADLOCAL int disable_counter;
DisabledInThisThread()365ffd83dbSDimitry Andric bool DisabledInThisThread() { return disable_counter > 0; }
DisableInThisThread()375ffd83dbSDimitry Andric void DisableInThisThread() { disable_counter++; }
EnableInThisThread()385ffd83dbSDimitry Andric void EnableInThisThread() {
395ffd83dbSDimitry Andric   if (disable_counter == 0) {
405ffd83dbSDimitry Andric     DisableCounterUnderflow();
415ffd83dbSDimitry Andric   }
425ffd83dbSDimitry Andric   disable_counter--;
435ffd83dbSDimitry Andric }
445ffd83dbSDimitry Andric 
455ffd83dbSDimitry Andric // There is nothing left to do after the globals callbacks.
ProcessGlobalRegions(Frontier * frontier)465ffd83dbSDimitry Andric void ProcessGlobalRegions(Frontier *frontier) {}
475ffd83dbSDimitry Andric 
485ffd83dbSDimitry Andric // Nothing to do here.
ProcessPlatformSpecificAllocations(Frontier * frontier)495ffd83dbSDimitry Andric void ProcessPlatformSpecificAllocations(Frontier *frontier) {}
505ffd83dbSDimitry Andric 
515ffd83dbSDimitry Andric // On Fuchsia, we can intercept _Exit gracefully, and return a failing exit
525ffd83dbSDimitry Andric // code if required at that point.  Calling Die() here is undefined
535ffd83dbSDimitry Andric // behavior and causes rare race conditions.
HandleLeaks()545ffd83dbSDimitry Andric void HandleLeaks() {}
555ffd83dbSDimitry Andric 
5681ad6265SDimitry Andric // This is defined differently in asan_fuchsia.cpp and lsan_fuchsia.cpp.
5781ad6265SDimitry Andric bool UseExitcodeOnLeak();
5881ad6265SDimitry Andric 
ExitHook(int status)595ffd83dbSDimitry Andric int ExitHook(int status) {
6081ad6265SDimitry Andric   if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit) {
6181ad6265SDimitry Andric     if (UseExitcodeOnLeak())
6281ad6265SDimitry Andric       DoLeakCheck();
6381ad6265SDimitry Andric     else
6481ad6265SDimitry Andric       DoRecoverableLeakCheckVoid();
6581ad6265SDimitry Andric   }
665ffd83dbSDimitry Andric   return status == 0 && HasReportedLeaks() ? common_flags()->exitcode : status;
675ffd83dbSDimitry Andric }
685ffd83dbSDimitry Andric 
LockStuffAndStopTheWorld(StopTheWorldCallback callback,CheckForLeaksParam * argument)695ffd83dbSDimitry Andric void LockStuffAndStopTheWorld(StopTheWorldCallback callback,
705ffd83dbSDimitry Andric                               CheckForLeaksParam *argument) {
710eae32dcSDimitry Andric   ScopedStopTheWorldLock lock;
725ffd83dbSDimitry Andric 
735ffd83dbSDimitry Andric   struct Params {
745ffd83dbSDimitry Andric     InternalMmapVector<uptr> allocator_caches;
755ffd83dbSDimitry Andric     StopTheWorldCallback callback;
765ffd83dbSDimitry Andric     CheckForLeaksParam *argument;
775ffd83dbSDimitry Andric   } params = {{}, callback, argument};
785ffd83dbSDimitry Andric 
795ffd83dbSDimitry Andric   // Callback from libc for globals (data/bss modulo relro), when enabled.
805ffd83dbSDimitry Andric   auto globals = +[](void *chunk, size_t size, void *data) {
815ffd83dbSDimitry Andric     auto params = static_cast<const Params *>(data);
825ffd83dbSDimitry Andric     uptr begin = reinterpret_cast<uptr>(chunk);
835ffd83dbSDimitry Andric     uptr end = begin + size;
845ffd83dbSDimitry Andric     ScanGlobalRange(begin, end, &params->argument->frontier);
855ffd83dbSDimitry Andric   };
865ffd83dbSDimitry Andric 
875ffd83dbSDimitry Andric   // Callback from libc for thread stacks.
885ffd83dbSDimitry Andric   auto stacks = +[](void *chunk, size_t size, void *data) {
895ffd83dbSDimitry Andric     auto params = static_cast<const Params *>(data);
905ffd83dbSDimitry Andric     uptr begin = reinterpret_cast<uptr>(chunk);
915ffd83dbSDimitry Andric     uptr end = begin + size;
925ffd83dbSDimitry Andric     ScanRangeForPointers(begin, end, &params->argument->frontier, "STACK",
935ffd83dbSDimitry Andric                          kReachable);
945ffd83dbSDimitry Andric   };
955ffd83dbSDimitry Andric 
965ffd83dbSDimitry Andric   // Callback from libc for thread registers.
975ffd83dbSDimitry Andric   auto registers = +[](void *chunk, size_t size, void *data) {
985ffd83dbSDimitry Andric     auto params = static_cast<const Params *>(data);
995ffd83dbSDimitry Andric     uptr begin = reinterpret_cast<uptr>(chunk);
1005ffd83dbSDimitry Andric     uptr end = begin + size;
1015ffd83dbSDimitry Andric     ScanRangeForPointers(begin, end, &params->argument->frontier, "REGISTERS",
1025ffd83dbSDimitry Andric                          kReachable);
1035ffd83dbSDimitry Andric   };
1045ffd83dbSDimitry Andric 
1055ffd83dbSDimitry Andric   if (flags()->use_tls) {
1065ffd83dbSDimitry Andric     // Collect the allocator cache range from each thread so these
1075ffd83dbSDimitry Andric     // can all be excluded from the reported TLS ranges.
1085ffd83dbSDimitry Andric     GetAllThreadAllocatorCachesLocked(&params.allocator_caches);
1095ffd83dbSDimitry Andric     __sanitizer::Sort(params.allocator_caches.data(),
1105ffd83dbSDimitry Andric                       params.allocator_caches.size());
1115ffd83dbSDimitry Andric   }
1125ffd83dbSDimitry Andric 
1135ffd83dbSDimitry Andric   // Callback from libc for TLS regions.  This includes thread_local
1145ffd83dbSDimitry Andric   // variables as well as C11 tss_set and POSIX pthread_setspecific.
1155ffd83dbSDimitry Andric   auto tls = +[](void *chunk, size_t size, void *data) {
1165ffd83dbSDimitry Andric     auto params = static_cast<const Params *>(data);
1175ffd83dbSDimitry Andric     uptr begin = reinterpret_cast<uptr>(chunk);
1185ffd83dbSDimitry Andric     uptr end = begin + size;
119e8d8bef9SDimitry Andric     auto i = __sanitizer::InternalLowerBound(params->allocator_caches, begin);
1205ffd83dbSDimitry Andric     if (i < params->allocator_caches.size() &&
1215ffd83dbSDimitry Andric         params->allocator_caches[i] >= begin &&
122*5f757f3fSDimitry Andric         params->allocator_caches[i] <= end &&
123*5f757f3fSDimitry Andric         end - params->allocator_caches[i] >= sizeof(AllocatorCache)) {
1245ffd83dbSDimitry Andric       // Split the range in two and omit the allocator cache within.
1255ffd83dbSDimitry Andric       ScanRangeForPointers(begin, params->allocator_caches[i],
1265ffd83dbSDimitry Andric                            &params->argument->frontier, "TLS", kReachable);
1275ffd83dbSDimitry Andric       uptr begin2 = params->allocator_caches[i] + sizeof(AllocatorCache);
1285ffd83dbSDimitry Andric       ScanRangeForPointers(begin2, end, &params->argument->frontier, "TLS",
1295ffd83dbSDimitry Andric                            kReachable);
1305ffd83dbSDimitry Andric     } else {
1315ffd83dbSDimitry Andric       ScanRangeForPointers(begin, end, &params->argument->frontier, "TLS",
1325ffd83dbSDimitry Andric                            kReachable);
1335ffd83dbSDimitry Andric     }
1345ffd83dbSDimitry Andric   };
1355ffd83dbSDimitry Andric 
1365ffd83dbSDimitry Andric   // This stops the world and then makes callbacks for various memory regions.
1375ffd83dbSDimitry Andric   // The final callback is the last thing before the world starts up again.
1385ffd83dbSDimitry Andric   __sanitizer_memory_snapshot(
1395ffd83dbSDimitry Andric       flags()->use_globals ? globals : nullptr,
1405ffd83dbSDimitry Andric       flags()->use_stacks ? stacks : nullptr,
1415ffd83dbSDimitry Andric       flags()->use_registers ? registers : nullptr,
1425ffd83dbSDimitry Andric       flags()->use_tls ? tls : nullptr,
1435ffd83dbSDimitry Andric       [](zx_status_t, void *data) {
1445ffd83dbSDimitry Andric         auto params = static_cast<const Params *>(data);
1455ffd83dbSDimitry Andric 
1465ffd83dbSDimitry Andric         // We don't use the thread registry at all for enumerating the threads
1475ffd83dbSDimitry Andric         // and their stacks, registers, and TLS regions.  So use it separately
148bdd1243dSDimitry Andric         // just for the allocator cache, and to call ScanExtraStackRanges,
1495ffd83dbSDimitry Andric         // which ASan needs.
1505ffd83dbSDimitry Andric         if (flags()->use_stacks) {
151bdd1243dSDimitry Andric           InternalMmapVector<Range> ranges;
152bdd1243dSDimitry Andric           GetThreadExtraStackRangesLocked(&ranges);
153bdd1243dSDimitry Andric           ScanExtraStackRanges(ranges, &params->argument->frontier);
1545ffd83dbSDimitry Andric         }
155e8d8bef9SDimitry Andric         params->callback(SuspendedThreadsListFuchsia(), params->argument);
1565ffd83dbSDimitry Andric       },
1575ffd83dbSDimitry Andric       &params);
1585ffd83dbSDimitry Andric }
1595ffd83dbSDimitry Andric 
1605ffd83dbSDimitry Andric }  // namespace __lsan
1615ffd83dbSDimitry Andric 
1625ffd83dbSDimitry Andric // This is declared (in extern "C") by <zircon/sanitizer.h>.
1635ffd83dbSDimitry Andric // _Exit calls this directly to intercept and change the status value.
__sanitizer_process_exit_hook(int status)1645ffd83dbSDimitry Andric int __sanitizer_process_exit_hook(int status) {
1655ffd83dbSDimitry Andric   return __lsan::ExitHook(status);
1665ffd83dbSDimitry Andric }
1675ffd83dbSDimitry Andric 
1685ffd83dbSDimitry Andric #endif
169