@@ -4238,6 +4238,18 @@ bool Thread::SysSweepThreadsForDebug(bool forceSync)
42384238 if ((thread->m_State & TS_DebugWillSync) == 0 )
42394239 continue ;
42404240
4241+ #ifdef FEATURE_SPECIAL_USER_MODE_APC
4242+ if (thread->m_State & Thread::TS_SSToExitApcCallDone)
4243+ {
4244+ thread->ResetThreadState (Thread::TS_SSToExitApcCallDone);
4245+ goto Label_MarkThreadAsSynced;
4246+ }
4247+ if (thread->m_State & Thread::TS_SSToExitApcCall)
4248+ {
4249+ continue ;
4250+ }
4251+ #endif
4252+
42414253 if (!UseContextBasedThreadRedirection ())
42424254 {
42434255 // On platforms that do not support safe thread suspension we either
@@ -5353,6 +5365,19 @@ BOOL Thread::HandledJITCase()
53535365#endif // FEATURE_HIJACK
53545366
53555367// Some simple helpers to keep track of the threads we are waiting for
5368+ void Thread::MarkForSuspensionAndWait (ULONG bit)
5369+ {
5370+ CONTRACTL {
5371+ NOTHROW;
5372+ GC_NOTRIGGER;
5373+ }
5374+ CONTRACTL_END;
5375+ m_DebugSuspendEvent.Reset ();
5376+ InterlockedOr ((LONG*)&m_State, bit);
5377+ ThreadStore::IncrementTrapReturningThreads ();
5378+ m_DebugSuspendEvent.Wait (INFINITE,FALSE );
5379+ }
5380+
53565381void Thread::MarkForSuspension (ULONG bit)
53575382{
53585383 CONTRACTL {
@@ -5775,7 +5800,7 @@ BOOL CheckActivationSafePoint(SIZE_T ip)
57755800// address to take the thread to the appropriate stub (based on the return
57765801// type of the method) which will then handle preparing the thread for GC.
57775802//
5778- void HandleSuspensionForInterruptedThread (CONTEXT *interruptedContext)
5803+ void HandleSuspensionForInterruptedThread (CONTEXT *interruptedContext, bool suspendForDebugger )
57795804{
57805805 struct AutoClearPendingThreadActivation
57815806 {
@@ -5811,6 +5836,18 @@ void HandleSuspensionForInterruptedThread(CONTEXT *interruptedContext)
58115836 if (!codeInfo.IsValid ())
58125837 return ;
58135838
5839+ #ifdef FEATURE_SPECIAL_USER_MODE_APC
5840+ // It's not allowed to change the IP while paused in an APC Callback for security reasons if CET is turned on
5841+ // So we enable the single step in the thread that is running the APC Callback
5842+ // and then it will be paused using single step exception after exiting the APC callback
5843+ // this will allow the debugger to setIp to execute FuncEvalHijack.
5844+ if (suspendForDebugger)
5845+ {
5846+ g_pDebugInterface->SingleStepToExitApcCall (pThread, interruptedContext);
5847+ return ;
5848+ }
5849+ #endif
5850+
58145851 DWORD addrOffset = codeInfo.GetRelOffset ();
58155852
58165853 ICodeManager *pEECM = codeInfo.GetCodeManager ();
@@ -5886,6 +5923,11 @@ void HandleSuspensionForInterruptedThread(CONTEXT *interruptedContext)
58865923 }
58875924}
58885925
5926+ void HandleSuspensionForInterruptedThread (CONTEXT *interruptedContext)
5927+ {
5928+ HandleSuspensionForInterruptedThread (interruptedContext, false );
5929+ }
5930+
58895931#ifdef FEATURE_SPECIAL_USER_MODE_APC
58905932void Thread::ApcActivationCallback (ULONG_PTR Parameter)
58915933{
@@ -5915,7 +5957,7 @@ void Thread::ApcActivationCallback(ULONG_PTR Parameter)
59155957 case ActivationReason::SuspendForGC:
59165958 case ActivationReason::SuspendForDebugger:
59175959 case ActivationReason::ThreadAbort:
5918- HandleSuspensionForInterruptedThread (pContext);
5960+ HandleSuspensionForInterruptedThread (pContext, reason == ActivationReason::SuspendForDebugger );
59195961 break ;
59205962
59215963 default :
0 commit comments