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, ¶ms->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, ¶ms->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, ¶ms->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(¶ms.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 ¶ms->argument->frontier, "TLS", kReachable);
1275ffd83dbSDimitry Andric uptr begin2 = params->allocator_caches[i] + sizeof(AllocatorCache);
1285ffd83dbSDimitry Andric ScanRangeForPointers(begin2, end, ¶ms->argument->frontier, "TLS",
1295ffd83dbSDimitry Andric kReachable);
1305ffd83dbSDimitry Andric } else {
1315ffd83dbSDimitry Andric ScanRangeForPointers(begin, end, ¶ms->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, ¶ms->argument->frontier);
1545ffd83dbSDimitry Andric }
155e8d8bef9SDimitry Andric params->callback(SuspendedThreadsListFuchsia(), params->argument);
1565ffd83dbSDimitry Andric },
1575ffd83dbSDimitry Andric ¶ms);
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