diff --git a/compiler-rt/cmake/config-ix.cmake b/compiler-rt/cmake/config-ix.cmake --- a/compiler-rt/cmake/config-ix.cmake +++ b/compiler-rt/cmake/config-ix.cmake @@ -625,7 +625,7 @@ endif() if (COMPILER_RT_HAS_SANITIZER_COMMON AND LSAN_SUPPORTED_ARCH AND - OS_NAME MATCHES "Darwin|Linux|NetBSD") + OS_NAME MATCHES "Darwin|Linux|NetBSD|Fuchsia") set(COMPILER_RT_HAS_LSAN TRUE) else() set(COMPILER_RT_HAS_LSAN FALSE) diff --git a/compiler-rt/lib/lsan/CMakeLists.txt b/compiler-rt/lib/lsan/CMakeLists.txt --- a/compiler-rt/lib/lsan/CMakeLists.txt +++ b/compiler-rt/lib/lsan/CMakeLists.txt @@ -5,6 +5,7 @@ set(LSAN_COMMON_SOURCES lsan_common.cpp + lsan_common_fuchsia.cpp lsan_common_linux.cpp lsan_common_mac.cpp ) @@ -12,8 +13,8 @@ set(LSAN_SOURCES lsan.cpp lsan_allocator.cpp - lsan_linux.cpp lsan_interceptors.cpp + lsan_linux.cpp lsan_mac.cpp lsan_malloc_mac.cpp lsan_preinit.cpp diff --git a/compiler-rt/lib/lsan/lsan.cpp b/compiler-rt/lib/lsan/lsan.cpp --- a/compiler-rt/lib/lsan/lsan.cpp +++ b/compiler-rt/lib/lsan/lsan.cpp @@ -87,6 +87,7 @@ __sanitizer_set_report_path(common_flags()->log_path); } +#if !SANITIZER_FUCHSIA static void OnStackUnwind(const SignalContext &sig, const void *, BufferedStackTrace *stack) { stack->Unwind(StackTrace::GetNextInstructionPc(sig.pc), sig.bp, sig.context, @@ -97,6 +98,7 @@ HandleDeadlySignal(siginfo, context, GetCurrentThread(), &OnStackUnwind, nullptr); } +#endif extern "C" void __lsan_init() { CHECK(!lsan_init_is_running); @@ -113,7 +115,9 @@ InitTlsSize(); InitializeInterceptors(); InitializeThreadRegistry(); +#if !SANITIZER_FUCHSIA InstallDeadlySignalHandlers(LsanOnDeadlySignal); +#endif u32 tid = ThreadCreate(0, 0, true); CHECK_EQ(tid, 0); ThreadStart(tid, GetTid()); diff --git a/compiler-rt/lib/lsan/lsan_allocator.h b/compiler-rt/lib/lsan/lsan_allocator.h --- a/compiler-rt/lib/lsan/lsan_allocator.h +++ b/compiler-rt/lib/lsan/lsan_allocator.h @@ -66,7 +66,10 @@ using PrimaryAllocatorASVT = SizeClassAllocator32>; using PrimaryAllocator = PrimaryAllocatorASVT; #elif defined(__x86_64__) || defined(__powerpc64__) -# if defined(__powerpc64__) +# if SANITIZER_FUCHSIA +const uptr kAllocatorSpace = ~(uptr)0; +const uptr kAllocatorSize = 0x40000000000ULL; // 4T. +# elif defined(__powerpc64__) const uptr kAllocatorSpace = 0xa0000000000ULL; const uptr kAllocatorSize = 0x20000000000ULL; // 2T. # else diff --git a/compiler-rt/lib/lsan/lsan_common.h b/compiler-rt/lib/lsan/lsan_common.h --- a/compiler-rt/lib/lsan/lsan_common.h +++ b/compiler-rt/lib/lsan/lsan_common.h @@ -40,7 +40,7 @@ #elif defined(__arm__) && \ SANITIZER_LINUX && !SANITIZER_ANDROID #define CAN_SANITIZE_LEAKS 1 -#elif SANITIZER_NETBSD +#elif SANITIZER_NETBSD || SANITIZER_FUCHSIA #define CAN_SANITIZE_LEAKS 1 #else #define CAN_SANITIZE_LEAKS 0 @@ -126,12 +126,20 @@ uptr size; }; +struct CheckForLeaksParam { + Frontier frontier; + LeakReport leak_report; + bool success = false; +}; + InternalMmapVector const *GetRootRegions(); void ScanRootRegion(Frontier *frontier, RootRegion const ®ion, uptr region_begin, uptr region_end, bool is_readable); +void ForEachExtraStackRangeCb(uptr begin, uptr end, void* arg); // Run stoptheworld while holding any platform-specific locks, as well as the // allocator and thread registry locks. -void LockStuffAndStopTheWorld(StopTheWorldCallback callback, void* argument); +void LockStuffAndStopTheWorld(StopTheWorldCallback callback, + CheckForLeaksParam* argument); void ScanRangeForPointers(uptr begin, uptr end, Frontier *frontier, diff --git a/compiler-rt/lib/lsan/lsan_common.cpp b/compiler-rt/lib/lsan/lsan_common.cpp --- a/compiler-rt/lib/lsan/lsan_common.cpp +++ b/compiler-rt/lib/lsan/lsan_common.cpp @@ -211,6 +211,13 @@ ScanRangeForPointers(begin, end, frontier, "FAKE STACK", kReachable); } +#if SANITIZER_FUCHSIA + +// Fuchsia handles all threads together with its own callback. +static void ProcessThreads(SuspendedThreadsList const &, Frontier *) {} + +#else + // Scans thread data (stacks and TLS) for heap pointers. static void ProcessThreads(SuspendedThreadsList const &suspended_threads, Frontier *frontier) { @@ -308,6 +315,8 @@ } } +#endif // SANITIZER_FUCHSIA + void ScanRootRegion(Frontier *frontier, const RootRegion &root_region, uptr region_begin, uptr region_end, bool is_readable) { uptr intersection_begin = Max(root_region.begin, region_begin); @@ -443,25 +452,23 @@ } // Sets the appropriate tag on each chunk. -static void ClassifyAllChunks(SuspendedThreadsList const &suspended_threads) { - // Holds the flood fill frontier. - Frontier frontier; - - ForEachChunk(CollectIgnoredCb, &frontier); - ProcessGlobalRegions(&frontier); - ProcessThreads(suspended_threads, &frontier); - ProcessRootRegions(&frontier); - FloodFillTag(&frontier, kReachable); +static void ClassifyAllChunks(SuspendedThreadsList const &suspended_threads, + Frontier *frontier) { + ForEachChunk(CollectIgnoredCb, frontier); + ProcessGlobalRegions(frontier); + ProcessThreads(suspended_threads, frontier); + ProcessRootRegions(frontier); + FloodFillTag(frontier, kReachable); - CHECK_EQ(0, frontier.size()); - ProcessPC(&frontier); + CHECK_EQ(0, frontier->size()); + ProcessPC(frontier); // The check here is relatively expensive, so we do this in a separate flood // fill. That way we can skip the check for chunks that are reachable // otherwise. LOG_POINTERS("Processing platform-specific allocations.\n"); - ProcessPlatformSpecificAllocations(&frontier); - FloodFillTag(&frontier, kReachable); + ProcessPlatformSpecificAllocations(frontier); + FloodFillTag(frontier, kReachable); // Iterate over leaked chunks and mark those that are reachable from other // leaked chunks. @@ -521,10 +528,13 @@ Printf("%s\n\n", line); } -struct CheckForLeaksParam { - bool success; - LeakReport leak_report; -}; +#if SANITIZER_FUCHSIA + +// Fuchsia provides a libc interface that guarantees all threads are +// covered, and SuspendedThreadList is never really used. +static void ReportUnsuspendedThreads(const SuspendedThreadsList &) {} + +#else static void ReportIfNotSuspended(ThreadContextBase *tctx, void *arg) { const InternalMmapVector &suspended_threads = @@ -550,13 +560,15 @@ &ReportIfNotSuspended, &threads); } +#endif // SANITIZER_FUCHSIA + static void CheckForLeaksCallback(const SuspendedThreadsList &suspended_threads, void *arg) { CheckForLeaksParam *param = reinterpret_cast(arg); CHECK(param); CHECK(!param->success); ReportUnsuspendedThreads(suspended_threads); - ClassifyAllChunks(suspended_threads); + ClassifyAllChunks(suspended_threads, ¶m->frontier); ForEachChunk(CollectLeaksCb, ¶m->leak_report); // Clean up for subsequent leak checks. This assumes we did not overwrite any // kIgnored tags. @@ -569,7 +581,6 @@ return false; EnsureMainThreadIDIsCorrect(); CheckForLeaksParam param; - param.success = false; LockStuffAndStopTheWorld(CheckForLeaksCallback, ¶m); if (!param.success) { diff --git a/compiler-rt/lib/lsan/lsan_common_fuchsia.cpp b/compiler-rt/lib/lsan/lsan_common_fuchsia.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/lib/lsan/lsan_common_fuchsia.cpp @@ -0,0 +1,141 @@ +//=-- lsan_common_fuchsia.cpp --------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===---------------------------------------------------------------------===// +// +// This file is a part of LeakSanitizer. +// Implementation of common leak checking functionality. Fuchsia-specific code. +// +//===---------------------------------------------------------------------===// + +#include "lsan_common.h" +#include "sanitizer_common/sanitizer_platform.h" + +#if CAN_SANITIZE_LEAKS && SANITIZER_FUCHSIA +#include + +#include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_thread_registry.h" + +// Ensure that the Zircon system ABI is linked in. +#pragma comment(lib, "zircon") + +namespace __lsan { + +void InitializePlatformSpecificModules() {} + +LoadedModule *GetLinker() { return nullptr; } + +__attribute__((tls_model("initial-exec"))) THREADLOCAL int disable_counter; +bool DisabledInThisThread() { return disable_counter > 0; } +void DisableInThisThread() { disable_counter++; } +void EnableInThisThread() { + if (disable_counter == 0) { + DisableCounterUnderflow(); + } + disable_counter--; +} + +// There is nothing left to do after the globals callbacks. +void ProcessGlobalRegions(Frontier *frontier) {} + +// Nothing to do here. +void ProcessPlatformSpecificAllocations(Frontier *frontier) {} + +// On Fuchsia, we can intercept _Exit gracefully, and return a failing exit +// code if required at that point. Calling Die() here is undefined +// behavior and causes rare race conditions. +void HandleLeaks() {} + +int ExitHook(int status) { + return status == 0 && HasReportedLeaks() ? common_flags()->exitcode : status; +} + +void LockStuffAndStopTheWorld(StopTheWorldCallback callback, + CheckForLeaksParam *argument) { + LockThreadRegistry(); + LockAllocator(); + + struct Params { + StopTheWorldCallback callback; + CheckForLeaksParam *argument; + } params = {callback, argument}; + + // Callback from libc for globals (data/bss modulo relro), when enabled. + auto globals = +[](void *chunk, size_t size, void *data) { + auto params = static_cast(data); + uptr begin = reinterpret_cast(chunk); + uptr end = begin + size; + ScanGlobalRange(begin, end, ¶ms->argument->frontier); + }; + + // Callback from libc for thread stacks. + auto stacks = +[](void *chunk, size_t size, void *data) { + auto params = static_cast(data); + uptr begin = reinterpret_cast(chunk); + uptr end = begin + size; + ScanRangeForPointers(begin, end, ¶ms->argument->frontier, "STACK", + kReachable); + }; + + // Callback from libc for thread registers. + auto registers = +[](void *chunk, size_t size, void *data) { + auto params = static_cast(data); + uptr begin = reinterpret_cast(chunk); + uptr end = begin + size; + ScanRangeForPointers(begin, end, ¶ms->argument->frontier, "REGISTERS", + kReachable); + }; + + // Callback from libc for TLS regions. This includes thread_local + // variables as well as C11 tss_set and POSIX pthread_setspecific. + auto tls = +[](void *chunk, size_t size, void *data) { + auto params = static_cast(data); + uptr begin = reinterpret_cast(chunk); + uptr end = begin + size; + ScanRangeForPointers(begin, end, ¶ms->argument->frontier, "TLS", + kReachable); + }; + + // This stops the world and then makes callbacks for various memory regions. + // The final callback is the last thing before the world starts up again. + __sanitizer_memory_snapshot( + flags()->use_globals ? globals : nullptr, + flags()->use_stacks ? stacks : nullptr, + flags()->use_registers ? registers : nullptr, + flags()->use_tls ? tls : nullptr, + [](zx_status_t, void *data) { + auto params = static_cast(data); + + // We don't use the thread registry at all for enumerating the + // threads and their stacks, registers, and TLS regions. So use it + // separately just to call ForEachExtraStackRange, which ASan needs. + if (flags()->use_stacks) { + GetThreadRegistryLocked()->RunCallbackForEachThreadLocked( + [](ThreadContextBase *tctx, void *arg) { + ForEachExtraStackRange(tctx->os_id, ForEachExtraStackRangeCb, + arg); + }, + ¶ms->argument->frontier); + } + + params->callback({}, params->argument); + }, + ¶ms); + + UnlockAllocator(); + UnlockThreadRegistry(); +} + +} // namespace __lsan + +// This is declared (in extern "C") by . +// _Exit calls this directly to intercept and change the status value. +int __sanitizer_process_exit_hook(int status) { + return __lsan::ExitHook(status); +} + +#endif diff --git a/compiler-rt/lib/lsan/lsan_common_linux.cpp b/compiler-rt/lib/lsan/lsan_common_linux.cpp --- a/compiler-rt/lib/lsan/lsan_common_linux.cpp +++ b/compiler-rt/lib/lsan/lsan_common_linux.cpp @@ -134,7 +134,8 @@ // while holding the libdl lock in the parent thread, we can safely reenter it // in the tracer. The solution is to run stoptheworld from a dl_iterate_phdr() // callback in the parent thread. -void LockStuffAndStopTheWorld(StopTheWorldCallback callback, void *argument) { +void LockStuffAndStopTheWorld(StopTheWorldCallback callback, + CheckForLeaksParam *argument) { DoStopTheWorldParam param = {callback, argument}; dl_iterate_phdr(LockStuffAndStopTheWorldCallback, ¶m); } diff --git a/compiler-rt/lib/lsan/lsan_common_mac.cpp b/compiler-rt/lib/lsan/lsan_common_mac.cpp --- a/compiler-rt/lib/lsan/lsan_common_mac.cpp +++ b/compiler-rt/lib/lsan/lsan_common_mac.cpp @@ -193,7 +193,8 @@ // causes rare race conditions. void HandleLeaks() {} -void LockStuffAndStopTheWorld(StopTheWorldCallback callback, void *argument) { +void LockStuffAndStopTheWorld(StopTheWorldCallback callback, + CheckForLeaksParam *argument) { LockThreadRegistry(); LockAllocator(); StopTheWorld(callback, argument); diff --git a/compiler-rt/lib/lsan/lsan_interceptors.cpp b/compiler-rt/lib/lsan/lsan_interceptors.cpp --- a/compiler-rt/lib/lsan/lsan_interceptors.cpp +++ b/compiler-rt/lib/lsan/lsan_interceptors.cpp @@ -22,7 +22,9 @@ #include "sanitizer_common/sanitizer_platform_interceptors.h" #include "sanitizer_common/sanitizer_platform_limits_netbsd.h" #include "sanitizer_common/sanitizer_platform_limits_posix.h" +#if SANITIZER_POSIX #include "sanitizer_common/sanitizer_posix.h" +#endif #include "sanitizer_common/sanitizer_tls_get_addr.h" #include "lsan.h" #include "lsan_allocator.h" @@ -61,6 +63,7 @@ } INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) { +#if !SANITIZER_FUCHSIA if (lsan_init_is_running) { // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym. const uptr kCallocPoolSize = 1024; @@ -72,6 +75,7 @@ CHECK(allocated < kCallocPoolSize); return mem; } +#endif // !SANITIZER_FUCHSIA ENSURE_LSAN_INITED; GET_STACK_TRACE_MALLOC; return lsan_calloc(nmemb, size, stack); @@ -100,7 +104,7 @@ GET_STACK_TRACE_MALLOC; return lsan_valloc(size, stack); } -#endif +#endif // !SANITIZER_MAC #if SANITIZER_INTERCEPT_MEMALIGN INTERCEPTOR(void*, memalign, uptr alignment, uptr size) { @@ -307,7 +311,7 @@ ///// Thread initialization and finalization. ///// -#if !SANITIZER_NETBSD && !SANITIZER_FREEBSD +#if !SANITIZER_NETBSD && !SANITIZER_FREEBSD && !SANITIZER_FUCHSIA static unsigned g_thread_finalize_key; static void thread_finalize(void *v) { @@ -394,6 +398,54 @@ #define LSAN_MAYBE_INTERCEPT_STRERROR #endif +#if SANITIZER_FUCHSIA + +// These are declared (in extern "C") by . +// The system runtime will call our definitions directly. + +// This is called before each thread creation is attempted. So, in +// its first call, the calling thread is the initial and sole thread. +void *__sanitizer_before_thread_create_hook(thrd_t thread, bool detached, + const char *name, void *stack_base, + size_t stack_size) { + uptr user_id = reinterpret_cast(thread); + ENSURE_LSAN_INITED; + EnsureMainThreadIDIsCorrect(); + ThreadContext::OnCreatedArgs args; + args.stack_begin = reinterpret_cast(stack_base); + args.stack_end = args.stack_begin + stack_size; + u32 tid = ThreadCreate(GetCurrentThread(), user_id, detached, &args); + return reinterpret_cast(static_cast(tid)); +} + +// This is called after creating a new thread (in the creating thread), +// with the pointer returned by __sanitizer_before_thread_create_hook (above). +void __sanitizer_thread_create_hook(void *hook, thrd_t thread, int error) { + u32 tid = static_cast(reinterpret_cast(hook)); + // On success, there is nothing to do here. + if (error != thrd_success) { + // Clean up the thread registry for the thread creation that didn't happen. + ThreadCreateAborted(tid); + } +} + +// This is called in the newly-created thread before it runs anything else, +// with the pointer returned by __sanitizer_before_thread_create_hook (above). +void __sanitizer_thread_start_hook(void *hook, thrd_t self) { + u32 tid = static_cast(reinterpret_cast(hook)); + SetCurrentThread(tid); + ThreadStart(tid, GetTid()); +} + +// Each thread runs this just before it exits, +// with the pointer returned by BeforeThreadCreateHook (above). +// All per-thread destructors have already been called. +void __sanitizer_thread_exit_hook(void *hook, thrd_t self) { + ThreadFinish(); +} + +#else // !SANITIZER_FUCHSIA + struct ThreadParam { void *(*callback)(void *arg); void *param; @@ -477,9 +529,12 @@ #define COMMON_INTERCEPT_FUNCTION(name) INTERCEPT_FUNCTION(name) #include "sanitizer_common/sanitizer_signal_interceptors.inc" +#endif // !SANITIZER_FUCHSIA + namespace __lsan { void InitializeInterceptors() { +#if !SANITIZER_FUCHSIA InitializeSignalInterceptors(); INTERCEPT_FUNCTION(malloc); @@ -515,6 +570,8 @@ Die(); } #endif + +#endif // !SANITIZER_FUCHSIA } } // namespace __lsan diff --git a/compiler-rt/lib/lsan/lsan_linux.cpp b/compiler-rt/lib/lsan/lsan_linux.cpp --- a/compiler-rt/lib/lsan/lsan_linux.cpp +++ b/compiler-rt/lib/lsan/lsan_linux.cpp @@ -6,13 +6,13 @@ // //===----------------------------------------------------------------------===// // -// This file is a part of LeakSanitizer. Linux/NetBSD-specific code. +// This file is a part of LeakSanitizer. Linux/NetBSD/Fuchsia-specific code. // //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_platform.h" -#if SANITIZER_LINUX || SANITIZER_NETBSD +#if SANITIZER_LINUX || SANITIZER_NETBSD || SANITIZER_FUCHSIA #include "lsan_allocator.h" @@ -29,4 +29,4 @@ } // namespace __lsan -#endif // SANITIZER_LINUX || SANITIZER_NETBSD +#endif // SANITIZER_LINUX || SANITIZER_NETBSD || SANITIZER_FUCHSIA diff --git a/compiler-rt/lib/lsan/lsan_thread.h b/compiler-rt/lib/lsan/lsan_thread.h --- a/compiler-rt/lib/lsan/lsan_thread.h +++ b/compiler-rt/lib/lsan/lsan_thread.h @@ -25,6 +25,7 @@ class ThreadContext : public ThreadContextBase { public: explicit ThreadContext(int tid); + void OnCreated(void *arg) override; void OnStarted(void *arg) override; void OnFinished() override; uptr stack_begin() { return stack_begin_; } @@ -35,6 +36,10 @@ uptr cache_end() { return cache_end_; } DTLS *dtls() { return dtls_; } + struct OnCreatedArgs { + uptr stack_begin, stack_end; + }; + private: uptr stack_begin_, stack_end_, cache_begin_, cache_end_, @@ -47,7 +52,8 @@ void ThreadStart(u32 tid, tid_t os_id, ThreadType thread_type = ThreadType::Regular); void ThreadFinish(); -u32 ThreadCreate(u32 tid, uptr uid, bool detached); +u32 ThreadCreate(u32 tid, uptr uid, bool detached, void *arg = nullptr); +void ThreadCreateAborted(u32 tid); void ThreadJoin(u32 tid); u32 ThreadTid(uptr uid); diff --git a/compiler-rt/lib/lsan/lsan_thread.cpp b/compiler-rt/lib/lsan/lsan_thread.cpp --- a/compiler-rt/lib/lsan/lsan_thread.cpp +++ b/compiler-rt/lib/lsan/lsan_thread.cpp @@ -55,12 +55,34 @@ DTLS *dtls; }; +// On Fuchsia, the stack bounds of a new thread are available before +// the thread itself has started running. +void ThreadContext::OnCreated(void *arg) { + auto args = reinterpret_cast(arg); +#if SANITIZER_FUCHSIA + if (args) { + // Stack bounds passed through from __sanitizer_before_thread_create_hook. + stack_begin_ = args->stack_begin; + stack_end_ = args->stack_end; + } else { + // This is the initial thread. + __sanitizer::GetThreadStackTopAndBottom(true, &stack_end_, &stack_begin_); + } +#else + CHECK_EQ(args, nullptr); +#endif +} + void ThreadContext::OnStarted(void *arg) { OnStartedArgs *args = reinterpret_cast(arg); +#if !SANITIZER_FUCHSIA + // These are set in OnCreated, above. stack_begin_ = args->stack_begin; stack_end_ = args->stack_end; + // These are never used on Fuchsia. tls_begin_ = args->tls_begin; tls_end_ = args->tls_end; +#endif cache_begin_ = args->cache_begin; cache_end_ = args->cache_end; dtls_ = args->dtls; @@ -71,19 +93,20 @@ DTLS_Destroy(); } -u32 ThreadCreate(u32 parent_tid, uptr user_id, bool detached) { - return thread_registry->CreateThread(user_id, detached, parent_tid, - /* arg */ nullptr); +u32 ThreadCreate(u32 parent_tid, uptr user_id, bool detached, void *arg) { + return thread_registry->CreateThread(user_id, detached, parent_tid, arg); } void ThreadStart(u32 tid, tid_t os_id, ThreadType thread_type) { OnStartedArgs args; +#if !SANITIZER_FUCHSIA uptr stack_size = 0; uptr tls_size = 0; GetThreadStackAndTls(tid == 0, &args.stack_begin, &stack_size, &args.tls_begin, &tls_size); args.stack_end = args.stack_begin + stack_size; args.tls_end = args.tls_begin + tls_size; +#endif GetAllocatorCacheRange(&args.cache_begin, &args.cache_end); args.dtls = DTLS_Get(); thread_registry->StartThread(tid, os_id, thread_type, &args); @@ -94,6 +117,10 @@ SetCurrentThread(kInvalidTid); } +void ThreadCreateAborted(u32 tid) { + thread_registry->FinishThread(tid); +} + ThreadContext *CurrentThreadContext() { if (!thread_registry) return nullptr; if (GetCurrentThread() == kInvalidTid) diff --git a/compiler-rt/lib/sanitizer_common/CMakeLists.txt b/compiler-rt/lib/sanitizer_common/CMakeLists.txt --- a/compiler-rt/lib/sanitizer_common/CMakeLists.txt +++ b/compiler-rt/lib/sanitizer_common/CMakeLists.txt @@ -35,6 +35,7 @@ sanitizer_procmaps_solaris.cpp sanitizer_rtems.cpp sanitizer_solaris.cpp + sanitizer_stoptheworld_fuchsia.cpp sanitizer_stoptheworld_mac.cpp sanitizer_suppressions.cpp sanitizer_tls_get_addr.cpp diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_fuchsia.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_fuchsia.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_fuchsia.cpp @@ -0,0 +1,42 @@ +//===-- sanitizer_stoptheworld_fuchsia.cpp -------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===---------------------------------------------------------------------===// +// +// See sanitizer_stoptheworld.h for details. +// +//===---------------------------------------------------------------------===// + +#include "sanitizer_platform.h" + +#if SANITIZER_FUCHSIA + +#include + +#include "sanitizer_stoptheworld.h" + +namespace __sanitizer { + +// The Fuchsia implementation stops the world but doesn't offer a real +// SuspendedThreadsList argument. This is enough for ASan's use case, +// and LSan does not use this API on Fuchsia. +void StopTheWorld(StopTheWorldCallback callback, void *argument) { + struct Params { + StopTheWorldCallback callback; + void *argument; + } params = {callback, argument}; + __sanitizer_memory_snapshot( + nullptr, nullptr, nullptr, nullptr, + [](zx_status_t, void *data) { + auto params = reinterpret_cast(data); + params->callback({}, params->argument); + }, + ¶ms); +} + +} // namespace __sanitizer + +#endif // SANITIZER_FUCHSIA