Skip to content

Commit c534080

Browse files
tommcdonmikelle-rogersnoahfalk
authored
Avoid using OpenThread for out of process SetThreadContext debugging (dotnet#107511)
* Avoid using OpenThread in out of process thread context scenarios * Add comments * Update src/coreclr/debug/di/process.cpp Co-authored-by: mikelle-rogers <[email protected]> * Update src/coreclr/debug/di/process.cpp Co-authored-by: mikelle-rogers <[email protected]> * Update src/coreclr/debug/di/process.cpp Co-authored-by: Noah Falk <[email protected]> --------- Co-authored-by: mikelle-rogers <[email protected]> Co-authored-by: Noah Falk <[email protected]>
1 parent d2c7db0 commit c534080

File tree

1 file changed

+54
-10
lines changed

1 file changed

+54
-10
lines changed

src/coreclr/debug/di/process.cpp

+54-10
Original file line numberDiff line numberDiff line change
@@ -11157,17 +11157,49 @@ void CordbProcess::HandleSetThreadContextNeeded(DWORD dwThreadId)
1115711157
LOG((LF_CORDB, LL_INFO10000, "RS HandleSetThreadContextNeeded\n"));
1115811158

1115911159
#if defined(TARGET_WINDOWS) && defined(TARGET_AMD64)
11160-
HandleHolder hThread = OpenThread(
11161-
THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_QUERY_INFORMATION | THREAD_SUSPEND_RESUME,
11162-
FALSE, // thread handle is not inheritable.
11163-
dwThreadId);
11160+
// Before we can read the left side context information, we must:
11161+
// 1. obtain the thread handle
11162+
// 2. suspened the thread
11163+
// 3. read the thread context, and from that read the pointer to the left-side context and the size of the context
11164+
// 4. then we can perform the actual SetThreadContext operation
11165+
// 5. lastly, we must resume the thread
11166+
// For the first step of obtaining the thread handle,
11167+
// we have previously attempted to use ::OpenThread to get a handle to the thread.
11168+
// However, there are situations where OpenThread can fail with an Access Denied error.
11169+
// From https://github.com/dotnet/runtime/issues/107263, the control-c handler in
11170+
// Windows causes the process to have higher privileges.
11171+
// We are now using the following approach to access the thread handle, which is the same
11172+
// approach used by CordbThread::RefreshHandle:
11173+
// 1. Get the thread handle from the DAC
11174+
// 2. Duplicate the handle to the current process
11175+
11176+
// lookup the CordbThread by thread ID, so that we can access the left-side thread handle
11177+
CordbThread * pThread = TryLookupOrCreateThreadByVolatileOSId(dwThreadId);
11178+
11179+
IDacDbiInterface* pDAC = GetDAC();
11180+
if (pDAC == NULL)
11181+
{
11182+
LOG((LF_CORDB, LL_INFO10000, "RS HandleSetThreadContextNeeded - DAC not initialized\n"));
11183+
ThrowHR(E_UNEXPECTED);
11184+
}
1116411185

11165-
if (hThread == NULL)
11186+
HANDLE hOutOfProcThread = pDAC->GetThreadHandle(pThread->m_vmThreadToken);
11187+
if (hOutOfProcThread == NULL)
1116611188
{
11167-
LOG((LF_CORDB, LL_INFO10000, "RS HandleSetThreadContextNeeded - Unexpected result from OpenThread\n"));
11189+
LOG((LF_CORDB, LL_INFO10000, "RS HandleSetThreadContextNeeded - Failed to get thread handle\n"));
1116811190
ThrowHR(E_UNEXPECTED);
1116911191
}
1117011192

11193+
// Duplicate the thread handle to the current process
11194+
HandleHolder hThread;
11195+
BOOL fSuccess = DuplicateHandle(UnsafeGetProcessHandle(), hOutOfProcThread, ::GetCurrentProcess(), &hThread, 0, FALSE, DUPLICATE_SAME_ACCESS);
11196+
if (!fSuccess)
11197+
{
11198+
LOG((LF_CORDB, LL_INFO10000, "RS HandleSetThreadContextNeeded - Unexpected result from DuplicateHandle\n"));
11199+
ThrowHR(HRESULT_FROM_GetLastError());
11200+
}
11201+
11202+
// Suspend the thread and so that we can read the thread context.
1117111203
DWORD previousSuspendCount = ::SuspendThread(hThread);
1117211204
if (previousSuspendCount == (DWORD)-1)
1117311205
{
@@ -11178,12 +11210,22 @@ void CordbProcess::HandleSetThreadContextNeeded(DWORD dwThreadId)
1117811210
DT_CONTEXT context = { 0 };
1117911211
context.ContextFlags = CONTEXT_FULL;
1118011212

11181-
HRESULT hr = GetDataTarget()->GetThreadContext(dwThreadId, CONTEXT_FULL, sizeof(DT_CONTEXT), reinterpret_cast<BYTE*> (&context));
11182-
IfFailThrow(hr);
11213+
// we originally used GetDataTarget()->GetThreadContext, but
11214+
// the implementation uses ShimLocalDataTarget::GetThreadContext which
11215+
// depends on OpenThread which might fail with an Access Denied error (see note above)
11216+
BOOL success = ::GetThreadContext(hThread, (CONTEXT*)(&context));
11217+
if (!success)
11218+
{
11219+
LOG((LF_CORDB, LL_INFO10000, "RS HandleSetThreadContextNeeded - Unexpected result from GetThreadContext\n"));
11220+
ThrowHR(HRESULT_FROM_GetLastError());
11221+
}
1118311222

11223+
// Read the pointer to the left-side context and the size of the context from the thread context.
1118411224
TADDR lsContextAddr = (TADDR)context.Rcx;
1118511225
DWORD contextSize = (DWORD)context.Rdx;
1118611226

11227+
// Read the expected Rip and Rsp from the thread context. This is used to
11228+
// validate the context read from the left-side.
1118711229
TADDR expectedRip = (TADDR)context.R8;
1118811230
TADDR expectedRsp = (TADDR)context.R9;
1118911231

@@ -11198,7 +11240,7 @@ void CordbProcess::HandleSetThreadContextNeeded(DWORD dwThreadId)
1119811240

1119911241
PCONTEXT pContext = (PCONTEXT)_alloca(contextSize);
1120011242
ULONG32 cbRead;
11201-
hr = GetDataTarget()->ReadVirtual(lsContextAddr, reinterpret_cast<BYTE*>(pContext), contextSize, &cbRead);
11243+
HRESULT hr = GetDataTarget()->ReadVirtual(lsContextAddr, reinterpret_cast<BYTE*>(pContext), contextSize, &cbRead);
1120211244
if (FAILED(hr))
1120311245
{
1120411246
_ASSERTE(!"ReadVirtual failed");
@@ -11232,7 +11274,7 @@ void CordbProcess::HandleSetThreadContextNeeded(DWORD dwThreadId)
1123211274
// The initialize call should fail but return contextSize
1123311275
contextSize = 0;
1123411276
DWORD contextFlags = pContext->ContextFlags;
11235-
BOOL success = InitializeContext(NULL, contextFlags, NULL, &contextSize);
11277+
success = InitializeContext(NULL, contextFlags, NULL, &contextSize);
1123611278

1123711279
if(success || GetLastError() != ERROR_INSUFFICIENT_BUFFER)
1123811280
{
@@ -11276,6 +11318,7 @@ void CordbProcess::HandleSetThreadContextNeeded(DWORD dwThreadId)
1127611318

1127711319
DWORD lastError = 0;
1127811320

11321+
// Perform the actual SetThreadContext operation.
1127911322
success = ::SetThreadContext(hThread, pFrameContext);
1128011323
if (!success)
1128111324
{
@@ -11285,6 +11328,7 @@ void CordbProcess::HandleSetThreadContextNeeded(DWORD dwThreadId)
1128511328
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)));
1128611329
_ASSERTE(success);
1128711330

11331+
// Now that we have completed the SetThreadContext, resume the thread
1128811332
DWORD suspendCount = ::ResumeThread(hThread);
1128911333
if (suspendCount == (DWORD)-1)
1129011334
{

0 commit comments

Comments
 (0)