@@ -4238,6 +4238,18 @@ bool Thread::SysSweepThreadsForDebug(bool forceSync)
4238
4238
if ((thread->m_State & TS_DebugWillSync) == 0 )
4239
4239
continue ;
4240
4240
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
+
4241
4253
if (!UseContextBasedThreadRedirection ())
4242
4254
{
4243
4255
// On platforms that do not support safe thread suspension we either
@@ -5353,6 +5365,19 @@ BOOL Thread::HandledJITCase()
5353
5365
#endif // FEATURE_HIJACK
5354
5366
5355
5367
// 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
+
5356
5381
void Thread::MarkForSuspension (ULONG bit)
5357
5382
{
5358
5383
CONTRACTL {
@@ -5775,7 +5800,7 @@ BOOL CheckActivationSafePoint(SIZE_T ip)
5775
5800
// address to take the thread to the appropriate stub (based on the return
5776
5801
// type of the method) which will then handle preparing the thread for GC.
5777
5802
//
5778
- void HandleSuspensionForInterruptedThread (CONTEXT *interruptedContext)
5803
+ void HandleSuspensionForInterruptedThread (CONTEXT *interruptedContext, bool suspendForDebugger )
5779
5804
{
5780
5805
struct AutoClearPendingThreadActivation
5781
5806
{
@@ -5811,6 +5836,18 @@ void HandleSuspensionForInterruptedThread(CONTEXT *interruptedContext)
5811
5836
if (!codeInfo.IsValid ())
5812
5837
return ;
5813
5838
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
+
5814
5851
DWORD addrOffset = codeInfo.GetRelOffset ();
5815
5852
5816
5853
ICodeManager *pEECM = codeInfo.GetCodeManager ();
@@ -5886,6 +5923,11 @@ void HandleSuspensionForInterruptedThread(CONTEXT *interruptedContext)
5886
5923
}
5887
5924
}
5888
5925
5926
+ void HandleSuspensionForInterruptedThread (CONTEXT *interruptedContext)
5927
+ {
5928
+ HandleSuspensionForInterruptedThread (interruptedContext, false );
5929
+ }
5930
+
5889
5931
#ifdef FEATURE_SPECIAL_USER_MODE_APC
5890
5932
void Thread::ApcActivationCallback (ULONG_PTR Parameter)
5891
5933
{
@@ -5915,7 +5957,7 @@ void Thread::ApcActivationCallback(ULONG_PTR Parameter)
5915
5957
case ActivationReason::SuspendForGC:
5916
5958
case ActivationReason::SuspendForDebugger:
5917
5959
case ActivationReason::ThreadAbort:
5918
- HandleSuspensionForInterruptedThread (pContext);
5960
+ HandleSuspensionForInterruptedThread (pContext, reason == ActivationReason::SuspendForDebugger );
5919
5961
break ;
5920
5962
5921
5963
default :
0 commit comments