Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support managed debugging when CET is enabled #73572

Merged
merged 5 commits into from
Aug 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/coreclr/clrdefinitions.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ if(CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_I386 OR CLR_CMAKE_TARGET
endif(CLR_CMAKE_TARGET_WIN32)
endif(CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_I386 OR CLR_CMAKE_TARGET_ARCH_ARM64)

if(CLR_CMAKE_TARGET_WIN32 AND CLR_CMAKE_TARGET_ARCH_AMD64)
add_compile_definitions(OUT_OF_PROCESS_SETTHREADCONTEXT)
endif(CLR_CMAKE_TARGET_WIN32)

# Features - please keep them alphabetically sorted
if(CLR_CMAKE_TARGET_WIN32)
if(NOT CLR_CMAKE_TARGET_ARCH_I386)
Expand Down
166 changes: 166 additions & 0 deletions src/coreclr/debug/di/process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7661,6 +7661,8 @@ HRESULT CordbProcess::GetRuntimeOffsets()
m_runtimeOffsets.m_debuggerWordTLSIndex));
#endif // FEATURE_INTEROP_DEBUGGING

LOG((LF_CORDB, LL_INFO10000, " m_setThreadContextNeededAddr= 0x%p\n",
m_runtimeOffsets.m_setThreadContextNeededAddr));
LOG((LF_CORDB, LL_INFO10000, " m_TLSIndex= 0x%08x\n",
m_runtimeOffsets.m_TLSIndex));
LOG((LF_CORDB, LL_INFO10000, " m_EEThreadStateOffset= 0x%08x\n",
Expand Down Expand Up @@ -7719,6 +7721,7 @@ HRESULT CordbProcess::GetRuntimeOffsets()
m_runtimeOffsets.m_signalHijackCompleteBPAddr,
m_runtimeOffsets.m_excepNotForRuntimeBPAddr,
m_runtimeOffsets.m_notifyRSOfSyncCompleteBPAddr,
m_runtimeOffsets.m_setThreadContextNeededAddr,
};

const int NumFlares = ARRAY_SIZE(flares);
Expand Down Expand Up @@ -11152,7 +11155,162 @@ void CordbProcess::FilterClrNotification(
}
}

