1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4//
5
6//
7
8
9#include "common.h"
10#include "exstate.h"
11#include "exinfo.h"
12
13#ifdef _DEBUG
14#include "comutilnative.h" // for assertions only
15#endif
16
17OBJECTHANDLE ThreadExceptionState::GetThrowableAsHandle()
18{
19 WRAPPER_NO_CONTRACT;
20
21#ifdef WIN64EXCEPTIONS
22 if (m_pCurrentTracker)
23 {
24 return m_pCurrentTracker->m_hThrowable;
25 }
26
27 return NULL;
28#else // WIN64EXCEPTIONS
29 return m_currentExInfo.m_hThrowable;
30#endif // WIN64EXCEPTIONS
31}
32
33
34ThreadExceptionState::ThreadExceptionState()
35{
36#ifdef WIN64EXCEPTIONS
37 m_pCurrentTracker = NULL;
38#endif // WIN64EXCEPTIONS
39
40 m_flag = TEF_None;
41
42#ifndef FEATURE_PAL
43 // Init the UE Watson BucketTracker
44 m_UEWatsonBucketTracker.Init();
45#endif // !FEATURE_PAL
46
47#ifdef FEATURE_CORRUPTING_EXCEPTIONS
48 // Initialize the default exception severity to NotCorrupting
49 m_LastActiveExceptionCorruptionSeverity = NotSet;
50 m_fCanReflectionTargetHandleException = FALSE;
51#endif // FEATURE_CORRUPTING_EXCEPTIONS
52
53}
54
55ThreadExceptionState::~ThreadExceptionState()
56{
57#ifndef FEATURE_PAL
58 // Init the UE Watson BucketTracker
59 m_UEWatsonBucketTracker.ClearWatsonBucketDetails();
60#endif // !FEATURE_PAL
61}
62
63#if defined(_DEBUG)
64void ThreadExceptionState::AssertStackTraceInfo(StackTraceInfo *pSTI)
65{
66 LIMITED_METHOD_CONTRACT;
67#if defined(WIN64EXCEPTIONS)
68
69 _ASSERTE(pSTI == &(m_pCurrentTracker->m_StackTraceInfo) || pSTI == &(m_OOMTracker.m_StackTraceInfo));
70
71#else // win64exceptions
72
73 _ASSERTE(pSTI == &(m_currentExInfo.m_StackTraceInfo));
74
75#endif // win64exceptions
76} // void ThreadExceptionState::AssertStackTraceInfo()
77#endif // _debug
78
79#ifndef DACCESS_COMPILE
80
81Thread* ThreadExceptionState::GetMyThread()
82{
83 return (Thread*)(((BYTE*)this) - offsetof(Thread, m_ExceptionState));
84}
85
86
87void ThreadExceptionState::FreeAllStackTraces()
88{
89 WRAPPER_NO_CONTRACT;
90
91#ifdef WIN64EXCEPTIONS
92 ExceptionTracker* pNode = m_pCurrentTracker;
93#else // WIN64EXCEPTIONS
94 ExInfo* pNode = &m_currentExInfo;
95#endif // WIN64EXCEPTIONS
96
97 for ( ;
98 pNode != NULL;
99 pNode = pNode->m_pPrevNestedInfo)
100 {
101 pNode->m_StackTraceInfo.FreeStackTrace();
102 }
103}
104
105void ThreadExceptionState::ClearThrowablesForUnload(IGCHandleStore* handleStore)
106{
107 WRAPPER_NO_CONTRACT;
108
109#ifdef WIN64EXCEPTIONS
110 ExceptionTracker* pNode = m_pCurrentTracker;
111#else // WIN64EXCEPTIONS
112 ExInfo* pNode = &m_currentExInfo;
113#endif // WIN64EXCEPTIONS
114
115 for ( ;
116 pNode != NULL;
117 pNode = pNode->m_pPrevNestedInfo)
118 {
119 if (handleStore->ContainsHandle(pNode->m_hThrowable))
120 {
121 pNode->DestroyExceptionHandle();
122 }
123 }
124}
125
126
127// After unwinding from an SO, there may be stale exception state.
128void ThreadExceptionState::ClearExceptionStateAfterSO(void* pStackFrameSP)
129{
130 WRAPPER_NO_CONTRACT;
131
132 #if defined(WIN64EXCEPTIONS)
133 ExceptionTracker::PopTrackers(pStackFrameSP);
134 #else
135 // After unwinding from an SO, there may be stale exception state. We need to
136 // get rid of any state that assumes the handlers that have been unwound/unlinked.
137 //
138 // Because the ExState chains to entries that may be on the stack, and the
139 // stack has been unwound, it may not be safe to reference any entries
140 // other than the one of the Thread object.
141 //
142 // Consequently, we will simply Init() the ExInfo on the Thread object.
143 m_currentExInfo.Init();
144 #endif
145} // void ThreadExceptionState::ClearExceptionStateAfterSO()
146
147OBJECTREF ThreadExceptionState::GetThrowable()
148{
149 CONTRACTL
150 {
151 MODE_COOPERATIVE;
152 NOTHROW;
153 GC_NOTRIGGER;
154 SO_TOLERANT;
155 }
156 CONTRACTL_END;
157
158#ifdef WIN64EXCEPTIONS
159 if (m_pCurrentTracker && m_pCurrentTracker->m_hThrowable)
160 {
161 return ObjectFromHandle(m_pCurrentTracker->m_hThrowable);
162 }
163#else // WIN64EXCEPTIONS
164 if (m_currentExInfo.m_hThrowable)
165 {
166 return ObjectFromHandle(m_currentExInfo.m_hThrowable);
167 }
168#endif // WIN64EXCEPTIONS
169
170 return NULL;
171}
172
173void ThreadExceptionState::SetThrowable(OBJECTREF throwable DEBUG_ARG(SetThrowableErrorChecking stecFlags))
174{
175 CONTRACTL
176 {
177 if ((throwable == NULL) || CLRException::IsPreallocatedExceptionObject(throwable)) NOTHROW; else THROWS; // From CreateHandle
178 GC_NOTRIGGER;
179 if (throwable == NULL) MODE_ANY; else MODE_COOPERATIVE;
180 SO_TOLERANT;
181 }
182 CONTRACTL_END;
183
184#ifdef WIN64EXCEPTIONS
185 if (m_pCurrentTracker)
186 {
187 m_pCurrentTracker->DestroyExceptionHandle();
188 }
189#else // WIN64EXCEPTIONS
190 m_currentExInfo.DestroyExceptionHandle();
191#endif // WIN64EXCEPTIONS
192
193 if (throwable != NULL)
194 {
195 // Non-compliant exceptions are always wrapped.
196 // The use of the ExceptionNative:: helper here (rather than the global ::IsException helper)
197 // is hokey, but we need a GC_NOTRIGGER version and it's only for an ASSERT.
198 _ASSERTE(IsException(throwable->GetMethodTable()));
199
200 OBJECTHANDLE hNewThrowable;
201
202 // If we're tracking one of the preallocated exception objects, then just use the global handle that
203 // matches it rather than creating a new one.
204 if (CLRException::IsPreallocatedExceptionObject(throwable))
205 {
206 hNewThrowable = CLRException::GetPreallocatedHandleForObject(throwable);
207 }
208 else
209 {
210 BEGIN_SO_INTOLERANT_CODE(GetThread());
211 {
212 AppDomain* pDomain = GetMyThread()->GetDomain();
213 PREFIX_ASSUME(pDomain != NULL);
214 hNewThrowable = pDomain->CreateHandle(throwable);
215 }
216 END_SO_INTOLERANT_CODE;
217 }
218
219#ifdef WIN64EXCEPTIONS
220#ifdef _DEBUG
221 //
222 // Fatal stack overflow policy ends up short-circuiting the normal exception handling
223 // flow such that there could be no Tracker for this SO that is in flight. In this
224 // situation there is no place to store the throwable in the exception state, and instead
225 // it is presumed that the handle to the SO exception is elsewhere. (Current knowledge
226 // as of 7/15/05 is that it is stored in Thread::m_LastThrownObjectHandle;
227 //
228 if (stecFlags != STEC_CurrentTrackerEqualNullOkHackForFatalStackOverflow
229#ifdef FEATURE_INTERPRETER
230 && stecFlags != STEC_CurrentTrackerEqualNullOkForInterpreter
231#endif // FEATURE_INTERPRETER
232 )
233 {
234 CONSISTENCY_CHECK(CheckPointer(m_pCurrentTracker));
235 }
236#endif
237
238 if (m_pCurrentTracker != NULL)
239 {
240 m_pCurrentTracker->m_hThrowable = hNewThrowable;
241 }
242#else // WIN64EXCEPTIONS
243 m_currentExInfo.m_hThrowable = hNewThrowable;
244#endif // WIN64EXCEPTIONS
245 }
246}
247
248DWORD ThreadExceptionState::GetExceptionCode()
249{
250 LIMITED_METHOD_CONTRACT;
251
252#ifdef WIN64EXCEPTIONS
253 _ASSERTE(m_pCurrentTracker);
254 return m_pCurrentTracker->m_ExceptionCode;
255#else // WIN64EXCEPTIONS
256 return m_currentExInfo.m_ExceptionCode;
257#endif // WIN64EXCEPTIONS
258}
259
260BOOL ThreadExceptionState::IsComPlusException()
261{
262 STATIC_CONTRACT_NOTHROW;
263 STATIC_CONTRACT_GC_NOTRIGGER;
264 STATIC_CONTRACT_FORBID_FAULT;
265
266 if (GetExceptionCode() != EXCEPTION_COMPLUS)
267 {
268 return FALSE;
269 }
270
271 _ASSERTE(IsInstanceTaggedSEHCode(GetExceptionCode()));
272
273
274
275 return GetFlags()->WasThrownByUs();
276}
277
278
279#endif // !DACCESS_COMPILE
280
281BOOL ThreadExceptionState::IsExceptionInProgress()
282{
283 LIMITED_METHOD_DAC_CONTRACT;
284
285#ifdef WIN64EXCEPTIONS
286 return (m_pCurrentTracker != NULL);
287#else // WIN64EXCEPTIONS
288 return (m_currentExInfo.m_pBottomMostHandler != NULL);
289#endif // WIN64EXCEPTIONS
290}
291
292#if !defined(DACCESS_COMPILE)
293
294void ThreadExceptionState::GetLeafFrameInfo(StackTraceElement* pStackTraceElement)
295{
296 WRAPPER_NO_CONTRACT;
297
298#ifdef WIN64EXCEPTIONS
299 m_pCurrentTracker->m_StackTraceInfo.GetLeafFrameInfo(pStackTraceElement);
300#else
301 m_currentExInfo.m_StackTraceInfo.GetLeafFrameInfo(pStackTraceElement);
302#endif
303}
304
305EXCEPTION_POINTERS* ThreadExceptionState::GetExceptionPointers()
306{
307 LIMITED_METHOD_CONTRACT;
308
309#ifdef WIN64EXCEPTIONS
310 if (m_pCurrentTracker)
311 {
312 return (EXCEPTION_POINTERS*)&(m_pCurrentTracker->m_ptrs);
313 }
314 else
315 {
316 return NULL;
317 }
318#else // WIN64EXCEPTIONS
319 return m_currentExInfo.m_pExceptionPointers;
320#endif // WIN64EXCEPTIONS
321}
322
323//-----------------------------------------------------------------------------
324// SetExceptionPointers -- accessor to set pointer to EXCEPTION_POINTERS
325// member.
326//
327// only x86
328//
329#if !defined(WIN64EXCEPTIONS)
330void ThreadExceptionState::SetExceptionPointers(
331 EXCEPTION_POINTERS *pExceptionPointers) // Value to set
332{
333 m_currentExInfo.m_pExceptionPointers = pExceptionPointers;
334} // void ThreadExceptionState::SetExceptionPointers()
335#endif
336
337#endif // !DACCESS_COMPILE
338
339PTR_EXCEPTION_RECORD ThreadExceptionState::GetExceptionRecord()
340{
341 LIMITED_METHOD_DAC_CONTRACT;
342
343#ifdef WIN64EXCEPTIONS
344 if (m_pCurrentTracker)
345 {
346 return m_pCurrentTracker->m_ptrs.ExceptionRecord;
347 }
348 else
349 {
350 return NULL;
351 }
352#else // WIN64EXCEPTIONS
353 return m_currentExInfo.m_pExceptionRecord;
354#endif // WIN64EXCEPTIONS
355}
356
357PTR_CONTEXT ThreadExceptionState::GetContextRecord()
358{
359 LIMITED_METHOD_DAC_CONTRACT;
360
361#ifdef WIN64EXCEPTIONS
362 if (m_pCurrentTracker)
363 {
364 return m_pCurrentTracker->m_ptrs.ContextRecord;
365 }
366 else
367 {
368 return NULL;
369 }
370#else // WIN64EXCEPTIONS
371 return m_currentExInfo.m_pContext;
372#endif // WIN64EXCEPTIONS
373}
374
375ExceptionFlags* ThreadExceptionState::GetFlags()
376{
377#ifdef WIN64EXCEPTIONS
378
379 if (m_pCurrentTracker)
380 {
381 return &(m_pCurrentTracker->m_ExceptionFlags);
382 }
383 else
384 {
385 _ASSERTE(!"GetFlags() called when there is no current exception");
386 return NULL;
387 }
388
389#else // WIN64EXCEPTIONS
390
391 return &(m_currentExInfo.m_ExceptionFlags);
392
393#endif // WIN64EXCEPTIONS
394}
395
396#if !defined(DACCESS_COMPILE)
397
398#ifdef DEBUGGING_SUPPORTED
399DebuggerExState* ThreadExceptionState::GetDebuggerState()
400{
401#ifdef WIN64EXCEPTIONS
402 if (m_pCurrentTracker)
403 {
404 return &(m_pCurrentTracker->m_DebuggerExState);
405 }
406 else
407 {
408 _ASSERTE(!"unexpected use of GetDebuggerState() when no exception in flight");
409#if defined(_MSC_VER)
410 #pragma warning(disable : 4640)
411#endif
412 static DebuggerExState m_emptyDebuggerExState;
413
414#if defined(_MSC_VER)
415 #pragma warning(default : 4640)
416#endif
417 return &m_emptyDebuggerExState;
418 }
419#else // WIN64EXCEPTIONS
420 return &(m_currentExInfo.m_DebuggerExState);
421#endif // WIN64EXCEPTIONS
422}
423
424BOOL ThreadExceptionState::IsDebuggerInterceptable()
425{
426 LIMITED_METHOD_CONTRACT;
427 DWORD ExceptionCode = GetExceptionCode();
428 return (BOOL)((ExceptionCode != STATUS_STACK_OVERFLOW) &&
429 (ExceptionCode != EXCEPTION_BREAKPOINT) &&
430 (ExceptionCode != EXCEPTION_SINGLE_STEP) &&
431 !GetFlags()->UnwindHasStarted() &&
432 !GetFlags()->DebuggerInterceptNotPossible());
433}
434
435#ifdef _TARGET_X86_
436PEXCEPTION_REGISTRATION_RECORD GetClrSEHRecordServicingStackPointer(Thread *pThread, void *pStackPointer);
437#endif // _TARGET_X86_
438
439//---------------------------------------------------------------------------------------
440//
441// This function is called by the debugger to store information necessary to intercept the current exception.
442// This information is consumed by the EH subsystem to start the unwind and resume execution without
443// finding and executing a catch clause.
444//
445// Arguments:
446// pJitManager - the JIT manager for the method where we are going to intercept the exception
447// pThread - the thread on which the interception is taking place
448// methodToken - the MethodDef token of the interception method
449// pFunc - the MethodDesc of the interception method
450// natOffset - the native offset at which we are going to resume execution
451// sfDebuggerInterceptFramePointer
452// - the frame pointer of the interception method frame
453// pFlags - flags on the current exception (ExInfo on x86 and ExceptionTracker on WIN64);
454// to be set by this function to indicate that an interception is going on
455//
456// Return Value:
457// whether the operation is successful
458//
459
460BOOL DebuggerExState::SetDebuggerInterceptInfo(IJitManager *pJitManager,
461 Thread *pThread,
462 const METHODTOKEN& methodToken,
463 MethodDesc *pFunc,
464 ULONG_PTR natOffset,
465 StackFrame sfDebuggerInterceptFramePointer,
466 ExceptionFlags* pFlags)
467{
468 WRAPPER_NO_CONTRACT;
469
470 //
471 // Verify parameters are non-NULL
472 //
473 if ((pJitManager == NULL) ||
474 (pThread == NULL) ||
475 (methodToken.IsNull()) ||
476 (pFunc == NULL) ||
477 (natOffset == (TADDR)0) ||
478 (sfDebuggerInterceptFramePointer.IsNull()))
479 {
480 return FALSE;
481 }
482
483 //
484 // You can only call this function on the currently active exception.
485 //
486 if (this != pThread->GetExceptionState()->GetDebuggerState())
487 {
488 return FALSE;
489 }
490
491 //
492 // Check that the stack pointer is less than as far as we have searched so far.
493 //
494 if (sfDebuggerInterceptFramePointer > m_sfDebuggerIndicatedFramePointer)
495 {
496 return FALSE;
497 }
498
499 int nestingLevel = 0;
500
501#ifndef WIN64EXCEPTIONS
502 //
503 // Get the SEH frame that covers this location on the stack. Note: we pass a skip count of 1. We know that when
504 // this is called, there is a nested exception handler on pThread's stack that is only there during exception
505 // processing, and it won't be there when we go to do the interception. Therefore, we skip that nested record,
506 // and pick the next valid record above it.
507 //
508 m_pDebuggerInterceptFrame = GetClrSEHRecordServicingStackPointer(pThread, (LPVOID)sfDebuggerInterceptFramePointer.SP);
509 if (m_pDebuggerInterceptFrame == EXCEPTION_CHAIN_END)
510 {
511 return FALSE;
512 }
513
514 //
515 // Now we need to search and find the function information for this entry on the stack.
516 //
517 nestingLevel = ComputeEnclosingHandlerNestingLevel(pJitManager,
518 methodToken,
519 natOffset);
520#endif // !WIN64EXCEPTIONS
521
522 //
523 // These values will override the normal information used by the EH subsystem to handle the exception.
524 // They are retrieved by GetDebuggerInterceptInfo().
525 //
526 m_pDebuggerInterceptFunc = pFunc;
527 m_dDebuggerInterceptHandlerDepth = nestingLevel;
528 m_sfDebuggerInterceptFramePointer = sfDebuggerInterceptFramePointer;
529 m_pDebuggerInterceptNativeOffset = natOffset;
530
531 // set a flag on the exception tracking struct to indicate that an interception is in progress
532 pFlags->SetDebuggerInterceptInfo();
533 return TRUE;
534}
535#endif // DEBUGGING_SUPPORTED
536
537#endif // DACCESS_COMPILE
538
539EHClauseInfo* ThreadExceptionState::GetCurrentEHClauseInfo()
540{
541#ifdef WIN64EXCEPTIONS
542 if (m_pCurrentTracker)
543 {
544 return &(m_pCurrentTracker->m_EHClauseInfo);
545 }
546 else
547 {
548 _ASSERTE(!"unexpected use of GetCurrentEHClauseInfo() when no exception in flight");
549#if defined(_MSC_VER)
550 #pragma warning(disable : 4640)
551#endif // defined(_MSC_VER)
552
553 static EHClauseInfo m_emptyEHClauseInfo;
554
555#if defined(_MSC_VER)
556 #pragma warning(default : 4640)
557#endif // defined(_MSC_VER)
558
559 return &m_emptyEHClauseInfo;
560 }
561#else // WIN64EXCEPTIONS
562 return &(m_currentExInfo.m_EHClauseInfo);
563#endif // WIN64EXCEPTIONS
564}
565
566void ThreadExceptionState::SetThreadExceptionFlag(ThreadExceptionFlag flag)
567{
568 LIMITED_METHOD_CONTRACT;
569
570 m_flag = (ThreadExceptionFlag)((DWORD)m_flag | flag);
571}
572
573void ThreadExceptionState::ResetThreadExceptionFlag(ThreadExceptionFlag flag)
574{
575 LIMITED_METHOD_CONTRACT;
576
577 m_flag = (ThreadExceptionFlag)((DWORD)m_flag & ~flag);
578}
579
580BOOL ThreadExceptionState::HasThreadExceptionFlag(ThreadExceptionFlag flag)
581{
582 LIMITED_METHOD_CONTRACT;
583
584 return ((DWORD)m_flag & flag);
585}
586
587ThreadExceptionFlagHolder::ThreadExceptionFlagHolder(ThreadExceptionState::ThreadExceptionFlag flag)
588{
589 WRAPPER_NO_CONTRACT;
590
591 Thread* pThread = GetThread();
592 _ASSERTE(pThread);
593
594 m_pExState = pThread->GetExceptionState();
595
596 m_flag = flag;
597 m_pExState->SetThreadExceptionFlag(m_flag);
598}
599
600ThreadExceptionFlagHolder::~ThreadExceptionFlagHolder()
601{
602 WRAPPER_NO_CONTRACT;
603
604 _ASSERTE(m_pExState);
605 m_pExState->ResetThreadExceptionFlag(m_flag);
606}
607
608#ifdef DACCESS_COMPILE
609
610void
611ThreadExceptionState::EnumChainMemoryRegions(CLRDataEnumMemoryFlags flags)
612{
613#ifdef WIN64EXCEPTIONS
614 ExceptionTracker* head = m_pCurrentTracker;
615
616 if (head == NULL)
617 {
618 return;
619 }
620
621#else // WIN64EXCEPTIONS
622 ExInfo* head = &m_currentExInfo;
623#endif // WIN64EXCEPTIONS
624
625 for (;;)
626 {
627 head->EnumMemoryRegions(flags);
628
629 if (!head->m_pPrevNestedInfo.IsValid())
630 {
631 break;
632 }
633
634 head->m_pPrevNestedInfo.EnumMem();
635 head = head->m_pPrevNestedInfo;
636 }
637}
638
639
640#endif // DACCESS_COMPILE
641
642
643
644