Index: clang/lib/Driver/ToolChains/MSVC.cpp =================================================================== --- clang/lib/Driver/ToolChains/MSVC.cpp +++ clang/lib/Driver/ToolChains/MSVC.cpp @@ -1416,6 +1416,7 @@ Res |= SanitizerKind::PointerSubtract; Res |= SanitizerKind::Fuzzer; Res |= SanitizerKind::FuzzerNoLink; + Res |= SanitizerKind::Leak; Res &= ~SanitizerKind::CFIMFCall; return Res; } Index: compiler-rt/cmake/config-ix.cmake =================================================================== --- compiler-rt/cmake/config-ix.cmake +++ compiler-rt/cmake/config-ix.cmake @@ -709,7 +709,7 @@ endif() if (COMPILER_RT_HAS_SANITIZER_COMMON AND LSAN_SUPPORTED_ARCH AND - OS_NAME MATCHES "Android|Darwin|Linux|NetBSD|Fuchsia") + OS_NAME MATCHES "Android|Darwin|Linux|NetBSD|Fuchsia|Windows") set(COMPILER_RT_HAS_LSAN TRUE) else() set(COMPILER_RT_HAS_LSAN FALSE) Index: compiler-rt/lib/lsan/CMakeLists.txt =================================================================== --- compiler-rt/lib/lsan/CMakeLists.txt +++ compiler-rt/lib/lsan/CMakeLists.txt @@ -11,6 +11,7 @@ lsan_common_fuchsia.cpp lsan_common_linux.cpp lsan_common_mac.cpp + lsan_common_win.cpp ) set(LSAN_SOURCES @@ -24,6 +25,7 @@ lsan_posix.cpp lsan_preinit.cpp lsan_thread.cpp + lsan_win.cpp ) set(LSAN_HEADERS Index: compiler-rt/lib/lsan/lsan.h =================================================================== --- compiler-rt/lib/lsan/lsan.h +++ compiler-rt/lib/lsan/lsan.h @@ -16,6 +16,8 @@ #include "lsan_posix.h" #elif SANITIZER_FUCHSIA #include "lsan_fuchsia.h" +#elif SANITIZER_WINDOWS +# include "lsan_win.h" #endif #include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_stacktrace.h" Index: compiler-rt/lib/lsan/lsan_allocator.cpp =================================================================== --- compiler-rt/lib/lsan/lsan_allocator.cpp +++ compiler-rt/lib/lsan/lsan_allocator.cpp @@ -27,11 +27,11 @@ namespace __lsan { #if defined(__i386__) || defined(__arm__) -static const uptr kMaxAllowedMallocSize = 1UL << 30; +static const uptr kMaxAllowedMallocSize = static_cast(1) << 30; #elif defined(__mips64) || defined(__aarch64__) -static const uptr kMaxAllowedMallocSize = 4UL << 30; +static const uptr kMaxAllowedMallocSize = static_cast(4) << 30; #else -static const uptr kMaxAllowedMallocSize = 8UL << 30; +static const uptr kMaxAllowedMallocSize = static_cast(8) << 30; #endif static Allocator allocator; Index: compiler-rt/lib/lsan/lsan_common.h =================================================================== --- compiler-rt/lib/lsan/lsan_common.h +++ compiler-rt/lib/lsan/lsan_common.h @@ -44,8 +44,8 @@ #define CAN_SANITIZE_LEAKS 1 #elif SANITIZER_RISCV64 && SANITIZER_LINUX #define CAN_SANITIZE_LEAKS 1 -#elif SANITIZER_NETBSD || SANITIZER_FUCHSIA -#define CAN_SANITIZE_LEAKS 1 +#elif SANITIZER_NETBSD || SANITIZER_FUCHSIA || SANITIZER_WINDOWS +# define CAN_SANITIZE_LEAKS 1 #else #define CAN_SANITIZE_LEAKS 0 #endif Index: compiler-rt/lib/lsan/lsan_common.cpp =================================================================== --- compiler-rt/lib/lsan/lsan_common.cpp +++ compiler-rt/lib/lsan/lsan_common.cpp @@ -408,6 +408,7 @@ kReachable); } +# if !SANITIZER_WINDOWS static void ProcessRootRegion(Frontier *frontier, const RootRegion &root_region) { MemoryMappingLayout proc_maps(/*cache_enabled*/ true); @@ -424,6 +425,7 @@ for (uptr i = 0; i < root_regions.size(); i++) ProcessRootRegion(frontier, root_regions[i]); } +# endif static void FloodFillTag(Frontier *frontier, ChunkTag tag) { while (frontier->size()) { @@ -551,7 +553,9 @@ ForEachChunk(CollectIgnoredCb, frontier); ProcessGlobalRegions(frontier); ProcessThreads(suspended_threads, frontier); +# if !SANITIZER_WINDOWS ProcessRootRegions(frontier); +# endif FloodFillTag(frontier, kReachable); CHECK_EQ(0, frontier->size()); Index: compiler-rt/lib/lsan/lsan_common_win.cpp =================================================================== --- /dev/null +++ compiler-rt/lib/lsan/lsan_common_win.cpp @@ -0,0 +1,55 @@ +//=-- lsan_common_win.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. Darwin-specific code. +// +//===----------------------------------------------------------------------===// + +#include "lsan_common.h" +#include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_platform.h" + +#if CAN_SANITIZE_LEAKS && SANITIZER_WINDOWS + +# include "lsan_allocator.h" +# include "sanitizer_common/sanitizer_allocator_internal.h" + +namespace __lsan { + +void HandleLeaks() {} + +void InitializePlatformSpecificModules() {} + +void LockStuffAndStopTheWorld(StopTheWorldCallback callback, + CheckForLeaksParam *argument) { + LockThreadRegistry(); + LockAllocator(); + StopTheWorld(callback, argument); + UnlockAllocator(); + UnlockThreadRegistry(); +} + +THREADLOCAL int disable_counter; +bool DisabledInThisThread() { return disable_counter > 0; } +void DisableInThisThread() { disable_counter++; } +void EnableInThisThread() { + if (disable_counter == 0) { + DisableCounterUnderflow(); + } + disable_counter--; +} + +void ProcessGlobalRegions(Frontier *frontier) {} +void ProcessPlatformSpecificAllocations(Frontier *frontier) {} + +LoadedModule *GetLinker() { return nullptr; } + +} // namespace __lsan + +#endif // CAN_SANITIZE_LEAKS && SANITIZER_WINDOWS Index: compiler-rt/lib/lsan/lsan_interceptors.cpp =================================================================== --- compiler-rt/lib/lsan/lsan_interceptors.cpp +++ compiler-rt/lib/lsan/lsan_interceptors.cpp @@ -326,7 +326,8 @@ ///// Thread initialization and finalization. ///// -#if !SANITIZER_NETBSD && !SANITIZER_FREEBSD && !SANITIZER_FUCHSIA +#if !SANITIZER_NETBSD && !SANITIZER_FREEBSD && !SANITIZER_FUCHSIA && \ + !SANITIZER_WINDOWS static unsigned g_thread_finalize_key; static void thread_finalize(void *v) { @@ -499,7 +500,16 @@ void InitializeInterceptors() { // Fuchsia doesn't use interceptors that require any setup. -#if !SANITIZER_FUCHSIA + +#if SANITIZER_WINDOWS + INTERCEPT_FUNCTION(malloc); + INTERCEPT_FUNCTION(free); + INTERCEPT_FUNCTION(calloc); + INTERCEPT_FUNCTION(realloc); + LSAN_MAYBE_INTERCEPT_MEMALIGN; + INTERCEPT_FUNCTION(valloc); + LSAN_MAYBE_INTERCEPT_ALIGNED_ALLOC; +#elif !SANITIZER_FUCHSIA InitializeSignalInterceptors(); INTERCEPT_FUNCTION(malloc); @@ -529,12 +539,12 @@ LSAN_MAYBE_INTERCEPT_STRERROR; -#if !SANITIZER_NETBSD && !SANITIZER_FREEBSD +# if !SANITIZER_NETBSD && !SANITIZER_FREEBSD if (pthread_key_create(&g_thread_finalize_key, &thread_finalize)) { Report("LeakSanitizer: failed to create thread key.\n"); Die(); } -#endif +# endif #endif // !SANITIZER_FUCHSIA } Index: compiler-rt/lib/lsan/lsan_win.h =================================================================== --- /dev/null +++ compiler-rt/lib/lsan/lsan_win.h @@ -0,0 +1,41 @@ +//=-- lsan_win.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 Windows systems. +// +//===---------------------------------------------------------------------===// + +#ifndef LSAN_WINDOWS_H +#define LSAN_WINDOWS_H + +#include "lsan_thread.h" +#include "sanitizer_common/sanitizer_platform.h" + +#if !SANITIZER_WINDOWS +# error "lsan_win.h is used only on Windows systems (SANITIZER_WINDOWS)" +#endif + +namespace __sanitizer { +struct DTLS; +} + +namespace __lsan { + +class ThreadContext final : public ThreadContextLsanBase { + public: + explicit ThreadContext(int tid); + void OnStarted(void *arg) override; +}; + +void ThreadStart(u32 tid, tid_t os_id, + ThreadType thread_type = ThreadType::Regular); + +} // namespace __lsan + +#endif // LSAN_WINDOWS_H Index: compiler-rt/lib/lsan/lsan_win.cpp =================================================================== --- /dev/null +++ compiler-rt/lib/lsan/lsan_win.cpp @@ -0,0 +1,106 @@ +//=-- lsan_win.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_WINDOWS +# 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; +}; + +void ThreadContext::OnStarted(void *arg) { + auto args = reinterpret_cast(arg); + stack_begin_ = args->stack_begin; + stack_end_ = args->stack_end; + cache_begin_ = args->cache_begin; + cache_end_ = args->cache_end; +} + +void ThreadStart(u32 tid, tid_t os_id, ThreadType thread_type) { + OnStartedArgs args; + uptr stack_size = 0; + uptr tls_size = 0; + GetThreadStackAndTls(tid == kMainTid, &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); + 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( + GetThreadRegistryLocked()->FindThreadContextByOsIDLocked(os_id)); + if (!context) + return false; + *stack_begin = context->stack_begin(); + *stack_end = context->stack_end(); + *cache_begin = context->cache_begin(); + *cache_end = context->cache_end(); + *dtls = 0; + return true; +} + +void InitializeMainThread() { + u32 tid = ThreadCreate(kMainTid, true); + CHECK_EQ(tid, kMainTid); + 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); +} + +void ReplaceSystemMalloc() {} + +static THREADLOCAL u32 current_thread_tid = kInvalidTid; +u32 GetCurrentThread() { return current_thread_tid; } +void SetCurrentThread(u32 tid) { current_thread_tid = tid; } + +static THREADLOCAL AllocatorCache allocator_cache; +AllocatorCache *GetAllocatorCache() { return &allocator_cache; } + +} // namespace __lsan + +int lsan_win_init() { + __lsan_init(); + return 0; +} + +# pragma section(".CRT$XIB", long, read) +__declspec(allocate(".CRT$XIB")) int (*__lsan_preinit)() = lsan_win_init; + +#endif // SANITIZER_WINDOWS Index: compiler-rt/lib/sanitizer_common/CMakeLists.txt =================================================================== --- compiler-rt/lib/sanitizer_common/CMakeLists.txt +++ compiler-rt/lib/sanitizer_common/CMakeLists.txt @@ -34,6 +34,7 @@ sanitizer_solaris.cpp sanitizer_stoptheworld_fuchsia.cpp sanitizer_stoptheworld_mac.cpp + sanitizer_stoptheworld_win.cpp sanitizer_suppressions.cpp sanitizer_tls_get_addr.cpp sanitizer_thread_registry.cpp Index: compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_win.cpp =================================================================== --- /dev/null +++ compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_win.cpp @@ -0,0 +1,138 @@ +//===-- sanitizer_stoptheworld_win.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_common.h" +#include "sanitizer_internal_defs.h" +#include "sanitizer_platform.h" + +#if SANITIZER_WINDOWS + +# define WIN32_LEAN_AND_MEAN +# include + +// windows.h needs to be included before tlhelp32.h +# include + +# include "sanitizer_stoptheworld.h" + +namespace __sanitizer { + +struct SuspendedThreadsListWindows final : public SuspendedThreadsList { + InternalMmapVector threadHandles; + InternalMmapVector threadIds; + + SuspendedThreadsListWindows() { + threadIds.reserve(1024); + threadHandles.reserve(1024); + } + + PtraceRegistersStatus GetRegistersAndSP(uptr index, + InternalMmapVector *buffer, + uptr *sp) const override; + + tid_t GetThreadID(uptr index) const override; + uptr ThreadCount() const override; +}; + +PtraceRegistersStatus SuspendedThreadsListWindows::GetRegistersAndSP( + uptr index, InternalMmapVector *buffer, uptr *sp) const { + CHECK_LT(index, threadHandles.size()); + + CONTEXT thread_context; + thread_context.ContextFlags = CONTEXT_ALL; + CHECK(GetThreadContext(threadHandles[index], &thread_context)); + + buffer->resize(RoundUpTo(sizeof(thread_context), sizeof(uptr)) / + sizeof(uptr)); + internal_memcpy(buffer->data(), &thread_context, sizeof(thread_context)); + + *sp = thread_context.Rsp; + + return REGISTERS_AVAILABLE; +} + +tid_t SuspendedThreadsListWindows::GetThreadID(uptr index) const { + CHECK_LT(index, threadIds.size()); + return threadIds[index]; +} + +uptr SuspendedThreadsListWindows::ThreadCount() const { + return threadIds.size(); +} + +struct RunThreadArgs { + StopTheWorldCallback callback; + void *argument; +}; + +DWORD WINAPI RunThread(void *argument) { + RunThreadArgs *run_args = (RunThreadArgs *)argument; + const HANDLE threads = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); + + CHECK(threads != INVALID_HANDLE_VALUE); + + const DWORD this_thread = GetCurrentThreadId(); + const DWORD this_process = GetCurrentProcessId(); + + SuspendedThreadsListWindows suspended_threads_list; + + THREADENTRY32 thread_entry; + thread_entry.dwSize = sizeof(thread_entry); + if (Thread32First(threads, &thread_entry)) { + do { + if (thread_entry.dwSize >= + FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + + sizeof(thread_entry.th32OwnerProcessID)) { + if (thread_entry.th32ThreadID == this_thread || + thread_entry.th32OwnerProcessID != this_process) + continue; + + const HANDLE thread = + OpenThread(THREAD_ALL_ACCESS, FALSE, thread_entry.th32ThreadID); + CHECK(thread); + SuspendThread(thread); + + suspended_threads_list.threadIds.push_back(thread_entry.th32ThreadID); + suspended_threads_list.threadHandles.push_back(thread); + } + thread_entry.dwSize = sizeof(thread_entry); + } while (Thread32Next(threads, &thread_entry)); + } + + run_args->callback(suspended_threads_list, run_args->argument); + + for (const auto suspended_thread_handle : + suspended_threads_list.threadHandles) { + ResumeThread(suspended_thread_handle); + CloseHandle(suspended_thread_handle); + } + + CloseHandle(threads); + + return 0; +} + +void StopTheWorld(StopTheWorldCallback callback, void *argument) { + struct RunThreadArgs arg = {callback, argument}; + DWORD trace_thread_id; + + auto trace_thread = + CreateThread(nullptr, 0, RunThread, &arg, 0, &trace_thread_id); + CHECK(trace_thread); + + WaitForSingleObject(trace_thread, INFINITE); + CloseHandle(trace_thread); +} + +} // namespace __sanitizer + +#endif // SANITIZER_WINDOWS Index: compiler-rt/test/lsan/lit.common.cfg.py =================================================================== --- compiler-rt/test/lsan/lit.common.cfg.py +++ compiler-rt/test/lsan/lit.common.cfg.py @@ -79,7 +79,8 @@ supported_linux = (not config.android) and config.host_os == 'Linux' and config.host_arch in ['aarch64', 'x86_64', 'ppc64', 'ppc64le', 'mips64', 'riscv64', 'arm', 'armhf', 'armv7l', 's390x'] supported_darwin = config.host_os == 'Darwin' and config.target_arch in ['x86_64'] supported_netbsd = config.host_os == 'NetBSD' and config.target_arch in ['x86_64', 'i386'] -if not (supported_android or supported_linux or supported_darwin or supported_netbsd): +supported_windows = config.host_os == 'Windows' +if not (supported_android or supported_linux or supported_darwin or supported_netbsd or supported_windows): config.unsupported = True # Don't support Thumb due to broken fast unwinder