Skip to content

Commit

Permalink
Workaround broken special APC when running x64 on arm64 windows (#102333
Browse files Browse the repository at this point in the history
)

* Workaround broken special APC when running x64 on arm64 windows

In ARM64 windows older than 24H2, the special APC is broken when running
x64 emulation. The callback that gets invoked doesn't get an argument
with correct CONTEXT of the interrupted location. This change disables
using the special APC for runtime suspension when running on the
affected Windows versions.

Close #100425

* Make the same fix for NativeAOT
  • Loading branch information
janvorli authored May 16, 2024
1 parent b7727f5 commit 379f7b1
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 1 deletion.
26 changes: 25 additions & 1 deletion src/coreclr/nativeaot/Runtime/windows/PalRedhawkMinWin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@
#include "thread.h"
#include "threadstore.h"

#ifdef FEATURE_SPECIAL_USER_MODE_APC
#include <versionhelpers.h>
#endif

#define REDHAWK_PALEXPORT extern "C"
#define REDHAWK_PALAPI __stdcall

Expand Down Expand Up @@ -630,12 +634,32 @@ REDHAWK_PALEXPORT void REDHAWK_PALAPI PalHijack(HANDLE hThread, _In_opt_ void* p
_ASSERTE(hThread != INVALID_HANDLE_VALUE);

#ifdef FEATURE_SPECIAL_USER_MODE_APC

// initialize g_pfnQueueUserAPC2Proc on demand.
// Note that only one thread at a time may perform suspension (guaranteed by the thread store lock)
// so simple conditional assignment is ok.
if (g_pfnQueueUserAPC2Proc == QUEUE_USER_APC2_UNINITIALIZED)
{
g_pfnQueueUserAPC2Proc = (QueueUserAPC2Proc)GetProcAddress(LoadKernel32dll(), "QueueUserAPC2");
#ifdef HOST_AMD64
HMODULE hKernel32 = LoadKernel32dll();

typedef BOOL (WINAPI *IsWow64Process2Proc)(HANDLE hProcess, USHORT *pProcessMachine, USHORT *pNativeMachine);

IsWow64Process2Proc pfnIsWow64Process2Proc = (IsWow64Process2Proc)GetProcAddress(hKernel32, "IsWow64Process2");
USHORT processMachine, hostMachine;
if (pfnIsWow64Process2Proc != nullptr &&
(*pfnIsWow64Process2Proc)(GetCurrentProcess(), &processMachine, &hostMachine) &&
(hostMachine == IMAGE_FILE_MACHINE_ARM64) &&
!IsWindowsVersionOrGreater(10, 0, 26100))
{
// Special user-mode APCs are broken on WOW64 processes (x64 running on Arm64 machine) with Windows older than 11.0.26100 (24H2)
g_pfnQueueUserAPC2Proc = NULL;
}
else
#endif // HOST_AMD64
{
g_pfnQueueUserAPC2Proc = (QueueUserAPC2Proc)GetProcAddress(hKernel32, "QueueUserAPC2");
}
}

if (g_pfnQueueUserAPC2Proc)
Expand Down
16 changes: 16 additions & 0 deletions src/coreclr/vm/threads.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@

#ifdef FEATURE_SPECIAL_USER_MODE_APC
#include "asmconstants.h"
#include <versionhelpers.h>
#endif

static const PortableTailCallFrame g_sentinelTailCallFrame = { NULL, NULL };
Expand Down Expand Up @@ -8130,6 +8131,21 @@ void Thread::InitializeSpecialUserModeApc()

HMODULE hKernel32 = WszLoadLibrary(WINDOWS_KERNEL32_DLLNAME_W, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);

#ifdef HOST_AMD64
typedef BOOL (WINAPI *IsWow64Process2Proc)(HANDLE hProcess, USHORT *pProcessMachine, USHORT *pNativeMachine);

IsWow64Process2Proc pfnIsWow64Process2Proc = (IsWow64Process2Proc)GetProcAddress(hKernel32, "IsWow64Process2");
USHORT processMachine, hostMachine;
if (pfnIsWow64Process2Proc != nullptr &&
(*pfnIsWow64Process2Proc)(GetCurrentProcess(), &processMachine, &hostMachine) &&
(hostMachine == IMAGE_FILE_MACHINE_ARM64) &&
!IsWindowsVersionOrGreater(10, 0, 26100))
{
// Special user-mode APCs are broken on WOW64 processes (x64 running on Arm64 machine) with Windows older than 11.0.26100 (24H2)
return;
}
#endif // HOST_AMD64

// See if QueueUserAPC2 exists
QueueUserAPC2Proc pfnQueueUserAPC2Proc = (QueueUserAPC2Proc)GetProcAddress(hKernel32, "QueueUserAPC2");
if (pfnQueueUserAPC2Proc == nullptr)
Expand Down

0 comments on commit 379f7b1

Please sign in to comment.