From a6d061b71145fe321fa68ee2b290fc608a363eef Mon Sep 17 00:00:00 2001 From: Billy Laws Date: Sat, 17 Feb 2024 23:21:48 +0000 Subject: [PATCH] InvalidationTracker: Invalidate code across all threads When thread management was moved to the frontend, invalidation moved from being a global operation to per-thread but the WOW64 backend wasn't updated to account for this. Now for any invalidation event loop over all threads tracked by the frontend and invalidate the appropriate range. --- Source/Windows/Common/InvalidationTracker.cpp | 40 ++++++++++++++----- Source/Windows/Common/InvalidationTracker.h | 17 +++++--- Source/Windows/WOW64/Module.cpp | 39 +++++++++++------- 3 files changed, 68 insertions(+), 28 deletions(-) diff --git a/Source/Windows/Common/InvalidationTracker.cpp b/Source/Windows/Common/InvalidationTracker.cpp index 85074c26d..81b491d3c 100644 --- a/Source/Windows/Common/InvalidationTracker.cpp +++ b/Source/Windows/Common/InvalidationTracker.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include "InvalidationTracker.h" @@ -9,13 +10,19 @@ #include namespace FEX::Windows { -void InvalidationTracker::HandleMemoryProtectionNotification(FEXCore::Core::InternalThreadState* Thread, uint64_t Address, uint64_t Size, - ULONG Prot) { +InvalidationTracker::InvalidationTracker(FEXCore::Context::Context& CTX, const std::unordered_map& Threads) + : CTX {CTX} + , Threads {Threads} {} + +void InvalidationTracker::HandleMemoryProtectionNotification(uint64_t Address, uint64_t Size, ULONG Prot) { const auto AlignedBase = Address & FEXCore::Utils::FEX_PAGE_MASK; const auto AlignedSize = (Address - AlignedBase + Size + FEXCore::Utils::FEX_PAGE_SIZE - 1) & FEXCore::Utils::FEX_PAGE_MASK; if (Prot & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE)) { - Thread->CTX->InvalidateGuestCodeRange(Thread, AlignedBase, AlignedSize); + std::scoped_lock Lock(CTX.GetCodeInvalidationMutex()); + for (auto Thread : Threads) { + CTX.InvalidateGuestCodeRange(Thread.second, AlignedBase, AlignedSize); + } } if (Prot & PAGE_EXECUTE_READWRITE) { @@ -28,7 +35,7 @@ void InvalidationTracker::HandleMemoryProtectionNotification(FEXCore::Core::Inte } } -void InvalidationTracker::InvalidateContainingSection(FEXCore::Core::InternalThreadState* Thread, uint64_t Address, bool Free) { +void InvalidationTracker::InvalidateContainingSection(uint64_t Address, bool Free) { MEMORY_BASIC_INFORMATION Info; if (NtQueryVirtualMemory(NtCurrentProcess(), reinterpret_cast(Address), MemoryBasicInformation, &Info, sizeof(Info), nullptr)) { return; @@ -36,7 +43,13 @@ void InvalidationTracker::InvalidateContainingSection(FEXCore::Core::InternalThr const auto SectionBase = reinterpret_cast(Info.AllocationBase); const auto SectionSize = reinterpret_cast(Info.BaseAddress) + Info.RegionSize - reinterpret_cast(Info.AllocationBase); - Thread->CTX->InvalidateGuestCodeRange(Thread, SectionBase, SectionSize); + + { + std::scoped_lock Lock(CTX.GetCodeInvalidationMutex()); + for (auto Thread : Threads) { + CTX.InvalidateGuestCodeRange(Thread.second, SectionBase, SectionSize); + } + } if (Free) { std::scoped_lock Lock(RWXIntervalsLock); @@ -44,10 +57,16 @@ void InvalidationTracker::InvalidateContainingSection(FEXCore::Core::InternalThr } } -void InvalidationTracker::InvalidateAlignedInterval(FEXCore::Core::InternalThreadState* Thread, uint64_t Address, uint64_t Size, bool Free) { +void InvalidationTracker::InvalidateAlignedInterval(uint64_t Address, uint64_t Size, bool Free) { const auto AlignedBase = Address & FEXCore::Utils::FEX_PAGE_MASK; const auto AlignedSize = (Address - AlignedBase + Size + FEXCore::Utils::FEX_PAGE_SIZE - 1) & FEXCore::Utils::FEX_PAGE_MASK; - Thread->CTX->InvalidateGuestCodeRange(Thread, AlignedBase, AlignedSize); + + { + std::scoped_lock Lock(CTX.GetCodeInvalidationMutex()); + for (auto Thread : Threads) { + CTX.InvalidateGuestCodeRange(Thread.second, AlignedBase, AlignedSize); + } + } if (Free) { std::scoped_lock Lock(RWXIntervalsLock); @@ -75,7 +94,7 @@ void InvalidationTracker::ReprotectRWXIntervals(uint64_t Address, uint64_t Size) } while (Address < End); } -bool InvalidationTracker::HandleRWXAccessViolation(FEXCore::Core::InternalThreadState* Thread, uint64_t FaultAddress) { +bool InvalidationTracker::HandleRWXAccessViolation(uint64_t FaultAddress) { const bool NeedsInvalidate = [&](uint64_t Address) { std::unique_lock Lock(RWXIntervalsLock); const bool Enclosed = RWXIntervals.Query(Address).Enclosed; @@ -93,7 +112,10 @@ bool InvalidationTracker::HandleRWXAccessViolation(FEXCore::Core::InternalThread if (NeedsInvalidate) { // RWXIntervalsLock cannot be held during invalidation - Thread->CTX->InvalidateGuestCodeRange(Thread, FaultAddress & FEXCore::Utils::FEX_PAGE_MASK, FEXCore::Utils::FEX_PAGE_SIZE); + std::scoped_lock Lock(CTX.GetCodeInvalidationMutex()); + for (auto Thread : Threads) { + CTX.InvalidateGuestCodeRange(Thread.second, FaultAddress & FEXCore::Utils::FEX_PAGE_MASK, FEXCore::Utils::FEX_PAGE_SIZE); + } return true; } return false; diff --git a/Source/Windows/Common/InvalidationTracker.h b/Source/Windows/Common/InvalidationTracker.h index 4851730b6..f539c5b01 100644 --- a/Source/Windows/Common/InvalidationTracker.h +++ b/Source/Windows/Common/InvalidationTracker.h @@ -1,28 +1,35 @@ // SPDX-License-Identifier: MIT -// FIXME TODO put in cpp #pragma once #include "IntervalList.h" #include +#include namespace FEXCore::Core { struct InternalThreadState; } +namespace FEXCore::Context { +class Context; +} + namespace FEX::Windows { /** * @brief Handles SMC and regular code invalidation */ class InvalidationTracker { public: - void HandleMemoryProtectionNotification(FEXCore::Core::InternalThreadState* Thread, uint64_t Address, uint64_t Size, ULONG Prot); - void InvalidateContainingSection(FEXCore::Core::InternalThreadState* Thread, uint64_t Address, bool Free); - void InvalidateAlignedInterval(FEXCore::Core::InternalThreadState* Thread, uint64_t Address, uint64_t Size, bool Free); + InvalidationTracker(FEXCore::Context::Context& CTX, const std::unordered_map& Threads); + void HandleMemoryProtectionNotification(uint64_t Address, uint64_t Size, ULONG Prot); + void InvalidateContainingSection(uint64_t Address, bool Free); + void InvalidateAlignedInterval(uint64_t Address, uint64_t Size, bool Free); void ReprotectRWXIntervals(uint64_t Address, uint64_t Size); - bool HandleRWXAccessViolation(FEXCore::Core::InternalThreadState* Thread, uint64_t FaultAddress); + bool HandleRWXAccessViolation(uint64_t FaultAddress); private: IntervalList RWXIntervals; std::mutex RWXIntervalsLock; + FEXCore::Context::Context& CTX; + const std::unordered_map& Threads; }; } // namespace FEX::Windows diff --git a/Source/Windows/WOW64/Module.cpp b/Source/Windows/WOW64/Module.cpp index 972fc04a5..5bec8ceff 100644 --- a/Source/Windows/WOW64/Module.cpp +++ b/Source/Windows/WOW64/Module.cpp @@ -94,7 +94,7 @@ fextl::unique_ptr CTX; fextl::unique_ptr SignalDelegator; fextl::unique_ptr SyscallHandler; -FEX::Windows::InvalidationTracker InvalidationTracker; +std::optional InvalidationTracker; std::optional CPUFeatures; std::mutex ThreadCreationMutex; @@ -394,7 +394,7 @@ public: } void MarkGuestExecutableRange(FEXCore::Core::InternalThreadState* Thread, uint64_t Start, uint64_t Length) override { - InvalidationTracker.ReprotectRWXIntervals(Start, Length); + InvalidationTracker->ReprotectRWXIntervals(Start, Length); } }; @@ -423,6 +423,7 @@ void BTCpuProcessInit() { CTX->SetSignalDelegator(SignalDelegator.get()); CTX->SetSyscallHandler(SyscallHandler.get()); CTX->InitCore(); + InvalidationTracker.emplace(*CTX, Threads); CPUFeatures.emplace(*CTX); } @@ -617,15 +618,22 @@ NTSTATUS BTCpuResetToConsistentState(EXCEPTION_POINTERS* Ptrs) { if (Exception->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) { const auto FaultAddress = static_cast(Exception->ExceptionInformation[1]); - if (InvalidationTracker.HandleRWXAccessViolation(GetTLS().ThreadState(), FaultAddress)) { - LogMan::Msg::DFmt("Handled self-modifying code: pc: {:X} fault: {:X}", Context->Pc, FaultAddress); - NtContinue(Context, FALSE); - } if (Context::HandleSuspendInterrupt(Context, FaultAddress)) { LogMan::Msg::DFmt("Resumed from suspend"); NtContinue(Context, FALSE); } + + bool HandledRWX = false; + if (GetTLS().ThreadState()) { + std::scoped_lock Lock(ThreadCreationMutex); + HandledRWX = InvalidationTracker->HandleRWXAccessViolation(FaultAddress); + } + + if (HandledRWX) { + LogMan::Msg::DFmt("Handled self-modifying code: pc: {:X} fault: {:X}", Context->Pc, FaultAddress); + NtContinue(Context, FALSE); + } } if (!IsAddressInJit(Context->Pc)) { @@ -647,29 +655,32 @@ NTSTATUS BTCpuResetToConsistentState(EXCEPTION_POINTERS* Ptrs) { } void BTCpuFlushInstructionCache2(const void* Address, SIZE_T Size) { - InvalidationTracker.InvalidateAlignedInterval(GetTLS().ThreadState(), reinterpret_cast(Address), static_cast(Size), false); + std::scoped_lock Lock(ThreadCreationMutex); + InvalidationTracker->InvalidateAlignedInterval(reinterpret_cast(Address), static_cast(Size), false); } void BTCpuNotifyMemoryAlloc(void* Address, SIZE_T Size, ULONG Type, ULONG Prot) { - InvalidationTracker.HandleMemoryProtectionNotification(GetTLS().ThreadState(), reinterpret_cast(Address), - static_cast(Size), Prot); + std::scoped_lock Lock(ThreadCreationMutex); + InvalidationTracker->HandleMemoryProtectionNotification(reinterpret_cast(Address), static_cast(Size), Prot); } void BTCpuNotifyMemoryProtect(void* Address, SIZE_T Size, ULONG NewProt) { - InvalidationTracker.HandleMemoryProtectionNotification(GetTLS().ThreadState(), reinterpret_cast(Address), - static_cast(Size), NewProt); + std::scoped_lock Lock(ThreadCreationMutex); + InvalidationTracker->HandleMemoryProtectionNotification(reinterpret_cast(Address), static_cast(Size), NewProt); } void BTCpuNotifyMemoryFree(void* Address, SIZE_T Size, ULONG FreeType) { + std::scoped_lock Lock(ThreadCreationMutex); if (!Size) { - InvalidationTracker.InvalidateContainingSection(GetTLS().ThreadState(), reinterpret_cast(Address), true); + InvalidationTracker->InvalidateContainingSection(reinterpret_cast(Address), true); } else if (FreeType & MEM_DECOMMIT) { - InvalidationTracker.InvalidateAlignedInterval(GetTLS().ThreadState(), reinterpret_cast(Address), static_cast(Size), true); + InvalidationTracker->InvalidateAlignedInterval(reinterpret_cast(Address), static_cast(Size), true); } } void BTCpuNotifyUnmapViewOfSection(void* Address, ULONG Flags) { - InvalidationTracker.InvalidateContainingSection(GetTLS().ThreadState(), reinterpret_cast(Address), true); + std::scoped_lock Lock(ThreadCreationMutex); + InvalidationTracker->InvalidateContainingSection(reinterpret_cast(Address), true); } BOOLEAN WINAPI BTCpuIsProcessorFeaturePresent(UINT Feature) {