diff options
author | Martin Liska <mliska@suse.cz> | 2020-06-01 21:15:18 +0200 |
---|---|---|
committer | Martin Liska <mliska@suse.cz> | 2020-06-02 08:02:07 +0200 |
commit | 3c6331c29f1376ed220246e7dead94bc527a9aa9 (patch) | |
tree | df4288713dd999fd2b0783cf0f82a7a127ed5964 /libsanitizer/lsan | |
parent | 2b11374cb8d864dff3792e7c84188ba7e8f136e7 (diff) | |
download | gcc-3c6331c29f1376ed220246e7dead94bc527a9aa9.zip gcc-3c6331c29f1376ed220246e7dead94bc527a9aa9.tar.gz gcc-3c6331c29f1376ed220246e7dead94bc527a9aa9.tar.bz2 |
Libsanitizer: merge from master.
Merged from revision b638b63b99d66786cb37336292604a2ae3490cfd.
The patch successfully bootstraps on x86_64-linux-gnu and
ppc64le-linux-gnu. I also tested ppc64-linux-gnu that exposed:
https://reviews.llvm.org/D80864 (which is fixed on master).
Abidiff looks happy and I made UBSAN and ASAN bootstrap on
x86_64-linux-gnu.
I'm planning to do merge from master twice a year, once now and
next time short before stage1 closes.
I am going to install the patches as merge from master is obvious
and I haven't made anything special.
libsanitizer/ChangeLog:
* MERGE: Merge from master.
Diffstat (limited to 'libsanitizer/lsan')
-rw-r--r-- | libsanitizer/lsan/lsan.cpp | 17 | ||||
-rw-r--r-- | libsanitizer/lsan/lsan.h | 6 | ||||
-rw-r--r-- | libsanitizer/lsan/lsan_allocator.h | 5 | ||||
-rw-r--r-- | libsanitizer/lsan/lsan_common.cpp | 51 | ||||
-rw-r--r-- | libsanitizer/lsan/lsan_common.h | 17 | ||||
-rw-r--r-- | libsanitizer/lsan/lsan_common_fuchsia.cpp | 166 | ||||
-rw-r--r-- | libsanitizer/lsan/lsan_common_linux.cpp | 3 | ||||
-rw-r--r-- | libsanitizer/lsan/lsan_common_mac.cpp | 3 | ||||
-rw-r--r-- | libsanitizer/lsan/lsan_fuchsia.cpp | 123 | ||||
-rw-r--r-- | libsanitizer/lsan/lsan_fuchsia.h | 35 | ||||
-rw-r--r-- | libsanitizer/lsan/lsan_interceptors.cpp | 19 | ||||
-rw-r--r-- | libsanitizer/lsan/lsan_linux.cpp | 6 | ||||
-rw-r--r-- | libsanitizer/lsan/lsan_posix.cpp | 96 | ||||
-rw-r--r-- | libsanitizer/lsan/lsan_posix.h | 49 | ||||
-rw-r--r-- | libsanitizer/lsan/lsan_thread.cpp | 98 | ||||
-rw-r--r-- | libsanitizer/lsan/lsan_thread.h | 35 |
16 files changed, 589 insertions, 140 deletions
diff --git a/libsanitizer/lsan/lsan.cpp b/libsanitizer/lsan/lsan.cpp index 4ce0304..80a6e2f 100644 --- a/libsanitizer/lsan/lsan.cpp +++ b/libsanitizer/lsan/lsan.cpp @@ -15,7 +15,6 @@ #include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_flag_parser.h" -#include "sanitizer_common/sanitizer_stacktrace.h" #include "lsan_allocator.h" #include "lsan_common.h" #include "lsan_thread.h" @@ -87,17 +86,6 @@ static void InitializeFlags() { __sanitizer_set_report_path(common_flags()->log_path); } -static void OnStackUnwind(const SignalContext &sig, const void *, - BufferedStackTrace *stack) { - stack->Unwind(StackTrace::GetNextInstructionPc(sig.pc), sig.bp, sig.context, - common_flags()->fast_unwind_on_fatal); -} - -static void LsanOnDeadlySignal(int signo, void *siginfo, void *context) { - HandleDeadlySignal(siginfo, context, GetCurrentThread(), &OnStackUnwind, - nullptr); -} - extern "C" void __lsan_init() { CHECK(!lsan_init_is_running); if (lsan_inited) @@ -114,10 +102,7 @@ extern "C" void __lsan_init() { InitializeInterceptors(); InitializeThreadRegistry(); InstallDeadlySignalHandlers(LsanOnDeadlySignal); - u32 tid = ThreadCreate(0, 0, true); - CHECK_EQ(tid, 0); - ThreadStart(tid, GetTid()); - SetCurrentThread(tid); + InitializeMainThread(); if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit) Atexit(DoLeakCheck); diff --git a/libsanitizer/lsan/lsan.h b/libsanitizer/lsan/lsan.h index 9904ada..1e82ad7 100644 --- a/libsanitizer/lsan/lsan.h +++ b/libsanitizer/lsan/lsan.h @@ -12,6 +12,11 @@ //===----------------------------------------------------------------------===// #include "lsan_thread.h" +#if SANITIZER_POSIX +#include "lsan_posix.h" +#elif SANITIZER_FUCHSIA +#include "lsan_fuchsia.h" +#endif #include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_stacktrace.h" @@ -33,6 +38,7 @@ namespace __lsan { void InitializeInterceptors(); void ReplaceSystemMalloc(); +void LsanOnDeadlySignal(int signo, void *siginfo, void *context); #define ENSURE_LSAN_INITED do { \ CHECK(!lsan_init_is_running); \ diff --git a/libsanitizer/lsan/lsan_allocator.h b/libsanitizer/lsan/lsan_allocator.h index e139709..bda9d8c 100644 --- a/libsanitizer/lsan/lsan_allocator.h +++ b/libsanitizer/lsan/lsan_allocator.h @@ -66,7 +66,10 @@ template <typename AddressSpaceView> using PrimaryAllocatorASVT = SizeClassAllocator32<AP32<AddressSpaceView>>; using PrimaryAllocator = PrimaryAllocatorASVT<LocalAddressSpaceView>; #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/libsanitizer/lsan/lsan_common.cpp b/libsanitizer/lsan/lsan_common.cpp index 9ff9f4c..32ea4e8 100644 --- a/libsanitizer/lsan/lsan_common.cpp +++ b/libsanitizer/lsan/lsan_common.cpp @@ -211,6 +211,13 @@ void ForEachExtraStackRangeCb(uptr begin, uptr end, void* arg) { 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 @@ static void ProcessThreads(SuspendedThreadsList const &suspended_threads, } } +#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 @@ void ProcessPC(Frontier *frontier) { } // 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,11 +528,6 @@ static void PrintMatchedSuppressions() { Printf("%s\n\n", line); } -struct CheckForLeaksParam { - bool success; - LeakReport leak_report; -}; - static void ReportIfNotSuspended(ThreadContextBase *tctx, void *arg) { const InternalMmapVector<tid_t> &suspended_threads = *(const InternalMmapVector<tid_t> *)arg; @@ -538,6 +540,14 @@ static void ReportIfNotSuspended(ThreadContextBase *tctx, void *arg) { } } +#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 // !SANITIZER_FUCHSIA + static void ReportUnsuspendedThreads( const SuspendedThreadsList &suspended_threads) { InternalMmapVector<tid_t> threads(suspended_threads.ThreadCount()); @@ -550,13 +560,15 @@ static void ReportUnsuspendedThreads( &ReportIfNotSuspended, &threads); } +#endif // !SANITIZER_FUCHSIA + static void CheckForLeaksCallback(const SuspendedThreadsList &suspended_threads, void *arg) { CheckForLeaksParam *param = reinterpret_cast<CheckForLeaksParam *>(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 @@ static bool CheckForLeaks() { return false; EnsureMainThreadIDIsCorrect(); CheckForLeaksParam param; - param.success = false; LockStuffAndStopTheWorld(CheckForLeaksCallback, ¶m); if (!param.success) { diff --git a/libsanitizer/lsan/lsan_common.h b/libsanitizer/lsan/lsan_common.h index d24abe3..6252d52 100644 --- a/libsanitizer/lsan/lsan_common.h +++ b/libsanitizer/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,24 @@ struct RootRegion { uptr size; }; +// LockStuffAndStopTheWorld can start to use Scan* calls to collect into +// this Frontier vector before the StopTheWorldCallback actually runs. +// This is used when the OS has a unified callback API for suspending +// threads and enumerating roots. +struct CheckForLeaksParam { + Frontier frontier; + LeakReport leak_report; + bool success = false; +}; + InternalMmapVector<RootRegion> 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, @@ -211,6 +223,7 @@ ThreadRegistry *GetThreadRegistryLocked(); bool GetThreadRangesLocked(tid_t os_id, uptr *stack_begin, uptr *stack_end, uptr *tls_begin, uptr *tls_end, uptr *cache_begin, uptr *cache_end, DTLS **dtls); +void GetAllThreadAllocatorCachesLocked(InternalMmapVector<uptr> *caches); void ForEachExtraStackRange(tid_t os_id, RangeIteratorCallback callback, void *arg); // If called from the main thread, updates the main thread's TID in the thread diff --git a/libsanitizer/lsan/lsan_common_fuchsia.cpp b/libsanitizer/lsan/lsan_common_fuchsia.cpp new file mode 100644 index 0000000..caedbf1 --- /dev/null +++ b/libsanitizer/lsan/lsan_common_fuchsia.cpp @@ -0,0 +1,166 @@ +//=-- 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 <zircon/sanitizer.h> + +#include "lsan_allocator.h" +#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 { + InternalMmapVector<uptr> allocator_caches; + 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<const Params *>(data); + uptr begin = reinterpret_cast<uptr>(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<const Params *>(data); + uptr begin = reinterpret_cast<uptr>(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<const Params *>(data); + uptr begin = reinterpret_cast<uptr>(chunk); + uptr end = begin + size; + ScanRangeForPointers(begin, end, ¶ms->argument->frontier, "REGISTERS", + kReachable); + }; + + if (flags()->use_tls) { + // Collect the allocator cache range from each thread so these + // can all be excluded from the reported TLS ranges. + GetAllThreadAllocatorCachesLocked(¶ms.allocator_caches); + __sanitizer::Sort(params.allocator_caches.data(), + params.allocator_caches.size()); + } + + // 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<const Params *>(data); + uptr begin = reinterpret_cast<uptr>(chunk); + uptr end = begin + size; + auto i = __sanitizer::InternalLowerBound(params->allocator_caches, 0, + params->allocator_caches.size(), + begin, CompareLess<uptr>()); + if (i < params->allocator_caches.size() && + params->allocator_caches[i] >= begin && + end - params->allocator_caches[i] <= sizeof(AllocatorCache)) { + // Split the range in two and omit the allocator cache within. + ScanRangeForPointers(begin, params->allocator_caches[i], + ¶ms->argument->frontier, "TLS", kReachable); + uptr begin2 = params->allocator_caches[i] + sizeof(AllocatorCache); + ScanRangeForPointers(begin2, end, ¶ms->argument->frontier, "TLS", + kReachable); + } else { + 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<const Params *>(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 for the allocator cache, and 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 <zircon/sanitizer.h>. +// _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/libsanitizer/lsan/lsan_common_linux.cpp b/libsanitizer/lsan/lsan_common_linux.cpp index ea1a4a2..c97ef315 100644 --- a/libsanitizer/lsan/lsan_common_linux.cpp +++ b/libsanitizer/lsan/lsan_common_linux.cpp @@ -134,7 +134,8 @@ static int LockStuffAndStopTheWorldCallback(struct dl_phdr_info *info, // 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/libsanitizer/lsan/lsan_common_mac.cpp b/libsanitizer/lsan/lsan_common_mac.cpp index c1804e9..8516a17 100644 --- a/libsanitizer/lsan/lsan_common_mac.cpp +++ b/libsanitizer/lsan/lsan_common_mac.cpp @@ -193,7 +193,8 @@ void ProcessPlatformSpecificAllocations(Frontier *frontier) { // 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/libsanitizer/lsan/lsan_fuchsia.cpp b/libsanitizer/lsan/lsan_fuchsia.cpp new file mode 100644 index 0000000..40e65c6 --- /dev/null +++ b/libsanitizer/lsan/lsan_fuchsia.cpp @@ -0,0 +1,123 @@ +//=-- lsan_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. +// Standalone LSan RTL code specific to Fuchsia. +// +//===---------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" + +#if SANITIZER_FUCHSIA +#include <zircon/sanitizer.h> + +#include "lsan.h" +#include "lsan_allocator.h" + +using namespace __lsan; + +namespace __lsan { + +void LsanOnDeadlySignal(int signo, void *siginfo, void *context) {} + +ThreadContext::ThreadContext(int tid) : ThreadContextLsanBase(tid) {} + +struct OnCreatedArgs { + uptr stack_begin, stack_end; +}; + +// On Fuchsia, the stack bounds of a new thread are available before +// the thread itself has started running. +void ThreadContext::OnCreated(void *arg) { + // Stack bounds passed through from __sanitizer_before_thread_create_hook + // or InitializeMainThread. + auto args = reinterpret_cast<const OnCreatedArgs *>(arg); + stack_begin_ = args->stack_begin; + stack_end_ = args->stack_end; +} + +struct OnStartedArgs { + uptr cache_begin, cache_end; +}; + +void ThreadContext::OnStarted(void *arg) { + auto args = reinterpret_cast<const OnStartedArgs *>(arg); + cache_begin_ = args->cache_begin; + cache_end_ = args->cache_end; +} + +void ThreadStart(u32 tid) { + OnStartedArgs args; + GetAllocatorCacheRange(&args.cache_begin, &args.cache_end); + CHECK_EQ(args.cache_end - args.cache_begin, sizeof(AllocatorCache)); + ThreadContextLsanBase::ThreadStart(tid, GetTid(), ThreadType::Regular, &args); +} + +void InitializeMainThread() { + OnCreatedArgs args; + __sanitizer::GetThreadStackTopAndBottom(true, &args.stack_end, + &args.stack_begin); + u32 tid = ThreadCreate(0, GetThreadSelf(), true, &args); + CHECK_EQ(tid, 0); + ThreadStart(tid); +} + +void GetAllThreadAllocatorCachesLocked(InternalMmapVector<uptr> *caches) { + GetThreadRegistryLocked()->RunCallbackForEachThreadLocked( + [](ThreadContextBase *tctx, void *arg) { + auto ctx = static_cast<ThreadContext *>(tctx); + static_cast<decltype(caches)>(arg)->push_back(ctx->cache_begin()); + }, + caches); +} + +} // namespace __lsan + +// These are declared (in extern "C") by <zircon/sanitizer.h>. +// 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<uptr>(thread); + ENSURE_LSAN_INITED; + EnsureMainThreadIDIsCorrect(); + OnCreatedArgs args; + args.stack_begin = reinterpret_cast<uptr>(stack_base); + args.stack_end = args.stack_begin + stack_size; + u32 parent_tid = GetCurrentThread(); + u32 tid = ThreadCreate(parent_tid, user_id, detached, &args); + return reinterpret_cast<void *>(static_cast<uptr>(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<u32>(reinterpret_cast<uptr>(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. + GetThreadRegistryLocked()->FinishThread(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<u32>(reinterpret_cast<uptr>(hook)); + ThreadStart(tid); +} + +// 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(); } + +#endif // SANITIZER_FUCHSIA diff --git a/libsanitizer/lsan/lsan_fuchsia.h b/libsanitizer/lsan/lsan_fuchsia.h new file mode 100644 index 0000000..65d20ea --- /dev/null +++ b/libsanitizer/lsan/lsan_fuchsia.h @@ -0,0 +1,35 @@ +//=-- lsan_fuchsia.h ---------------------------------------------------===// +// +// 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. +// Standalone LSan RTL code specific to Fuchsia. +// +//===---------------------------------------------------------------------===// + +#ifndef LSAN_FUCHSIA_H +#define LSAN_FUCHSIA_H + +#include "lsan_thread.h" +#include "sanitizer_common/sanitizer_platform.h" + +#if !SANITIZER_FUCHSIA +#error "lsan_fuchsia.h is used only on Fuchsia systems (SANITIZER_FUCHSIA)" +#endif + +namespace __lsan { + +class ThreadContext : public ThreadContextLsanBase { + public: + explicit ThreadContext(int tid); + void OnCreated(void *arg) override; + void OnStarted(void *arg) override; +}; + +} // namespace __lsan + +#endif // LSAN_FUCHSIA_H diff --git a/libsanitizer/lsan/lsan_interceptors.cpp b/libsanitizer/lsan/lsan_interceptors.cpp index f642bb8..9ce9b78 100644 --- a/libsanitizer/lsan/lsan_interceptors.cpp +++ b/libsanitizer/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,9 @@ INTERCEPTOR(void, free, void *p) { } INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) { + // This hack is not required for Fuchsia because there are no dlsym calls + // involved in setting up interceptors. +#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 +77,7 @@ INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) { CHECK(allocated < kCallocPoolSize); return mem; } +#endif // !SANITIZER_FUCHSIA ENSURE_LSAN_INITED; GET_STACK_TRACE_MALLOC; return lsan_calloc(nmemb, size, stack); @@ -100,7 +106,7 @@ INTERCEPTOR(void*, valloc, uptr size) { 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 +313,7 @@ INTERCEPTOR(void, _ZdaPvRKSt9nothrow_t, void *ptr, std::nothrow_t const&) ///// 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 +400,8 @@ INTERCEPTOR(char *, strerror, int errnum) { #define LSAN_MAYBE_INTERCEPT_STRERROR #endif +#if SANITIZER_POSIX + struct ThreadParam { void *(*callback)(void *arg); void *param; @@ -416,7 +424,6 @@ extern "C" void *__lsan_thread_start_func(void *arg) { int tid = 0; while ((tid = atomic_load(&p->tid, memory_order_acquire)) == 0) internal_sched_yield(); - SetCurrentThread(tid); ThreadStart(tid, GetTid()); atomic_store(&p->tid, 0, memory_order_release); return callback(param); @@ -477,9 +484,13 @@ INTERCEPTOR(void, _exit, int status) { #define COMMON_INTERCEPT_FUNCTION(name) INTERCEPT_FUNCTION(name) #include "sanitizer_common/sanitizer_signal_interceptors.inc" +#endif // SANITIZER_POSIX + namespace __lsan { void InitializeInterceptors() { + // Fuchsia doesn't use interceptors that require any setup. +#if !SANITIZER_FUCHSIA InitializeSignalInterceptors(); INTERCEPT_FUNCTION(malloc); @@ -515,6 +526,8 @@ void InitializeInterceptors() { Die(); } #endif + +#endif // !SANITIZER_FUCHSIA } } // namespace __lsan diff --git a/libsanitizer/lsan/lsan_linux.cpp b/libsanitizer/lsan/lsan_linux.cpp index 14a42b7..47c2f21 100644 --- a/libsanitizer/lsan/lsan_linux.cpp +++ b/libsanitizer/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 @@ void ReplaceSystemMalloc() {} } // namespace __lsan -#endif // SANITIZER_LINUX || SANITIZER_NETBSD +#endif // SANITIZER_LINUX || SANITIZER_NETBSD || SANITIZER_FUCHSIA diff --git a/libsanitizer/lsan/lsan_posix.cpp b/libsanitizer/lsan/lsan_posix.cpp new file mode 100644 index 0000000..8e05915 --- /dev/null +++ b/libsanitizer/lsan/lsan_posix.cpp @@ -0,0 +1,96 @@ +//=-- lsan_posix.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. +// Standalone LSan RTL code common to POSIX-like systems. +// +//===---------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" + +#if SANITIZER_POSIX +#include "lsan.h" +#include "lsan_allocator.h" +#include "sanitizer_common/sanitizer_stacktrace.h" +#include "sanitizer_common/sanitizer_tls_get_addr.h" + +namespace __lsan { + +ThreadContext::ThreadContext(int tid) : ThreadContextLsanBase(tid) {} + +struct OnStartedArgs { + uptr stack_begin; + uptr stack_end; + uptr cache_begin; + uptr cache_end; + uptr tls_begin; + uptr tls_end; + DTLS *dtls; +}; + +void ThreadContext::OnStarted(void *arg) { + auto args = reinterpret_cast<const OnStartedArgs *>(arg); + stack_begin_ = args->stack_begin; + stack_end_ = args->stack_end; + tls_begin_ = args->tls_begin; + tls_end_ = args->tls_end; + cache_begin_ = args->cache_begin; + cache_end_ = args->cache_end; + dtls_ = args->dtls; +} + +void ThreadStart(u32 tid, tid_t os_id, ThreadType thread_type) { + OnStartedArgs args; + 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; + GetAllocatorCacheRange(&args.cache_begin, &args.cache_end); + args.dtls = DTLS_Get(); + ThreadContextLsanBase::ThreadStart(tid, os_id, thread_type, &args); +} + +bool GetThreadRangesLocked(tid_t os_id, uptr *stack_begin, uptr *stack_end, + uptr *tls_begin, uptr *tls_end, uptr *cache_begin, + uptr *cache_end, DTLS **dtls) { + ThreadContext *context = static_cast<ThreadContext *>( + GetThreadRegistryLocked()->FindThreadContextByOsIDLocked(os_id)); + if (!context) + return false; + *stack_begin = context->stack_begin(); + *stack_end = context->stack_end(); + *tls_begin = context->tls_begin(); + *tls_end = context->tls_end(); + *cache_begin = context->cache_begin(); + *cache_end = context->cache_end(); + *dtls = context->dtls(); + return true; +} + +void InitializeMainThread() { + u32 tid = ThreadCreate(0, 0, true); + CHECK_EQ(tid, 0); + ThreadStart(tid, GetTid()); +} + +static void OnStackUnwind(const SignalContext &sig, const void *, + BufferedStackTrace *stack) { + stack->Unwind(StackTrace::GetNextInstructionPc(sig.pc), sig.bp, sig.context, + common_flags()->fast_unwind_on_fatal); +} + +void LsanOnDeadlySignal(int signo, void *siginfo, void *context) { + HandleDeadlySignal(siginfo, context, GetCurrentThread(), &OnStackUnwind, + nullptr); +} + +} // namespace __lsan + +#endif // SANITIZER_POSIX diff --git a/libsanitizer/lsan/lsan_posix.h b/libsanitizer/lsan/lsan_posix.h new file mode 100644 index 0000000..840e427 --- /dev/null +++ b/libsanitizer/lsan/lsan_posix.h @@ -0,0 +1,49 @@ +//=-- lsan_posix.h -----------------------------------------------------===// +// +// 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. +// Standalone LSan RTL code common to POSIX-like systems. +// +//===---------------------------------------------------------------------===// + +#ifndef LSAN_POSIX_H +#define LSAN_POSIX_H + +#include "lsan_thread.h" +#include "sanitizer_common/sanitizer_platform.h" + +#if !SANITIZER_POSIX +#error "lsan_posix.h is used only on POSIX-like systems (SANITIZER_POSIX)" +#endif + +namespace __sanitizer { +struct DTLS; +} + +namespace __lsan { + +class ThreadContext : public ThreadContextLsanBase { + public: + explicit ThreadContext(int tid); + void OnStarted(void *arg) override; + uptr tls_begin() { return tls_begin_; } + uptr tls_end() { return tls_end_; } + DTLS *dtls() { return dtls_; } + + private: + uptr tls_begin_ = 0; + uptr tls_end_ = 0; + DTLS *dtls_ = nullptr; +}; + +void ThreadStart(u32 tid, tid_t os_id, + ThreadType thread_type = ThreadType::Regular); + +} // namespace __lsan + +#endif // LSAN_POSIX_H diff --git a/libsanitizer/lsan/lsan_thread.cpp b/libsanitizer/lsan/lsan_thread.cpp index 84e7ce6..40bdc25 100644 --- a/libsanitizer/lsan/lsan_thread.cpp +++ b/libsanitizer/lsan/lsan_thread.cpp @@ -13,12 +13,13 @@ #include "lsan_thread.h" +#include "lsan.h" +#include "lsan_allocator.h" +#include "lsan_common.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_placement_new.h" #include "sanitizer_common/sanitizer_thread_registry.h" #include "sanitizer_common/sanitizer_tls_get_addr.h" -#include "lsan_allocator.h" -#include "lsan_common.h" namespace __lsan { @@ -26,7 +27,7 @@ static ThreadRegistry *thread_registry; static ThreadContextBase *CreateThreadContext(u32 tid) { void *mem = MmapOrDie(sizeof(ThreadContext), "ThreadContext"); - return new(mem) ThreadContext(tid); + return new (mem) ThreadContext(tid); } static const uptr kMaxThreads = 1 << 13; @@ -34,59 +35,26 @@ static const uptr kThreadQuarantineSize = 64; void InitializeThreadRegistry() { static ALIGNED(64) char thread_registry_placeholder[sizeof(ThreadRegistry)]; - thread_registry = new(thread_registry_placeholder) - ThreadRegistry(CreateThreadContext, kMaxThreads, kThreadQuarantineSize); + thread_registry = new (thread_registry_placeholder) + ThreadRegistry(CreateThreadContext, kMaxThreads, kThreadQuarantineSize); } -ThreadContext::ThreadContext(int tid) - : ThreadContextBase(tid), - stack_begin_(0), - stack_end_(0), - cache_begin_(0), - cache_end_(0), - tls_begin_(0), - tls_end_(0), - dtls_(nullptr) {} - -struct OnStartedArgs { - uptr stack_begin, stack_end, - cache_begin, cache_end, - tls_begin, tls_end; - DTLS *dtls; -}; - -void ThreadContext::OnStarted(void *arg) { - OnStartedArgs *args = reinterpret_cast<OnStartedArgs *>(arg); - stack_begin_ = args->stack_begin; - stack_end_ = args->stack_end; - tls_begin_ = args->tls_begin; - tls_end_ = args->tls_end; - cache_begin_ = args->cache_begin; - cache_end_ = args->cache_end; - dtls_ = args->dtls; -} +ThreadContextLsanBase::ThreadContextLsanBase(int tid) + : ThreadContextBase(tid) {} -void ThreadContext::OnFinished() { +void ThreadContextLsanBase::OnFinished() { AllocatorThreadFinish(); 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; - 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; - GetAllocatorCacheRange(&args.cache_begin, &args.cache_end); - args.dtls = DTLS_Get(); - thread_registry->StartThread(tid, os_id, thread_type, &args); +void ThreadContextLsanBase::ThreadStart(u32 tid, tid_t os_id, + ThreadType thread_type, void *arg) { + thread_registry->StartThread(tid, os_id, thread_type, arg); + SetCurrentThread(tid); } void ThreadFinish() { @@ -95,7 +63,8 @@ void ThreadFinish() { } ThreadContext *CurrentThreadContext() { - if (!thread_registry) return nullptr; + if (!thread_registry) + return nullptr; if (GetCurrentThread() == kInvalidTid) return nullptr; // No lock needed when getting current thread. @@ -111,12 +80,12 @@ static bool FindThreadByUid(ThreadContextBase *tctx, void *arg) { } u32 ThreadTid(uptr uid) { - return thread_registry->FindThread(FindThreadByUid, (void*)uid); + return thread_registry->FindThread(FindThreadByUid, (void *)uid); } void ThreadJoin(u32 tid) { CHECK_NE(tid, kInvalidTid); - thread_registry->JoinThread(tid, /* arg */nullptr); + thread_registry->JoinThread(tid, /* arg */ nullptr); } void EnsureMainThreadIDIsCorrect() { @@ -126,37 +95,16 @@ void EnsureMainThreadIDIsCorrect() { ///// Interface to the common LSan module. ///// -bool GetThreadRangesLocked(tid_t os_id, uptr *stack_begin, uptr *stack_end, - uptr *tls_begin, uptr *tls_end, uptr *cache_begin, - uptr *cache_end, DTLS **dtls) { - ThreadContext *context = static_cast<ThreadContext *>( - thread_registry->FindThreadContextByOsIDLocked(os_id)); - if (!context) return false; - *stack_begin = context->stack_begin(); - *stack_end = context->stack_end(); - *tls_begin = context->tls_begin(); - *tls_end = context->tls_end(); - *cache_begin = context->cache_begin(); - *cache_end = context->cache_end(); - *dtls = context->dtls(); - return true; -} - void ForEachExtraStackRange(tid_t os_id, RangeIteratorCallback callback, - void *arg) { -} + void *arg) {} -void LockThreadRegistry() { - thread_registry->Lock(); -} +void LockThreadRegistry() { thread_registry->Lock(); } -void UnlockThreadRegistry() { - thread_registry->Unlock(); -} +void UnlockThreadRegistry() { thread_registry->Unlock(); } ThreadRegistry *GetThreadRegistryLocked() { thread_registry->CheckLocked(); return thread_registry; } -} // namespace __lsan +} // namespace __lsan diff --git a/libsanitizer/lsan/lsan_thread.h b/libsanitizer/lsan/lsan_thread.h index b869d06..0ab1582 100644 --- a/libsanitizer/lsan/lsan_thread.h +++ b/libsanitizer/lsan/lsan_thread.h @@ -16,38 +16,36 @@ #include "sanitizer_common/sanitizer_thread_registry.h" -namespace __sanitizer { -struct DTLS; -} - namespace __lsan { -class ThreadContext : public ThreadContextBase { +class ThreadContextLsanBase : public ThreadContextBase { public: - explicit ThreadContext(int tid); - void OnStarted(void *arg) override; + explicit ThreadContextLsanBase(int tid); void OnFinished() override; uptr stack_begin() { return stack_begin_; } uptr stack_end() { return stack_end_; } - uptr tls_begin() { return tls_begin_; } - uptr tls_end() { return tls_end_; } uptr cache_begin() { return cache_begin_; } uptr cache_end() { return cache_end_; } - DTLS *dtls() { return dtls_; } - private: - uptr stack_begin_, stack_end_, - cache_begin_, cache_end_, - tls_begin_, tls_end_; - DTLS *dtls_; + // The argument is passed on to the subclass's OnStarted member function. + static void ThreadStart(u32 tid, tid_t os_id, ThreadType thread_type, + void *onstarted_arg); + + protected: + uptr stack_begin_ = 0; + uptr stack_end_ = 0; + uptr cache_begin_ = 0; + uptr cache_end_ = 0; }; +// This subclass of ThreadContextLsanBase is declared in an OS-specific header. +class ThreadContext; + void InitializeThreadRegistry(); +void InitializeMainThread(); -void ThreadStart(u32 tid, tid_t os_id, - ThreadType thread_type = ThreadType::Regular); +u32 ThreadCreate(u32 tid, uptr uid, bool detached, void *arg = nullptr); void ThreadFinish(); -u32 ThreadCreate(u32 tid, uptr uid, bool detached); void ThreadJoin(u32 tid); u32 ThreadTid(uptr uid); @@ -55,6 +53,7 @@ u32 GetCurrentThread(); void SetCurrentThread(u32 tid); ThreadContext *CurrentThreadContext(); void EnsureMainThreadIDIsCorrect(); + } // namespace __lsan #endif // LSAN_THREAD_H |