xref: /freebsd-src/contrib/llvm-project/compiler-rt/lib/lsan/lsan_common_fuchsia.cpp (revision e8d8bef961a50d4dc22501cde4fb9fb0be1b2532)
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"
155ffd83dbSDimitry Andric #include "sanitizer_common/sanitizer_platform.h"
165ffd83dbSDimitry Andric 
175ffd83dbSDimitry Andric #if CAN_SANITIZE_LEAKS && SANITIZER_FUCHSIA
185ffd83dbSDimitry Andric #include <zircon/sanitizer.h>
195ffd83dbSDimitry Andric 
205ffd83dbSDimitry Andric #include "lsan_allocator.h"
215ffd83dbSDimitry Andric #include "sanitizer_common/sanitizer_flags.h"
22*e8d8bef9SDimitry Andric #include "sanitizer_common/sanitizer_stoptheworld_fuchsia.h"
235ffd83dbSDimitry Andric #include "sanitizer_common/sanitizer_thread_registry.h"
245ffd83dbSDimitry Andric 
255ffd83dbSDimitry Andric // Ensure that the Zircon system ABI is linked in.
265ffd83dbSDimitry Andric #pragma comment(lib, "zircon")
275ffd83dbSDimitry Andric 
285ffd83dbSDimitry Andric namespace __lsan {
295ffd83dbSDimitry Andric 
305ffd83dbSDimitry Andric void InitializePlatformSpecificModules() {}
315ffd83dbSDimitry Andric 
325ffd83dbSDimitry Andric LoadedModule *GetLinker() { return nullptr; }
335ffd83dbSDimitry Andric 
345ffd83dbSDimitry Andric __attribute__((tls_model("initial-exec"))) THREADLOCAL int disable_counter;
355ffd83dbSDimitry Andric bool DisabledInThisThread() { return disable_counter > 0; }
365ffd83dbSDimitry Andric void DisableInThisThread() { disable_counter++; }
375ffd83dbSDimitry Andric void EnableInThisThread() {
385ffd83dbSDimitry Andric   if (disable_counter == 0) {
395ffd83dbSDimitry Andric     DisableCounterUnderflow();
405ffd83dbSDimitry Andric   }
415ffd83dbSDimitry Andric   disable_counter--;
425ffd83dbSDimitry Andric }
435ffd83dbSDimitry Andric 
445ffd83dbSDimitry Andric // There is nothing left to do after the globals callbacks.
455ffd83dbSDimitry Andric void ProcessGlobalRegions(Frontier *frontier) {}
465ffd83dbSDimitry Andric 
475ffd83dbSDimitry Andric // Nothing to do here.
485ffd83dbSDimitry Andric void ProcessPlatformSpecificAllocations(Frontier *frontier) {}
495ffd83dbSDimitry Andric 
505ffd83dbSDimitry Andric // On Fuchsia, we can intercept _Exit gracefully, and return a failing exit
515ffd83dbSDimitry Andric // code if required at that point.  Calling Die() here is undefined
525ffd83dbSDimitry Andric // behavior and causes rare race conditions.
535ffd83dbSDimitry Andric void HandleLeaks() {}
545ffd83dbSDimitry Andric 
555ffd83dbSDimitry Andric int ExitHook(int status) {
565ffd83dbSDimitry Andric   return status == 0 && HasReportedLeaks() ? common_flags()->exitcode : status;
575ffd83dbSDimitry Andric }
585ffd83dbSDimitry Andric 
595ffd83dbSDimitry Andric void LockStuffAndStopTheWorld(StopTheWorldCallback callback,
605ffd83dbSDimitry Andric                               CheckForLeaksParam *argument) {
615ffd83dbSDimitry Andric   LockThreadRegistry();
625ffd83dbSDimitry Andric   LockAllocator();
635ffd83dbSDimitry Andric 
645ffd83dbSDimitry Andric   struct Params {
655ffd83dbSDimitry Andric     InternalMmapVector<uptr> allocator_caches;
665ffd83dbSDimitry Andric     StopTheWorldCallback callback;
675ffd83dbSDimitry Andric     CheckForLeaksParam *argument;
685ffd83dbSDimitry Andric   } params = {{}, callback, argument};
695ffd83dbSDimitry Andric 
705ffd83dbSDimitry Andric   // Callback from libc for globals (data/bss modulo relro), when enabled.
715ffd83dbSDimitry Andric   auto globals = +[](void *chunk, size_t size, void *data) {
725ffd83dbSDimitry Andric     auto params = static_cast<const Params *>(data);
735ffd83dbSDimitry Andric     uptr begin = reinterpret_cast<uptr>(chunk);
745ffd83dbSDimitry Andric     uptr end = begin + size;
755ffd83dbSDimitry Andric     ScanGlobalRange(begin, end, &params->argument->frontier);
765ffd83dbSDimitry Andric   };
775ffd83dbSDimitry Andric 
785ffd83dbSDimitry Andric   // Callback from libc for thread stacks.
795ffd83dbSDimitry Andric   auto stacks = +[](void *chunk, size_t size, void *data) {
805ffd83dbSDimitry Andric     auto params = static_cast<const Params *>(data);
815ffd83dbSDimitry Andric     uptr begin = reinterpret_cast<uptr>(chunk);
825ffd83dbSDimitry Andric     uptr end = begin + size;
835ffd83dbSDimitry Andric     ScanRangeForPointers(begin, end, &params->argument->frontier, "STACK",
845ffd83dbSDimitry Andric                          kReachable);
855ffd83dbSDimitry Andric   };
865ffd83dbSDimitry Andric 
875ffd83dbSDimitry Andric   // Callback from libc for thread registers.
885ffd83dbSDimitry Andric   auto registers = +[](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, "REGISTERS",
935ffd83dbSDimitry Andric                          kReachable);
945ffd83dbSDimitry Andric   };
955ffd83dbSDimitry Andric 
965ffd83dbSDimitry Andric   if (flags()->use_tls) {
975ffd83dbSDimitry Andric     // Collect the allocator cache range from each thread so these
985ffd83dbSDimitry Andric     // can all be excluded from the reported TLS ranges.
995ffd83dbSDimitry Andric     GetAllThreadAllocatorCachesLocked(&params.allocator_caches);
1005ffd83dbSDimitry Andric     __sanitizer::Sort(params.allocator_caches.data(),
1015ffd83dbSDimitry Andric                       params.allocator_caches.size());
1025ffd83dbSDimitry Andric   }
1035ffd83dbSDimitry Andric 
1045ffd83dbSDimitry Andric   // Callback from libc for TLS regions.  This includes thread_local
1055ffd83dbSDimitry Andric   // variables as well as C11 tss_set and POSIX pthread_setspecific.
1065ffd83dbSDimitry Andric   auto tls = +[](void *chunk, size_t size, void *data) {
1075ffd83dbSDimitry Andric     auto params = static_cast<const Params *>(data);
1085ffd83dbSDimitry Andric     uptr begin = reinterpret_cast<uptr>(chunk);
1095ffd83dbSDimitry Andric     uptr end = begin + size;
110*e8d8bef9SDimitry Andric     auto i = __sanitizer::InternalLowerBound(params->allocator_caches, begin);
1115ffd83dbSDimitry Andric     if (i < params->allocator_caches.size() &&
1125ffd83dbSDimitry Andric         params->allocator_caches[i] >= begin &&
1135ffd83dbSDimitry Andric         end - params->allocator_caches[i] <= sizeof(AllocatorCache)) {
1145ffd83dbSDimitry Andric       // Split the range in two and omit the allocator cache within.
1155ffd83dbSDimitry Andric       ScanRangeForPointers(begin, params->allocator_caches[i],
1165ffd83dbSDimitry Andric                            &params->argument->frontier, "TLS", kReachable);
1175ffd83dbSDimitry Andric       uptr begin2 = params->allocator_caches[i] + sizeof(AllocatorCache);
1185ffd83dbSDimitry Andric       ScanRangeForPointers(begin2, end, &params->argument->frontier, "TLS",
1195ffd83dbSDimitry Andric                            kReachable);
1205ffd83dbSDimitry Andric     } else {
1215ffd83dbSDimitry Andric       ScanRangeForPointers(begin, end, &params->argument->frontier, "TLS",
1225ffd83dbSDimitry Andric                            kReachable);
1235ffd83dbSDimitry Andric     }
1245ffd83dbSDimitry Andric   };
1255ffd83dbSDimitry Andric 
1265ffd83dbSDimitry Andric   // This stops the world and then makes callbacks for various memory regions.
1275ffd83dbSDimitry Andric   // The final callback is the last thing before the world starts up again.
1285ffd83dbSDimitry Andric   __sanitizer_memory_snapshot(
1295ffd83dbSDimitry Andric       flags()->use_globals ? globals : nullptr,
1305ffd83dbSDimitry Andric       flags()->use_stacks ? stacks : nullptr,
1315ffd83dbSDimitry Andric       flags()->use_registers ? registers : nullptr,
1325ffd83dbSDimitry Andric       flags()->use_tls ? tls : nullptr,
1335ffd83dbSDimitry Andric       [](zx_status_t, void *data) {
1345ffd83dbSDimitry Andric         auto params = static_cast<const Params *>(data);
1355ffd83dbSDimitry Andric 
1365ffd83dbSDimitry Andric         // We don't use the thread registry at all for enumerating the threads
1375ffd83dbSDimitry Andric         // and their stacks, registers, and TLS regions.  So use it separately
1385ffd83dbSDimitry Andric         // just for the allocator cache, and to call ForEachExtraStackRange,
1395ffd83dbSDimitry Andric         // which ASan needs.
1405ffd83dbSDimitry Andric         if (flags()->use_stacks) {
1415ffd83dbSDimitry Andric           GetThreadRegistryLocked()->RunCallbackForEachThreadLocked(
1425ffd83dbSDimitry Andric               [](ThreadContextBase *tctx, void *arg) {
1435ffd83dbSDimitry Andric                 ForEachExtraStackRange(tctx->os_id, ForEachExtraStackRangeCb,
1445ffd83dbSDimitry Andric                                        arg);
1455ffd83dbSDimitry Andric               },
1465ffd83dbSDimitry Andric               &params->argument->frontier);
1475ffd83dbSDimitry Andric         }
1485ffd83dbSDimitry Andric 
149*e8d8bef9SDimitry Andric         params->callback(SuspendedThreadsListFuchsia(), params->argument);
1505ffd83dbSDimitry Andric       },
1515ffd83dbSDimitry Andric       &params);
1525ffd83dbSDimitry Andric 
1535ffd83dbSDimitry Andric   UnlockAllocator();
1545ffd83dbSDimitry Andric   UnlockThreadRegistry();
1555ffd83dbSDimitry Andric }
1565ffd83dbSDimitry Andric 
1575ffd83dbSDimitry Andric }  // namespace __lsan
1585ffd83dbSDimitry Andric 
1595ffd83dbSDimitry Andric // This is declared (in extern "C") by <zircon/sanitizer.h>.
1605ffd83dbSDimitry Andric // _Exit calls this directly to intercept and change the status value.
1615ffd83dbSDimitry Andric int __sanitizer_process_exit_hook(int status) {
1625ffd83dbSDimitry Andric   return __lsan::ExitHook(status);
1635ffd83dbSDimitry Andric }
1645ffd83dbSDimitry Andric 
1655ffd83dbSDimitry Andric #endif
166