summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Vorlicek <janvorli@microsoft.com>2017-02-25 11:08:35 +0100
committerGitHub <noreply@github.com>2017-02-25 11:08:35 +0100
commitf24ec95e0db5dcae011b394faf8c858c18d1bd65 (patch)
tree613e5edcbf18afb2eb244e4e060ea355948dd80d
parentb5b8b1096171ffecfa0a7c1e643cae6cabd16a23 (diff)
downloadcoreclr-f24ec95e0db5dcae011b394faf8c858c18d1bd65.tar.gz
coreclr-f24ec95e0db5dcae011b394faf8c858c18d1bd65.tar.bz2
coreclr-f24ec95e0db5dcae011b394faf8c858c18d1bd65.zip
Fix GC hole when exception filter throws unhandled exception (#9785)
The extra Unix specific piece of code in the StackFrameIterator::Filter that handles the difference in the exception stack unwinding on Unix was not skipping exception trackers belonging to filter clauses. But that was not right, since filter funclet stack frames behave the same way on Windows and Unix. They can be present on the stack when we reach their parent frame if the filter hasn't finished running yet or they can be gone if the filter completed running, either succesfully or with unhandled exception. This change adds skipping of filter funclet related exception trackers at that place so that the common code processes them. This fixes the GC hole mentioned in the title that was discovered when running some tests with GCStress mode 2.
-rw-r--r--src/vm/exceptionhandling.h5
-rw-r--r--src/vm/stackwalk.cpp29
2 files changed, 23 insertions, 11 deletions
diff --git a/src/vm/exceptionhandling.h b/src/vm/exceptionhandling.h
index 4dba095ff5..6bf58d1bd0 100644
--- a/src/vm/exceptionhandling.h
+++ b/src/vm/exceptionhandling.h
@@ -653,6 +653,11 @@ public:
{
return !m_ExceptionFlags.UnwindHasStarted();
}
+
+ EHClauseInfo* GetEHClauseInfo()
+ {
+ return &m_EHClauseInfo;
+ }
private: ;
diff --git a/src/vm/stackwalk.cpp b/src/vm/stackwalk.cpp
index ffcd453cd7..d6b116bf8f 100644
--- a/src/vm/stackwalk.cpp
+++ b/src/vm/stackwalk.cpp
@@ -1727,10 +1727,12 @@ ProcessFuncletsForGCReporting:
// was a caller of an already executed exception handler based on the previous exception trackers.
// The handler funclet frames are already gone from the stack, so the exception trackers are the
// only source of evidence about it.
- // The filter funclet is different though, its frame is always present on the stack when its parent
- // frame is reached by the stack walker, because no exception can escape a filter funclet.
// This is different from Windows where the full stack is preserved until an exception is fully handled
// and so we can detect it just from walking the stack.
+ // The filter funclet frames are different, they behave the same way on Windows and Unix. They can be present
+ // on the stack when we reach their parent frame if the filter hasn't finished running yet or they can be
+ // gone if the filter completed running, either succesfully or with unhandled exception.
+ // So the special handling below ignores trackers belonging to filter clauses.
bool fProcessingFilterFunclet = !m_sfFuncletParent.IsNull() && !(m_fProcessNonFilterFunclet || m_fProcessIntermediaryNonFilterFunclet);
if (!fRecheckCurrentFrame && !fSkippingFunclet && (pTracker != NULL) && !fProcessingFilterFunclet)
{
@@ -1739,25 +1741,30 @@ ProcessFuncletsForGCReporting:
// frame matching the current frame, it means that the funclet stack frame was reclaimed.
StackFrame sfFuncletParent;
ExceptionTracker* pCurrTracker = pTracker;
- bool hasFuncletStarted = m_crawl.pThread->GetExceptionState()->GetCurrentEHClauseInfo()->IsManagedCodeEntered();
+
+ bool hasFuncletStarted = pTracker->GetEHClauseInfo()->IsManagedCodeEntered();
while (pCurrTracker != NULL)
{
- if (hasFuncletStarted)
+ // Ignore exception trackers for filter clauses, since their frames are handled the same way as on Windows
+ if (pCurrTracker->GetEHClauseInfo()->GetClauseType() != COR_PRF_CLAUSE_FILTER)
{
- sfFuncletParent = pCurrTracker->GetCallerOfEnclosingClause();
+ if (hasFuncletStarted)
+ {
+ sfFuncletParent = pCurrTracker->GetCallerOfEnclosingClause();
+ if (!sfFuncletParent.IsNull() && ExceptionTracker::IsUnwoundToTargetParentFrame(&m_crawl, sfFuncletParent))
+ {
+ break;
+ }
+ }
+
+ sfFuncletParent = pCurrTracker->GetCallerOfCollapsedEnclosingClause();
if (!sfFuncletParent.IsNull() && ExceptionTracker::IsUnwoundToTargetParentFrame(&m_crawl, sfFuncletParent))
{
break;
}
}
- sfFuncletParent = pCurrTracker->GetCallerOfCollapsedEnclosingClause();
- if (!sfFuncletParent.IsNull() && ExceptionTracker::IsUnwoundToTargetParentFrame(&m_crawl, sfFuncletParent))
- {
- break;
- }
-
// Funclets handling exception for trackers older than the current one were always started,
// since the current tracker was created due to an exception in the funclet belonging to
// the previous tracker.