From 9524e5f7cfe2f002e339b3878052b717c520babb Mon Sep 17 00:00:00 2001 From: Thays Grazia Date: Tue, 22 Apr 2025 18:29:01 -0300 Subject: [PATCH 1/5] backport 111408 --- src/coreclr/debug/ee/controller.cpp | 14 ++++++++- src/coreclr/debug/ee/debugger.cpp | 17 +++++++++++ src/coreclr/debug/ee/debugger.h | 3 ++ src/coreclr/vm/dbginterface.h | 3 ++ src/coreclr/vm/threads.h | 8 ++++-- src/coreclr/vm/threadsuspend.cpp | 44 ++++++++++++++++++++++++++++- 6 files changed, 85 insertions(+), 4 deletions(-) diff --git a/src/coreclr/debug/ee/controller.cpp b/src/coreclr/debug/ee/controller.cpp index 1738eb5862fee7..8962a382207b5c 100644 --- a/src/coreclr/debug/ee/controller.cpp +++ b/src/coreclr/debug/ee/controller.cpp @@ -4312,7 +4312,19 @@ bool DebuggerController::DispatchNativeException(EXCEPTION_RECORD *pException, } #endif - +#ifdef FEATURE_SPECIAL_USER_MODE_APC + if (pCurThread->m_State & Thread::TS_SSToExitApcCall) + { + if (!CheckActivationSafePoint(GetIP(pContext))) + { + return FALSE; + } + pCurThread->SetThreadState(Thread::TS_SSToExitApcCallDone); + pCurThread->ResetThreadState(Thread::TS_SSToExitApcCall); + DebuggerController::UnapplyTraceFlag(pCurThread); + pCurThread->MarkForSuspensionAndWait(Thread::TS_DebugSuspendPending); + } +#endif // Must restore the filter context. After the filter context is gone, we're // unprotected again and unsafe for a GC. diff --git a/src/coreclr/debug/ee/debugger.cpp b/src/coreclr/debug/ee/debugger.cpp index 2a6eefb467b0ad..110812b7513c56 100644 --- a/src/coreclr/debug/ee/debugger.cpp +++ b/src/coreclr/debug/ee/debugger.cpp @@ -15075,6 +15075,14 @@ HRESULT Debugger::FuncEvalSetup(DebuggerIPCE_FuncEvalInfo *pEvalInfo, return CORDBG_E_ILLEGAL_IN_STACK_OVERFLOW; } +#ifdef FEATURE_SPECIAL_USER_MODE_APC + if (pThread->m_hasPendingActivation) + { + _ASSERTE(!"Should never get here with a pending activation. (Debugger::FuncEvalSetup)"); + return CORDBG_E_ILLEGAL_IN_NATIVE_CODE; + } +#endif + bool fInException = pEvalInfo->evalDuringException; // The thread has to be at a GC safe place for now, just in case the func eval causes a collection. Processing an @@ -16733,6 +16741,15 @@ Debugger::EnumMemoryRegionsIfFuncEvalFrame(CLRDataEnumMemoryFlags flags, Frame * } } +#ifdef FEATURE_SPECIAL_USER_MODE_APC +void Debugger::SingleStepToExitApcCall(Thread* pThread, CONTEXT *interruptedContext) +{ + pThread->SetThreadState(Thread::TS_SSToExitApcCall); + g_pEEInterface->SetThreadFilterContext(pThread, interruptedContext); + DebuggerController::EnableSingleStep(pThread); + g_pEEInterface->SetThreadFilterContext(pThread, NULL); +} +#endif //FEATURE_SPECIAL_USER_MODE_APC #endif // #ifdef DACCESS_COMPILE #ifndef DACCESS_COMPILE diff --git a/src/coreclr/debug/ee/debugger.h b/src/coreclr/debug/ee/debugger.h index d7200e1eb6ad0e..63bc113e3112ae 100644 --- a/src/coreclr/debug/ee/debugger.h +++ b/src/coreclr/debug/ee/debugger.h @@ -3098,6 +3098,9 @@ class Debugger : public DebugInterface // Used by Debugger::FirstChanceNativeException to update the context from out of process void SendSetThreadContextNeeded(CONTEXT *context, DebuggerSteppingInfo *pDebuggerSteppingInfo = NULL); BOOL IsOutOfProcessSetContextEnabled(); +#ifdef FEATURE_SPECIAL_USER_MODE_APC + void SingleStepToExitApcCall(Thread* pThread, CONTEXT *interruptedContext); +#endif // FEATURE_SPECIAL_USER_MODE_APC }; diff --git a/src/coreclr/vm/dbginterface.h b/src/coreclr/vm/dbginterface.h index e11fdf58dcd534..2645a8b44e6fc7 100644 --- a/src/coreclr/vm/dbginterface.h +++ b/src/coreclr/vm/dbginterface.h @@ -414,6 +414,9 @@ class DebugInterface #ifndef DACCESS_COMPILE virtual HRESULT DeoptimizeMethod(Module* pModule, mdMethodDef methodDef) = 0; virtual HRESULT IsMethodDeoptimized(Module *pModule, mdMethodDef methodDef, BOOL *pResult) = 0; +#ifdef FEATURE_SPECIAL_USER_MODE_APC + virtual void SingleStepToExitApcCall(Thread* pThread, CONTEXT *interruptedContext) = 0; +#endif // FEATURE_SPECIAL_USER_MODE_APC #endif //DACCESS_COMPILE }; diff --git a/src/coreclr/vm/threads.h b/src/coreclr/vm/threads.h index 562b97b68bdf63..c07e94e3d0493b 100644 --- a/src/coreclr/vm/threads.h +++ b/src/coreclr/vm/threads.h @@ -489,6 +489,7 @@ class Thread friend void STDCALL OnHijackWorker(HijackArgs * pArgs); #ifdef FEATURE_THREAD_ACTIVATION friend void HandleSuspensionForInterruptedThread(CONTEXT *interruptedContext); + friend void HandleSuspensionForInterruptedThread(CONTEXT *interruptedContext, bool suspendForDebugger); friend BOOL CheckActivationSafePoint(SIZE_T ip); #endif // FEATURE_THREAD_ACTIVATION @@ -557,7 +558,7 @@ class Thread TS_Hijacked = 0x00000080, // Return address has been hijacked #endif // FEATURE_HIJACK - // unused = 0x00000100, + TS_SSToExitApcCall = 0x00000100, // Enable SS and resume the thread to exit an APC Call and keep the thread in suspend state TS_Background = 0x00000200, // Thread is a background thread TS_Unstarted = 0x00000400, // Thread has never been started TS_Dead = 0x00000800, // Thread is dead @@ -574,7 +575,7 @@ class Thread TS_ReportDead = 0x00010000, // in WaitForOtherThreads() TS_FullyInitialized = 0x00020000, // Thread is fully initialized and we are ready to broadcast its existence to external clients - // unused = 0x00040000, + TS_SSToExitApcCallDone = 0x00040000, // The thread exited an APC Call and it is already resumed and paused on SS TS_SyncSuspended = 0x00080000, // Suspended via WaitSuspendEvent TS_DebugWillSync = 0x00100000, // Debugger will wait for this thread to sync @@ -2568,6 +2569,9 @@ class Thread // Waiting & Synchronization //------------------------------------------------------------- +friend class DebuggerController; +protected: + void MarkForSuspensionAndWait(ULONG bit); // For suspends. The thread waits on this event. A client sets the event to cause // the thread to resume. void WaitSuspendEvents(); diff --git a/src/coreclr/vm/threadsuspend.cpp b/src/coreclr/vm/threadsuspend.cpp index 2ddc2c3b120c99..63d2cf63d2973e 100644 --- a/src/coreclr/vm/threadsuspend.cpp +++ b/src/coreclr/vm/threadsuspend.cpp @@ -4238,6 +4238,18 @@ bool Thread::SysSweepThreadsForDebug(bool forceSync) if ((thread->m_State & TS_DebugWillSync) == 0) continue; +#ifdef FEATURE_SPECIAL_USER_MODE_APC + if (thread->m_State & Thread::TS_SSToExitApcCallDone) + { + thread->ResetThreadState(Thread::TS_SSToExitApcCallDone); + goto Label_MarkThreadAsSynced; + } + if (thread->m_State & Thread::TS_SSToExitApcCall) + { + continue; + } + #endif + if (!UseContextBasedThreadRedirection()) { // On platforms that do not support safe thread suspension we either @@ -5353,6 +5365,19 @@ BOOL Thread::HandledJITCase() #endif // FEATURE_HIJACK // Some simple helpers to keep track of the threads we are waiting for +void Thread::MarkForSuspensionAndWait(ULONG bit) +{ + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + m_DebugSuspendEvent.Reset(); + InterlockedOr((LONG*)&m_State, bit); + ThreadStore::IncrementTrapReturningThreads(); + m_DebugSuspendEvent.Wait(INFINITE,FALSE); +} + void Thread::MarkForSuspension(ULONG bit) { CONTRACTL { @@ -5811,6 +5836,18 @@ void HandleSuspensionForInterruptedThread(CONTEXT *interruptedContext) if (!codeInfo.IsValid()) return; +#ifdef FEATURE_SPECIAL_USER_MODE_APC + // It's not allowed to change the IP while paused in an APC Callback for security reasons if CET is turned on + // So we enable the single step in the thread that is running the APC Callback + // and then it will be paused using single step exception after exiting the APC callback + // this will allow the debugger to setIp to execute FuncEvalHijack. + if (suspendForDebugger) + { + g_pDebugInterface->SingleStepToExitApcCall(pThread, interruptedContext); + return; + } + #endif + DWORD addrOffset = codeInfo.GetRelOffset(); ICodeManager *pEECM = codeInfo.GetCodeManager(); @@ -5886,6 +5923,11 @@ void HandleSuspensionForInterruptedThread(CONTEXT *interruptedContext) } } +void HandleSuspensionForInterruptedThread(CONTEXT *interruptedContext) +{ + HandleSuspensionForInterruptedThread(interruptedContext, false); +} + #ifdef FEATURE_SPECIAL_USER_MODE_APC void Thread::ApcActivationCallback(ULONG_PTR Parameter) { @@ -5915,7 +5957,7 @@ void Thread::ApcActivationCallback(ULONG_PTR Parameter) case ActivationReason::SuspendForGC: case ActivationReason::SuspendForDebugger: case ActivationReason::ThreadAbort: - HandleSuspensionForInterruptedThread(pContext); + HandleSuspensionForInterruptedThread(pContext, reason == ActivationReason::SuspendForDebugger); break; default: From 991cd7e2914d704f289777b3566579c3bfd4dc50 Mon Sep 17 00:00:00 2001 From: Thays Grazia Date: Wed, 23 Apr 2025 10:22:22 -0300 Subject: [PATCH 2/5] Fix compilation failure --- src/coreclr/vm/threadsuspend.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/threadsuspend.cpp b/src/coreclr/vm/threadsuspend.cpp index 63d2cf63d2973e..a4b120c95982bb 100644 --- a/src/coreclr/vm/threadsuspend.cpp +++ b/src/coreclr/vm/threadsuspend.cpp @@ -5800,7 +5800,7 @@ BOOL CheckActivationSafePoint(SIZE_T ip) // address to take the thread to the appropriate stub (based on the return // type of the method) which will then handle preparing the thread for GC. // -void HandleSuspensionForInterruptedThread(CONTEXT *interruptedContext) +void HandleSuspensionForInterruptedThread(CONTEXT *interruptedContext, bool suspendForDebugger) { struct AutoClearPendingThreadActivation { From 785ce3bf125d61848f81444fd632b59f40041d73 Mon Sep 17 00:00:00 2001 From: Thays Grazia Date: Wed, 23 Apr 2025 14:37:19 -0300 Subject: [PATCH 3/5] Fix compilation --- src/coreclr/debug/ee/debugger.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/coreclr/debug/ee/debugger.cpp b/src/coreclr/debug/ee/debugger.cpp index 110812b7513c56..16b7e432108d2b 100644 --- a/src/coreclr/debug/ee/debugger.cpp +++ b/src/coreclr/debug/ee/debugger.cpp @@ -16740,16 +16740,6 @@ Debugger::EnumMemoryRegionsIfFuncEvalFrame(CLRDataEnumMemoryFlags flags, Frame * } } } - -#ifdef FEATURE_SPECIAL_USER_MODE_APC -void Debugger::SingleStepToExitApcCall(Thread* pThread, CONTEXT *interruptedContext) -{ - pThread->SetThreadState(Thread::TS_SSToExitApcCall); - g_pEEInterface->SetThreadFilterContext(pThread, interruptedContext); - DebuggerController::EnableSingleStep(pThread); - g_pEEInterface->SetThreadFilterContext(pThread, NULL); -} -#endif //FEATURE_SPECIAL_USER_MODE_APC #endif // #ifdef DACCESS_COMPILE #ifndef DACCESS_COMPILE @@ -16838,6 +16828,16 @@ void Debugger::SendSetThreadContextNeeded(CONTEXT *context, DebuggerSteppingInfo _ASSERTE(!"We failed to SetThreadContext from out of process!"); } +#ifdef FEATURE_SPECIAL_USER_MODE_APC +void Debugger::SingleStepToExitApcCall(Thread* pThread, CONTEXT *interruptedContext) +{ + pThread->SetThreadState(Thread::TS_SSToExitApcCall); + g_pEEInterface->SetThreadFilterContext(pThread, interruptedContext); + DebuggerController::EnableSingleStep(pThread); + g_pEEInterface->SetThreadFilterContext(pThread, NULL); +} +#endif //FEATURE_SPECIAL_USER_MODE_APC + BOOL Debugger::IsOutOfProcessSetContextEnabled() { return m_fOutOfProcessSetContextEnabled; From d403007b4a2384bb8668579e075868166386a206 Mon Sep 17 00:00:00 2001 From: Thays Grazia Date: Wed, 23 Apr 2025 17:01:21 -0300 Subject: [PATCH 4/5] trying to fix compilation --- src/coreclr/debug/ee/debugger.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/coreclr/debug/ee/debugger.h b/src/coreclr/debug/ee/debugger.h index 63bc113e3112ae..4db35cd20d42c9 100644 --- a/src/coreclr/debug/ee/debugger.h +++ b/src/coreclr/debug/ee/debugger.h @@ -3098,9 +3098,11 @@ class Debugger : public DebugInterface // Used by Debugger::FirstChanceNativeException to update the context from out of process void SendSetThreadContextNeeded(CONTEXT *context, DebuggerSteppingInfo *pDebuggerSteppingInfo = NULL); BOOL IsOutOfProcessSetContextEnabled(); +#ifndef DACCESS_COMPILE #ifdef FEATURE_SPECIAL_USER_MODE_APC void SingleStepToExitApcCall(Thread* pThread, CONTEXT *interruptedContext); -#endif // FEATURE_SPECIAL_USER_MODE_APC +#endif // FEATURE_SPECIAL_USER_MODE_APC +#endif }; From d62f4aec555087e0da66f0e6cbeaad4c6a52cad7 Mon Sep 17 00:00:00 2001 From: Thays Grazia Date: Thu, 24 Apr 2025 10:51:27 -0300 Subject: [PATCH 5/5] Trying to fix compilation --- src/coreclr/debug/ee/debugger.cpp | 23 +++++++++++------------ src/coreclr/debug/ee/debugger.h | 2 +- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/coreclr/debug/ee/debugger.cpp b/src/coreclr/debug/ee/debugger.cpp index 16b7e432108d2b..69927f25784dc1 100644 --- a/src/coreclr/debug/ee/debugger.cpp +++ b/src/coreclr/debug/ee/debugger.cpp @@ -16827,17 +16827,6 @@ void Debugger::SendSetThreadContextNeeded(CONTEXT *context, DebuggerSteppingInfo LOG((LF_CORDB, LL_INFO10000, "D::SSTCN SetThreadContextNeededFlare returned\n")); _ASSERTE(!"We failed to SetThreadContext from out of process!"); } - -#ifdef FEATURE_SPECIAL_USER_MODE_APC -void Debugger::SingleStepToExitApcCall(Thread* pThread, CONTEXT *interruptedContext) -{ - pThread->SetThreadState(Thread::TS_SSToExitApcCall); - g_pEEInterface->SetThreadFilterContext(pThread, interruptedContext); - DebuggerController::EnableSingleStep(pThread); - g_pEEInterface->SetThreadFilterContext(pThread, NULL); -} -#endif //FEATURE_SPECIAL_USER_MODE_APC - BOOL Debugger::IsOutOfProcessSetContextEnabled() { return m_fOutOfProcessSetContextEnabled; @@ -16854,6 +16843,16 @@ BOOL Debugger::IsOutOfProcessSetContextEnabled() } #endif // OUT_OF_PROCESS_SETTHREADCONTEXT #endif // DACCESS_COMPILE - +#ifndef DACCESS_COMPILE +#ifdef FEATURE_SPECIAL_USER_MODE_APC +void Debugger::SingleStepToExitApcCall(Thread* pThread, CONTEXT *interruptedContext) +{ + pThread->SetThreadState(Thread::TS_SSToExitApcCall); + g_pEEInterface->SetThreadFilterContext(pThread, interruptedContext); + DebuggerController::EnableSingleStep(pThread); + g_pEEInterface->SetThreadFilterContext(pThread, NULL); +} +#endif //FEATURE_SPECIAL_USER_MODE_APC +#endif // DACCESS_COMPILE #endif //DEBUGGING_SUPPORTED diff --git a/src/coreclr/debug/ee/debugger.h b/src/coreclr/debug/ee/debugger.h index 4db35cd20d42c9..048d8fd388562e 100644 --- a/src/coreclr/debug/ee/debugger.h +++ b/src/coreclr/debug/ee/debugger.h @@ -3098,7 +3098,7 @@ class Debugger : public DebugInterface // Used by Debugger::FirstChanceNativeException to update the context from out of process void SendSetThreadContextNeeded(CONTEXT *context, DebuggerSteppingInfo *pDebuggerSteppingInfo = NULL); BOOL IsOutOfProcessSetContextEnabled(); -#ifndef DACCESS_COMPILE +#ifndef DACCESS_COMPILE #ifdef FEATURE_SPECIAL_USER_MODE_APC void SingleStepToExitApcCall(Thread* pThread, CONTEXT *interruptedContext); #endif // FEATURE_SPECIAL_USER_MODE_APC