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, ¶ms->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, ¶ms->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, ¶ms->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(¶ms.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 ¶ms->argument->frontier, "TLS", kReachable); 1175ffd83dbSDimitry Andric uptr begin2 = params->allocator_caches[i] + sizeof(AllocatorCache); 1185ffd83dbSDimitry Andric ScanRangeForPointers(begin2, end, ¶ms->argument->frontier, "TLS", 1195ffd83dbSDimitry Andric kReachable); 1205ffd83dbSDimitry Andric } else { 1215ffd83dbSDimitry Andric ScanRangeForPointers(begin, end, ¶ms->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 ¶ms->argument->frontier); 1475ffd83dbSDimitry Andric } 1485ffd83dbSDimitry Andric 149*e8d8bef9SDimitry Andric params->callback(SuspendedThreadsListFuchsia(), params->argument); 1505ffd83dbSDimitry Andric }, 1515ffd83dbSDimitry Andric ¶ms); 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