#ifdef OUT_OF_PROCESS_SETTHREADCONTEXT
void CordbProcess::HandleSetThreadContextNeeded(DWORD dwThreadId)
{
LOG((LF_CORDB, LL_INFO10000, "RS HandleSetThreadContextNeeded\n"));

#if defined(TARGET_WINDOWS) && defined(TARGET_AMD64)
HandleHolder hThread = OpenThread(
THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_QUERY_INFORMATION | THREAD_SUSPEND_RESUME,
FALSE, // thread handle is not inheritable.
dwThreadId);

if (hThread == NULL)
{
LOG((LF_CORDB, LL_INFO10000, "RS HandleSetThreadContextNeeded - Unexpected result from OpenThread\n"));
ThrowHR(E_UNEXPECTED);
}

DWORD previousSuspendCount = ::SuspendThread(hThread);
if (previousSuspendCount == (DWORD)-1)
{
LOG((LF_CORDB, LL_INFO10000, "RS HandleSetThreadContextNeeded - Unexpected result from SuspendThread\n"));
ThrowHR(HRESULT_FROM_GetLastError());
}

CONTEXT context = { 0 };
context.ContextFlags = CONTEXT_FULL;

HRESULT hr = GetDataTarget()->GetThreadContext(dwThreadId, CONTEXT_FULL, sizeof(CONTEXT), reinterpret_cast<BYTE*> (&context));
IfFailThrow(hr);

TADDR lsContextAddr = (TADDR)context.Rcx;
DWORD contextSize = (DWORD)context.Rdx;

TADDR expectedRip = (TADDR)context.R8;
TADDR expectedRsp = (TADDR)context.R9;

if (contextSize == 0 || contextSize > sizeof(CONTEXT) + 25000)
{
_ASSERTE(!"Corrupted HandleSetThreadContextNeeded message received");

LOG((LF_CORDB, LL_INFO10000, "RS HandleSetThreadContextNeeded - Corrupted HandleSetThreadContextNeeded message received\n"));

ThrowHR(E_UNEXPECTED);
}

PCONTEXT pContext = (PCONTEXT)_alloca(contextSize);
ULONG32 cbRead;
hr = GetDataTarget()->ReadVirtual(lsContextAddr, reinterpret_cast<BYTE*>(pContext), contextSize, &cbRead);
if (FAILED(hr))
{
_ASSERTE(!"ReadVirtual failed");

LOG((LF_CORDB, LL_INFO10000, "RS HandleSetThreadContextNeeded - ReadVirtual (error: 0x%X).\n", hr));

ThrowHR(CORDBG_E_READVIRTUAL_FAILURE);
}

if (cbRead != contextSize)
{
_ASSERTE(!"ReadVirtual context size mismatch");

LOG((LF_CORDB, LL_INFO10000, "RS HandleSetThreadContextNeeded - ReadVirtual context size mismatch\n"));

ThrowHR(ERROR_PARTIAL_COPY);
}

if (pContext->Rip != expectedRip || pContext->Rsp != expectedRsp)
{
_ASSERTE(!"ReadVirtual unexpectedly returned mismatched Rip and Rsp registers");

LOG((LF_CORDB, LL_INFO10000, "RS HandleSetThreadContextNeeded - ReadVirtual unexpectedly returned mismatched Rip and Rsp registers\n"));

ThrowHR(E_UNEXPECTED);
}

// TODO: Ideally we would use ICorDebugMutableDataTarget::SetThreadContext however this API currently only handles the legacy context.
// We should combine the following code with the shared implementation

// The initialize call should fail but return contextSize
contextSize = 0;
DWORD contextFlags = pContext->ContextFlags;
BOOL success = InitializeContext(NULL, contextFlags, NULL, &contextSize);

if(success || GetLastError() != ERROR_INSUFFICIENT_BUFFER)
{
_ASSERTE(!"InitializeContext unexpectedly succeeded or didn't return ERROR_INSUFFICIENT_BUFFER");

LOG((LF_CORDB, LL_INFO10000, "RS HandleSetThreadContextNeeded - InitializeContext unexpectedly succeeded or didn't return ERROR_INSUFFICIENT_BUFFER\n"));

ThrowHR(E_UNEXPECTED);
}

LOG((LF_CORDB, LL_INFO10000, "RS HandleSetThreadContextNeeded - InitializeContext ContextSize %d\n", contextSize));

PVOID pBuffer = _alloca(contextSize);
PCONTEXT pFrameContext = NULL;
success = InitializeContext(pBuffer, contextFlags, &pFrameContext, &contextSize);
if (!success)
{
HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
_ASSERTE(!"InitializeContext failed");

LOG((LF_CORDB, LL_INFO10000, "RS HandleSetThreadContextNeeded - Unexpected result from InitializeContext (error: 0x%X [%d]).\n", hr, GetLastError()));

ThrowHR(hr);
}

_ASSERTE((BYTE*)pFrameContext == pBuffer);

success = CopyContext(pFrameContext, contextFlags, pContext);
LOG((LF_CORDB, LL_INFO10000, "RS HandleSetThreadContextNeeded - CopyContext=%s %d\n", success?"SUCCESS":"FAIL", GetLastError()));
if (!success)
{
HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
_ASSERTE(!"CopyContext failed");

LOG((LF_CORDB, LL_INFO10000, "RS HandleSetThreadContextNeeded - Unexpected result from CopyContext (error: 0x%X [%d]).\n", hr, GetLastError()));

ThrowHR(hr);
}

LOG((LF_CORDB, LL_INFO10000, "RS HandleSetThreadContextNeeded - Set Thread Context - ID = 0x%X, SS enabled = %d\n", dwThreadId, /*(uint64_t)hThread,*/ (pContext->EFlags & 0x100) != 0));

DWORD lastError = 0;

success = ::SetThreadContext(hThread, pFrameContext);
if (!success)
{
lastError = ::GetLastError();
}

LOG((LF_CORDB, LL_INFO10000, "RS HandleSetThreadContextNeeded - Set Thread Context Completed: Success=%d GetLastError=%d hr=0x%X\n", success, lastError, HRESULT_FROM_WIN32(lastError)));
_ASSERTE(success);

DWORD suspendCount = ::ResumeThread(hThread);
if (suspendCount == (DWORD)-1)
{
LOG((LF_CORDB, LL_INFO10000, "RS HandleSetThreadContextNeeded - Unexpected result from ResumeThread\n"));
ThrowHR(HRESULT_FROM_GetLastError());
}
if (suspendCount != previousSuspendCount + 1)
{
LOG((LF_CORDB, LL_INFO10000, "RS HandleSetThreadContextNeeded - Unexpected result from ResumeThread\n"));
ThrowHR(E_UNEXPECTED);
}

if (!success)
{
LOG((LF_CORDB, LL_INFO10000, "RS HandleSetThreadContextNeeded - Unexpected result from SetThreadContext\n"));
ThrowHR(HRESULT_FROM_WIN32(lastError));
}
#else
#error Platform not supported
#endif
}
#endif // OUT_OF_PROCESS_SETTHREADCONTEXT

//
// If the thread has an unhandled managed exception, hijack it.
Expand Down Expand Up @@ -11377,7 +11535,15 @@ HRESULT CordbProcess::Filter(

// holder will invoke DeleteIPCEventHelper(pManagedEvent).
}
#ifdef OUT_OF_PROCESS_SETTHREADCONTEXT
else if (dwFirstChance && pRecord->ExceptionCode == STATUS_BREAKPOINT && pRecord->ExceptionAddress == m_runtimeOffsets.m_setThreadContextNeededAddr)
{
// this is a request to set the thread context out of process

HandleSetThreadContextNeeded(dwThreadId);
*pContinueStatus = DBG_CONTINUE;
}
#endif
}
PUBLIC_API_END(hr);
// we may not find the correct mscordacwks so fail gracefully
Expand Down
4 changes: 4 additions & 0 deletions src/coreclr/debug/di/rspriv.h
Original file line number Diff line number Diff line change
Expand Up @@ -3280,6 +3280,10 @@ class CordbProcess :
#endif
}

#ifdef OUT_OF_PROCESS_SETTHREADCONTEXT
void HandleSetThreadContextNeeded(DWORD dwThreadId);
#endif

//
// Shim callbacks to simulate fake attach events.
//
Expand Down
8 changes: 7 additions & 1 deletion src/coreclr/debug/ee/amd64/dbghelpers.asm
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,13 @@ LEAF_ENTRY NotifyRightSideOfSyncCompleteFlare, _TEXT
ret
LEAF_END NotifyRightSideOfSyncCompleteFlare, _TEXT


; Flare for setting the context out of process
LEAF_ENTRY SetThreadContextNeededFlare, _TEXT
int 3
; make sure that the basic block is unique
test rax,7
ret
LEAF_END SetThreadContextNeededFlare, _TEXT

; This goes at the end of the assembly file
end
Loading