diff --git a/compiler-rt/lib/asan/asan_allocator.cpp b/compiler-rt/lib/asan/asan_allocator.cpp --- a/compiler-rt/lib/asan/asan_allocator.cpp +++ b/compiler-rt/lib/asan/asan_allocator.cpp @@ -1124,6 +1124,15 @@ return 0; } +void ForEachRegisteredThreadContextCb(ThreadContextBase *tctx, void *arg) { + // This function can be used to treat memory reachable from `tctx` as live. + // This is useful for threads that have been created but not yet started. + + // This is currently a no-op because the ASan `pthread_create()` interceptor + // blocks until the child thread starts which keeps the thread's `arg` pointer + // live. +} + uptr GetUserBegin(uptr chunk) { __asan::AsanChunk *m = __asan::instance.GetAsanChunkByAddrFastLocked(chunk); return m ? m->Beg() : 0; 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 @@ -20,6 +20,7 @@ #include "sanitizer_common/sanitizer_platform.h" #include "sanitizer_common/sanitizer_stoptheworld.h" #include "sanitizer_common/sanitizer_symbolizer.h" +#include "sanitizer_common/sanitizer_thread_registry.h" // LeakSanitizer relies on some Glibc's internals (e.g. TLS machinery) on Linux. // Also, LSan doesn't like 32 bit architectures @@ -142,6 +143,7 @@ void ScanRootRegion(Frontier *frontier, RootRegion const ®ion, uptr region_begin, uptr region_end, bool is_readable); void ForEachExtraStackRangeCb(uptr begin, uptr end, void* arg); +void ForEachRegisteredThreadContextCb(ThreadContextBase *tctx, void *arg); // Run stoptheworld while holding any platform-specific locks, as well as the // allocator and thread registry locks. void LockStuffAndStopTheWorld(StopTheWorldCallback callback, 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 @@ -520,6 +520,11 @@ ForEachChunk(MarkInvalidPCCb, &arg); } +void ProcessThreadRegistry(Frontier *frontier) { + GetThreadRegistryLocked()->RunCallbackForEachThreadLocked( + ForEachRegisteredThreadContextCb, frontier); +} + // Sets the appropriate tag on each chunk. static void ClassifyAllChunks(SuspendedThreadsList const &suspended_threads, Frontier *frontier) { @@ -532,6 +537,7 @@ ForEachChunk(CollectIgnoredCb, frontier); ProcessGlobalRegions(frontier); ProcessThreads(suspended_threads, frontier); + ProcessThreadRegistry(frontier); ProcessRootRegions(frontier); FloodFillTag(frontier, kReachable); 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 @@ -112,4 +112,13 @@ return thread_registry; } +void ForEachRegisteredThreadContextCb(ThreadContextBase *tctx, void *arg) { + // This function can be used to treat memory reachable from `tctx` as live. + // This is useful for threads that have been created but not yet started. + + // This is currently a no-op because the LSan `pthread_create()` interceptor + // blocks until the child thread starts which keeps the thread's `arg` pointer + // live. +} + } // namespace __lsan