@@ -11157,17 +11157,49 @@ void CordbProcess::HandleSetThreadContextNeeded(DWORD dwThreadId)
11157
11157
LOG((LF_CORDB, LL_INFO10000, "RS HandleSetThreadContextNeeded\n"));
11158
11158
11159
11159
#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
+ }
11164
11185
11165
- if (hThread == NULL)
11186
+ HANDLE hOutOfProcThread = pDAC->GetThreadHandle(pThread->m_vmThreadToken);
11187
+ if (hOutOfProcThread == NULL)
11166
11188
{
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"));
11168
11190
ThrowHR(E_UNEXPECTED);
11169
11191
}
11170
11192
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.
11171
11203
DWORD previousSuspendCount = ::SuspendThread(hThread);
11172
11204
if (previousSuspendCount == (DWORD)-1)
11173
11205
{
@@ -11178,12 +11210,22 @@ void CordbProcess::HandleSetThreadContextNeeded(DWORD dwThreadId)
11178
11210
DT_CONTEXT context = { 0 };
11179
11211
context.ContextFlags = CONTEXT_FULL;
11180
11212
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
+ }
11183
11222
11223
+ // Read the pointer to the left-side context and the size of the context from the thread context.
11184
11224
TADDR lsContextAddr = (TADDR)context.Rcx;
11185
11225
DWORD contextSize = (DWORD)context.Rdx;
11186
11226
11227
+ // Read the expected Rip and Rsp from the thread context. This is used to
11228
+ // validate the context read from the left-side.
11187
11229
TADDR expectedRip = (TADDR)context.R8;
11188
11230
TADDR expectedRsp = (TADDR)context.R9;
11189
11231
@@ -11198,7 +11240,7 @@ void CordbProcess::HandleSetThreadContextNeeded(DWORD dwThreadId)
11198
11240
11199
11241
PCONTEXT pContext = (PCONTEXT)_alloca(contextSize);
11200
11242
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);
11202
11244
if (FAILED(hr))
11203
11245
{
11204
11246
_ASSERTE(!"ReadVirtual failed");
@@ -11232,7 +11274,7 @@ void CordbProcess::HandleSetThreadContextNeeded(DWORD dwThreadId)
11232
11274
// The initialize call should fail but return contextSize
11233
11275
contextSize = 0;
11234
11276
DWORD contextFlags = pContext->ContextFlags;
11235
- BOOL success = InitializeContext(NULL, contextFlags, NULL, &contextSize);
11277
+ success = InitializeContext(NULL, contextFlags, NULL, &contextSize);
11236
11278
11237
11279
if(success || GetLastError() != ERROR_INSUFFICIENT_BUFFER)
11238
11280
{
@@ -11276,6 +11318,7 @@ void CordbProcess::HandleSetThreadContextNeeded(DWORD dwThreadId)
11276
11318
11277
11319
DWORD lastError = 0;
11278
11320
11321
+ // Perform the actual SetThreadContext operation.
11279
11322
success = ::SetThreadContext(hThread, pFrameContext);
11280
11323
if (!success)
11281
11324
{
@@ -11285,6 +11328,7 @@ void CordbProcess::HandleSetThreadContextNeeded(DWORD dwThreadId)
11285
11328
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)));
11286
11329
_ASSERTE(success);
11287
11330
11331
+ // Now that we have completed the SetThreadContext, resume the thread
11288
11332
DWORD suspendCount = ::ResumeThread(hThread);
11289
11333
if (suspendCount == (DWORD)-1)
11290
11334
{
0 commit comments