diff --git a/FEXCore/include/FEXCore/HLE/SyscallHandler.h b/FEXCore/include/FEXCore/HLE/SyscallHandler.h index 024cff61f..185cce7a2 100644 --- a/FEXCore/include/FEXCore/HLE/SyscallHandler.h +++ b/FEXCore/include/FEXCore/HLE/SyscallHandler.h @@ -72,6 +72,8 @@ public: return OSABI; } virtual void MarkGuestExecutableRange(FEXCore::Core::InternalThreadState* Thread, uint64_t Start, uint64_t Length) {} + virtual void MarkOvercommitRange(uint64_t Start, uint64_t Length) {} + virtual void UnmarkOvercommitRange(uint64_t Start, uint64_t Length) {} virtual AOTIRCacheEntryLookupResult LookupAOTIRCacheEntry(FEXCore::Core::InternalThreadState* Thread, uint64_t GuestAddr) = 0; virtual SourcecodeResolver* GetSourcecodeResolver() { diff --git a/Source/Windows/ARM64EC/Module.cpp b/Source/Windows/ARM64EC/Module.cpp index 4fe74a723..8136a43fe 100644 --- a/Source/Windows/ARM64EC/Module.cpp +++ b/Source/Windows/ARM64EC/Module.cpp @@ -28,6 +28,7 @@ $end_info$ #include "Common/Config.h" #include "Common/Exception.h" #include "Common/InvalidationTracker.h" +#include "Common/OvercommitTracker.h" #include "Common/TSOHandlerConfig.h" #include "Common/CPUFeatures.h" #include "Common/Logging.h" @@ -119,6 +120,7 @@ fextl::unique_ptr SignalDelegator; fextl::unique_ptr SyscallHandler; std::optional InvalidationTracker; std::optional CPUFeatures; +std::optional OvercommitTracker; std::recursive_mutex ThreadCreationMutex; // Map of TIDs to their FEX thread state, `ThreadCreationMutex` must be locked when accessing @@ -425,6 +427,14 @@ public: void MarkGuestExecutableRange(FEXCore::Core::InternalThreadState* Thread, uint64_t Start, uint64_t Length) override { InvalidationTracker->ReprotectRWXIntervals(Start, Length); } + + void MarkOvercommitRange(uint64_t Start, uint64_t Length) override { + OvercommitTracker->MarkRange(Start, Length); + } + + void UnmarkOvercommitRange(uint64_t Start, uint64_t Length) override { + OvercommitTracker->UnmarkRange(Start, Length); + } }; } // namespace Exception @@ -459,8 +469,11 @@ NTSTATUS ProcessInit() { Exception::HandlerConfig.emplace(); const auto NtDll = GetModuleHandle("ntdll.dll"); + const bool IsWine = !!GetProcAddress(NtDll, "__wine_get_version"); + OvercommitTracker.emplace(IsWine); + { - auto HostFeatures = FEX::Windows::CPUFeatures::FetchHostFeatures(!!GetProcAddress(NtDll, "__wine_get_version")); + auto HostFeatures = FEX::Windows::CPUFeatures::FetchHostFeatures(IsWine); CTX = FEXCore::Context::Context::CreateNewContext(HostFeatures); } @@ -549,6 +562,14 @@ bool ResetToConsistentStateImpl(EXCEPTION_RECORD* Exception, CONTEXT* GuestConte } NTSTATUS ResetToConsistentState(EXCEPTION_RECORD* Exception, CONTEXT* GuestContext, ARM64_NT_CONTEXT* NativeContext) { + if (Exception->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) { + const auto FaultAddress = static_cast(Exception->ExceptionInformation[1]); + + if (OvercommitTracker && OvercommitTracker->HandleAccessViolation(FaultAddress)) { + NtContinueNative(NativeContext, false); + } + } + if (!GetCPUArea().ThreadState()) { return STATUS_SUCCESS; } diff --git a/Source/Windows/Common/OvercommitTracker.h b/Source/Windows/Common/OvercommitTracker.h new file mode 100644 index 000000000..63a707cd6 --- /dev/null +++ b/Source/Windows/Common/OvercommitTracker.h @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: MIT +#pragma once + +#include "IntervalList.h" +#include +#include + + +namespace FEX::Windows { +/** + * @brief Emulates memory overcommit of reserved regions with exceptions + */ +class OvercommitTracker { +private: + bool IsWine; + IntervalList OvercommitIntervals; + std::shared_mutex OvercommitIntervalsMutex; + +public: + OvercommitTracker(bool IsWine) + : IsWine {IsWine} {} + + void MarkRange(uint64_t Start, uint64_t Length) { + std::unique_lock Lock {OvercommitIntervalsMutex}; + OvercommitIntervals.Insert({Start, Start + Length}); + } + + void UnmarkRange(uint64_t Start, uint64_t Length) { + std::unique_lock Lock {OvercommitIntervalsMutex}; + OvercommitIntervals.Remove({Start, Start + Length}); + } + + bool HandleAccessViolation(uint64_t FaultAddress) { + std::shared_lock Lock {OvercommitIntervalsMutex}; + auto Query = OvercommitIntervals.Query(FaultAddress); + + if (Query.Enclosed) { + if (IsWine) { + MEMORY_BASIC_INFORMATION Info; + NtQueryVirtualMemory(NtCurrentProcess(), reinterpret_cast(FaultAddress), MemoryBasicInformation, &Info, sizeof(Info), nullptr); + const auto CommitSize = reinterpret_cast(Info.BaseAddress) + Info.RegionSize - FaultAddress; + VirtualAlloc(reinterpret_cast(Info.AllocationBase), CommitSize, MEM_COMMIT, PAGE_READWRITE); + } else { + static constexpr size_t MaxFaultCommitSize = 1024 * 64; + const auto AlignedFaultAddress = reinterpret_cast(FaultAddress & FEXCore::Utils::FEX_PAGE_MASK); + VirtualAlloc(AlignedFaultAddress, std::min(Query.Size, MaxFaultCommitSize), MEM_COMMIT, PAGE_READWRITE); + } + return true; + } + return false; + } +}; +} // namespace FEX::Windows diff --git a/Source/Windows/WOW64/Module.cpp b/Source/Windows/WOW64/Module.cpp index 8dd78ab76..b411c4345 100644 --- a/Source/Windows/WOW64/Module.cpp +++ b/Source/Windows/WOW64/Module.cpp @@ -30,6 +30,7 @@ $end_info$ #include "Common/Exception.h" #include "Common/TSOHandlerConfig.h" #include "Common/InvalidationTracker.h" +#include "Common/OvercommitTracker.h" #include "Common/CPUFeatures.h" #include "Common/Logging.h" #include "Common/Module.h" @@ -102,6 +103,7 @@ fextl::unique_ptr SyscallHandler; std::optional InvalidationTracker; std::optional CPUFeatures; +std::optional OvercommitTracker; std::mutex ThreadCreationMutex; // Map of TIDs to their FEX thread state, `ThreadCreationMutex` must be locked when accessing @@ -422,6 +424,14 @@ public: void MarkGuestExecutableRange(FEXCore::Core::InternalThreadState* Thread, uint64_t Start, uint64_t Length) override { InvalidationTracker->ReprotectRWXIntervals(Start, Length); } + + void MarkOvercommitRange(uint64_t Start, uint64_t Length) override { + OvercommitTracker->MarkRange(Start, Length); + } + + void UnmarkOvercommitRange(uint64_t Start, uint64_t Length) override { + OvercommitTracker->UnmarkRange(Start, Length); + } }; void BTCpuProcessInit() { @@ -443,9 +453,11 @@ void BTCpuProcessInit() { SyscallHandler = fextl::make_unique(); Context::HandlerConfig.emplace(); const auto NtDll = GetModuleHandle("ntdll.dll"); + const bool IsWine = !!GetProcAddress(NtDll, "__wine_get_version"); + OvercommitTracker.emplace(IsWine); { - auto HostFeatures = FEX::Windows::CPUFeatures::FetchHostFeatures(!!GetProcAddress(NtDll, "__wine_get_version")); + auto HostFeatures = FEX::Windows::CPUFeatures::FetchHostFeatures(IsWine); CTX = FEXCore::Context::Context::CreateNewContext(HostFeatures); } @@ -656,6 +668,10 @@ bool BTCpuResetToConsistentStateImpl(EXCEPTION_POINTERS* Ptrs) { if (Exception->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) { const auto FaultAddress = static_cast(Exception->ExceptionInformation[1]); + if (OvercommitTracker && OvercommitTracker->HandleAccessViolation(FaultAddress)) { + return true; + } + if (Context::HandleSuspendInterrupt(Context, FaultAddress)) { LogMan::Msg::DFmt("Resumed from suspend"); return true;