[release/10.0] Move coreclr EH second pass to native code - #120263

Merged
agocke merged 3 commits into
dotnet:release/10.0from
janvorli:backport-move-eh-2nd-pass-to-native
Oct 6, 2025
Merged

[release/10.0] Move coreclr EH second pass to native code#120263
agocke merged 3 commits into
dotnet:release/10.0from
janvorli:backport-move-eh-2nd-pass-to-native

Conversation

@janvorli

Copy link
Copy Markdown
Member

Backport of #119863 to release/10.0

Customer Impact

  • Customer reported
  • Found internally

There were some GC holes discovered caused by the fact that GC can be triggered during 2nd pass of EH in-between calls to finally handlers and catch handler. After considering options, moving the 2nd pass to native code seems the most reasonable solution.
The issue was reported by the RavenDB folks in #110769. It causes access violation crash on their system on Windows once in one or two days on average.

Regression

  • Yes
  • No

Introduced in .NET 8 by #88034 - the new exception handling, but got reported in .NET 9 since that's where the new EH was enabled by default.
It took almost 9 months to figure out the culprit due to the very narrow window in which it can occur and due to the fact that GC holes in general don't necessarily result in an observed bad behavior. That's why it was fixed this late in the release cycle.

Testing

  • Repro test created based on the customer code structure with instrumented coreclr where GC is injected at a specific problematic spot. It fails before the fix with a GC hole and passes with the fix.
  • Diagnostic tests
  • coreclr / libraries tests

Risk

Low, the change is 1:1 port of the 2nd pass of the EH from C# to C++ and then removal of a number of extra handling in the stack frame iterator and GC reference enumeration that was only necessary to support the cases when GC could have been happening in-between calls to finallys. That's no longer possible with the fix.

@janvorlijanvorli self-assigned this Sep 30, 2025
CopilotAI review requested due to automatic review settings September 30, 2025 17:14
@janvorlijanvorli added Servicing-consider Issue for next servicing release review area-ExceptionHandling-coreclr only use for closed issues labels Sep 30, 2025

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR moves the CoreCLR exception handling second pass from managed C# code to native C++ code to fix GC holes that occurred when the garbage collector was triggered between finally handler and catch handler calls during the second pass. This change addresses a customer-reported access violation crash in RavenDB that occurred intermittently due to these GC holes.

Key changes:

  • Remove managed second pass dispatch code and move logic to native implementation
  • Eliminate complex GC reference tracking infrastructure that was needed to handle GC during managed EH code
  • Simplify stack walking by removing special handling for managed exception handling frames

Reviewed Changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 3 comments.

Show a summary per file
FileDescription
src/coreclr/vm/stackwalk.hRemove GC reference tracking fields and methods for funclet handling
src/coreclr/vm/stackwalk.cppRemove complex GC reference tracking logic during stack walking
src/coreclr/vm/qcallentrypoints.cppRemove QCall entry points for managed funclet calls
src/coreclr/vm/metasig.hRemove method signature for managed unwind and intercept
src/coreclr/vm/gcenv.ee.common.cppRemove saved funclet info GC reference reporting
src/coreclr/vm/exinfo.hAdd new fields for catch handler info, remove saved funclet tracking
src/coreclr/vm/exinfo.cppRemove saved funclet initialization
src/coreclr/vm/exceptionhandlingqcalls.hRemove QCall declarations for managed funclet calls
src/coreclr/vm/exceptionhandling.hRemove second pass funclet caller marker
src/coreclr/vm/exceptionhandling.cppAdd native second pass implementation, convert funclet callers to native
src/coreclr/vm/excep.cppAdd second pass calls and remove old intercept unwind code
src/coreclr/vm/corelib.hRemove managed unwind and intercept method definition
src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.csAdd conditional compilation for CoreCLR vs NativeAOT paths
src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/InternalCalls.csRemove managed funclet call declarations
src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/AsmOffsets.csUpdate structure sizes and add new ExInfo field offsets

Comment threadsrc/coreclr/vm/exceptionhandling.cpp
Comment threadsrc/coreclr/vm/exceptionhandling.cpp
@janvorli
janvorliforce-pushed the backport-move-eh-2nd-pass-to-native branch 3 times, most recently from b8ea280 to bf4fa55CompareOctober 1, 2025 16:22
There were some GC holes discovered caused by the fact that GC can be
triggered during 2nd pass of EH in-between calls to finally handlers and
catch handler. After considering options, moving the 2nd pass to native
code seems the most reasonable solution.
@janvorli
janvorliforce-pushed the backport-move-eh-2nd-pass-to-native branch from bf4fa55 to 7fc9c86CompareOctober 1, 2025 17:50
@janvorli

Copy link
Copy Markdown
MemberAuthor

The interpreter test is failing because it would need another PR from main to work correctly with this changed EH. The reason is that resume after catch in the .NET 10 branch expects that the RhThrowEx and the like are on the stack. I've simplified / fixed that in main.
@agocke, @jkotas I am going to disable the interpreter test in .NET 10 branch so that I don't need to pull in extra changes that are not needed for .NET 10. The change was not trivial.

@janvorli
janvorliforce-pushed the backport-move-eh-2nd-pass-to-native branch from c666bc0 to 288916dCompareOctober 1, 2025 21:46
@rbhandarbhanda added Servicing-approved Approved for servicing release and removed Servicing-consider Issue for next servicing release review labels Oct 2, 2025
@rbhandarbhanda added this to the 10.0.0 milestone Oct 2, 2025
@agocke
agocke merged commit 468465c into dotnet:release/10.0Oct 6, 2025
105 checks passed
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Nov 6, 2025
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-ExceptionHandling-coreclronly use for closed issuesServicing-approvedApproved for servicing release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@janvorli@jkotas@agocke@rbhanda
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

[release/10.0] Move coreclr EH second pass to native code - #120263

Merged
agocke merged 3 commits into
dotnet:release/10.0from
janvorli:backport-move-eh-2nd-pass-to-native
Oct 6, 2025
Merged

[release/10.0] Move coreclr EH second pass to native code#120263
agocke merged 3 commits into
dotnet:release/10.0from
janvorli:backport-move-eh-2nd-pass-to-native

Conversation

@janvorli

Copy link
Copy Markdown
Member

Backport of #119863 to release/10.0

Customer Impact

  • Customer reported
  • Found internally

There were some GC holes discovered caused by the fact that GC can be triggered during 2nd pass of EH in-between calls to finally handlers and catch handler. After considering options, moving the 2nd pass to native code seems the most reasonable solution.
The issue was reported by the RavenDB folks in #110769. It causes access violation crash on their system on Windows once in one or two days on average.

Regression

  • Yes
  • No

Introduced in .NET 8 by #88034 - the new exception handling, but got reported in .NET 9 since that's where the new EH was enabled by default.
It took almost 9 months to figure out the culprit due to the very narrow window in which it can occur and due to the fact that GC holes in general don't necessarily result in an observed bad behavior. That's why it was fixed this late in the release cycle.

Testing

  • Repro test created based on the customer code structure with instrumented coreclr where GC is injected at a specific problematic spot. It fails before the fix with a GC hole and passes with the fix.
  • Diagnostic tests
  • coreclr / libraries tests

Risk

Low, the change is 1:1 port of the 2nd pass of the EH from C# to C++ and then removal of a number of extra handling in the stack frame iterator and GC reference enumeration that was only necessary to support the cases when GC could have been happening in-between calls to finallys. That's no longer possible with the fix.

@janvorlijanvorli self-assigned this Sep 30, 2025
CopilotAI review requested due to automatic review settings September 30, 2025 17:14
@janvorlijanvorli added Servicing-consider Issue for next servicing release review area-ExceptionHandling-coreclr only use for closed issues labels Sep 30, 2025

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR moves the CoreCLR exception handling second pass from managed C# code to native C++ code to fix GC holes that occurred when the garbage collector was triggered between finally handler and catch handler calls during the second pass. This change addresses a customer-reported access violation crash in RavenDB that occurred intermittently due to these GC holes.

Key changes:

  • Remove managed second pass dispatch code and move logic to native implementation
  • Eliminate complex GC reference tracking infrastructure that was needed to handle GC during managed EH code
  • Simplify stack walking by removing special handling for managed exception handling frames

Reviewed Changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 3 comments.

Show a summary per file
FileDescription
src/coreclr/vm/stackwalk.hRemove GC reference tracking fields and methods for funclet handling
src/coreclr/vm/stackwalk.cppRemove complex GC reference tracking logic during stack walking
src/coreclr/vm/qcallentrypoints.cppRemove QCall entry points for managed funclet calls
src/coreclr/vm/metasig.hRemove method signature for managed unwind and intercept
src/coreclr/vm/gcenv.ee.common.cppRemove saved funclet info GC reference reporting
src/coreclr/vm/exinfo.hAdd new fields for catch handler info, remove saved funclet tracking
src/coreclr/vm/exinfo.cppRemove saved funclet initialization
src/coreclr/vm/exceptionhandlingqcalls.hRemove QCall declarations for managed funclet calls
src/coreclr/vm/exceptionhandling.hRemove second pass funclet caller marker
src/coreclr/vm/exceptionhandling.cppAdd native second pass implementation, convert funclet callers to native
src/coreclr/vm/excep.cppAdd second pass calls and remove old intercept unwind code
src/coreclr/vm/corelib.hRemove managed unwind and intercept method definition
src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.csAdd conditional compilation for CoreCLR vs NativeAOT paths
src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/InternalCalls.csRemove managed funclet call declarations
src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/AsmOffsets.csUpdate structure sizes and add new ExInfo field offsets

Comment threadsrc/coreclr/vm/exceptionhandling.cpp
Comment threadsrc/coreclr/vm/exceptionhandling.cpp
@janvorli
janvorliforce-pushed the backport-move-eh-2nd-pass-to-native branch 3 times, most recently from b8ea280 to bf4fa55CompareOctober 1, 2025 16:22
There were some GC holes discovered caused by the fact that GC can be
triggered during 2nd pass of EH in-between calls to finally handlers and
catch handler. After considering options, moving the 2nd pass to native
code seems the most reasonable solution.
@janvorli
janvorliforce-pushed the backport-move-eh-2nd-pass-to-native branch from bf4fa55 to 7fc9c86CompareOctober 1, 2025 17:50
@janvorli

Copy link
Copy Markdown
MemberAuthor

The interpreter test is failing because it would need another PR from main to work correctly with this changed EH. The reason is that resume after catch in the .NET 10 branch expects that the RhThrowEx and the like are on the stack. I've simplified / fixed that in main.
@agocke, @jkotas I am going to disable the interpreter test in .NET 10 branch so that I don't need to pull in extra changes that are not needed for .NET 10. The change was not trivial.

@janvorli
janvorliforce-pushed the backport-move-eh-2nd-pass-to-native branch from c666bc0 to 288916dCompareOctober 1, 2025 21:46
@rbhandarbhanda added Servicing-approved Approved for servicing release and removed Servicing-consider Issue for next servicing release review labels Oct 2, 2025
@rbhandarbhanda added this to the 10.0.0 milestone Oct 2, 2025
@agocke
agocke merged commit 468465c into dotnet:release/10.0Oct 6, 2025
105 checks passed
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Nov 6, 2025
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-ExceptionHandling-coreclronly use for closed issuesServicing-approvedApproved for servicing release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@janvorli@jkotas@agocke@rbhanda
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[release/10.0] Move coreclr EH second pass to native code - #120263

Merged
agocke merged 3 commits into
dotnet:release/10.0from
janvorli:backport-move-eh-2nd-pass-to-native
Oct 6, 2025
Merged

[release/10.0] Move coreclr EH second pass to native code#120263
agocke merged 3 commits into
dotnet:release/10.0from
janvorli:backport-move-eh-2nd-pass-to-native

Conversation

@janvorli

Copy link
Copy Markdown
Member

Backport of #119863 to release/10.0

Customer Impact

  • Customer reported
  • Found internally

There were some GC holes discovered caused by the fact that GC can be triggered during 2nd pass of EH in-between calls to finally handlers and catch handler. After considering options, moving the 2nd pass to native code seems the most reasonable solution.
The issue was reported by the RavenDB folks in #110769. It causes access violation crash on their system on Windows once in one or two days on average.

Regression

  • Yes
  • No

Introduced in .NET 8 by #88034 - the new exception handling, but got reported in .NET 9 since that's where the new EH was enabled by default.
It took almost 9 months to figure out the culprit due to the very narrow window in which it can occur and due to the fact that GC holes in general don't necessarily result in an observed bad behavior. That's why it was fixed this late in the release cycle.

Testing

  • Repro test created based on the customer code structure with instrumented coreclr where GC is injected at a specific problematic spot. It fails before the fix with a GC hole and passes with the fix.
  • Diagnostic tests
  • coreclr / libraries tests

Risk

Low, the change is 1:1 port of the 2nd pass of the EH from C# to C++ and then removal of a number of extra handling in the stack frame iterator and GC reference enumeration that was only necessary to support the cases when GC could have been happening in-between calls to finallys. That's no longer possible with the fix.

@janvorlijanvorli self-assigned this Sep 30, 2025
CopilotAI review requested due to automatic review settings September 30, 2025 17:14
@janvorlijanvorli added Servicing-consider Issue for next servicing release review area-ExceptionHandling-coreclr only use for closed issues labels Sep 30, 2025

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR moves the CoreCLR exception handling second pass from managed C# code to native C++ code to fix GC holes that occurred when the garbage collector was triggered between finally handler and catch handler calls during the second pass. This change addresses a customer-reported access violation crash in RavenDB that occurred intermittently due to these GC holes.

Key changes:

  • Remove managed second pass dispatch code and move logic to native implementation
  • Eliminate complex GC reference tracking infrastructure that was needed to handle GC during managed EH code
  • Simplify stack walking by removing special handling for managed exception handling frames

Reviewed Changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 3 comments.

Show a summary per file
FileDescription
src/coreclr/vm/stackwalk.hRemove GC reference tracking fields and methods for funclet handling
src/coreclr/vm/stackwalk.cppRemove complex GC reference tracking logic during stack walking
src/coreclr/vm/qcallentrypoints.cppRemove QCall entry points for managed funclet calls
src/coreclr/vm/metasig.hRemove method signature for managed unwind and intercept
src/coreclr/vm/gcenv.ee.common.cppRemove saved funclet info GC reference reporting
src/coreclr/vm/exinfo.hAdd new fields for catch handler info, remove saved funclet tracking
src/coreclr/vm/exinfo.cppRemove saved funclet initialization
src/coreclr/vm/exceptionhandlingqcalls.hRemove QCall declarations for managed funclet calls
src/coreclr/vm/exceptionhandling.hRemove second pass funclet caller marker
src/coreclr/vm/exceptionhandling.cppAdd native second pass implementation, convert funclet callers to native
src/coreclr/vm/excep.cppAdd second pass calls and remove old intercept unwind code
src/coreclr/vm/corelib.hRemove managed unwind and intercept method definition
src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.csAdd conditional compilation for CoreCLR vs NativeAOT paths
src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/InternalCalls.csRemove managed funclet call declarations
src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/AsmOffsets.csUpdate structure sizes and add new ExInfo field offsets

Comment threadsrc/coreclr/vm/exceptionhandling.cpp
Comment threadsrc/coreclr/vm/exceptionhandling.cpp
@janvorli
janvorliforce-pushed the backport-move-eh-2nd-pass-to-native branch 3 times, most recently from b8ea280 to bf4fa55CompareOctober 1, 2025 16:22
There were some GC holes discovered caused by the fact that GC can be
triggered during 2nd pass of EH in-between calls to finally handlers and
catch handler. After considering options, moving the 2nd pass to native
code seems the most reasonable solution.
@janvorli
janvorliforce-pushed the backport-move-eh-2nd-pass-to-native branch from bf4fa55 to 7fc9c86CompareOctober 1, 2025 17:50
@janvorli

Copy link
Copy Markdown
MemberAuthor

The interpreter test is failing because it would need another PR from main to work correctly with this changed EH. The reason is that resume after catch in the .NET 10 branch expects that the RhThrowEx and the like are on the stack. I've simplified / fixed that in main.
@agocke, @jkotas I am going to disable the interpreter test in .NET 10 branch so that I don't need to pull in extra changes that are not needed for .NET 10. The change was not trivial.

@janvorli
janvorliforce-pushed the backport-move-eh-2nd-pass-to-native branch from c666bc0 to 288916dCompareOctober 1, 2025 21:46
@rbhandarbhanda added Servicing-approved Approved for servicing release and removed Servicing-consider Issue for next servicing release review labels Oct 2, 2025
@rbhandarbhanda added this to the 10.0.0 milestone Oct 2, 2025
@agocke
agocke merged commit 468465c into dotnet:release/10.0Oct 6, 2025
105 checks passed
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Nov 6, 2025
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-ExceptionHandling-coreclronly use for closed issuesServicing-approvedApproved for servicing release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@janvorli@jkotas@agocke@rbhanda
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[release/10.0] Move coreclr EH second pass to native code - #120263

Merged
agocke merged 3 commits into
dotnet:release/10.0from
janvorli:backport-move-eh-2nd-pass-to-native
Oct 6, 2025
Merged

[release/10.0] Move coreclr EH second pass to native code#120263
agocke merged 3 commits into
dotnet:release/10.0from
janvorli:backport-move-eh-2nd-pass-to-native

Conversation

@janvorli

Copy link
Copy Markdown
Member

Backport of #119863 to release/10.0

Customer Impact

  • Customer reported
  • Found internally

There were some GC holes discovered caused by the fact that GC can be triggered during 2nd pass of EH in-between calls to finally handlers and catch handler. After considering options, moving the 2nd pass to native code seems the most reasonable solution.
The issue was reported by the RavenDB folks in #110769. It causes access violation crash on their system on Windows once in one or two days on average.

Regression

  • Yes
  • No

Introduced in .NET 8 by #88034 - the new exception handling, but got reported in .NET 9 since that's where the new EH was enabled by default.
It took almost 9 months to figure out the culprit due to the very narrow window in which it can occur and due to the fact that GC holes in general don't necessarily result in an observed bad behavior. That's why it was fixed this late in the release cycle.

Testing

  • Repro test created based on the customer code structure with instrumented coreclr where GC is injected at a specific problematic spot. It fails before the fix with a GC hole and passes with the fix.
  • Diagnostic tests
  • coreclr / libraries tests

Risk

Low, the change is 1:1 port of the 2nd pass of the EH from C# to C++ and then removal of a number of extra handling in the stack frame iterator and GC reference enumeration that was only necessary to support the cases when GC could have been happening in-between calls to finallys. That's no longer possible with the fix.

@janvorlijanvorli self-assigned this Sep 30, 2025
CopilotAI review requested due to automatic review settings September 30, 2025 17:14
@janvorlijanvorli added Servicing-consider Issue for next servicing release review area-ExceptionHandling-coreclr only use for closed issues labels Sep 30, 2025

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR moves the CoreCLR exception handling second pass from managed C# code to native C++ code to fix GC holes that occurred when the garbage collector was triggered between finally handler and catch handler calls during the second pass. This change addresses a customer-reported access violation crash in RavenDB that occurred intermittently due to these GC holes.

Key changes:

  • Remove managed second pass dispatch code and move logic to native implementation
  • Eliminate complex GC reference tracking infrastructure that was needed to handle GC during managed EH code
  • Simplify stack walking by removing special handling for managed exception handling frames

Reviewed Changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 3 comments.

Show a summary per file
FileDescription
src/coreclr/vm/stackwalk.hRemove GC reference tracking fields and methods for funclet handling
src/coreclr/vm/stackwalk.cppRemove complex GC reference tracking logic during stack walking
src/coreclr/vm/qcallentrypoints.cppRemove QCall entry points for managed funclet calls
src/coreclr/vm/metasig.hRemove method signature for managed unwind and intercept
src/coreclr/vm/gcenv.ee.common.cppRemove saved funclet info GC reference reporting
src/coreclr/vm/exinfo.hAdd new fields for catch handler info, remove saved funclet tracking
src/coreclr/vm/exinfo.cppRemove saved funclet initialization
src/coreclr/vm/exceptionhandlingqcalls.hRemove QCall declarations for managed funclet calls
src/coreclr/vm/exceptionhandling.hRemove second pass funclet caller marker
src/coreclr/vm/exceptionhandling.cppAdd native second pass implementation, convert funclet callers to native
src/coreclr/vm/excep.cppAdd second pass calls and remove old intercept unwind code
src/coreclr/vm/corelib.hRemove managed unwind and intercept method definition
src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.csAdd conditional compilation for CoreCLR vs NativeAOT paths
src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/InternalCalls.csRemove managed funclet call declarations
src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/AsmOffsets.csUpdate structure sizes and add new ExInfo field offsets

Comment threadsrc/coreclr/vm/exceptionhandling.cpp
Comment threadsrc/coreclr/vm/exceptionhandling.cpp
@janvorli
janvorliforce-pushed the backport-move-eh-2nd-pass-to-native branch 3 times, most recently from b8ea280 to bf4fa55CompareOctober 1, 2025 16:22
There were some GC holes discovered caused by the fact that GC can be
triggered during 2nd pass of EH in-between calls to finally handlers and
catch handler. After considering options, moving the 2nd pass to native
code seems the most reasonable solution.
@janvorli
janvorliforce-pushed the backport-move-eh-2nd-pass-to-native branch from bf4fa55 to 7fc9c86CompareOctober 1, 2025 17:50
@janvorli

Copy link
Copy Markdown
MemberAuthor

The interpreter test is failing because it would need another PR from main to work correctly with this changed EH. The reason is that resume after catch in the .NET 10 branch expects that the RhThrowEx and the like are on the stack. I've simplified / fixed that in main.
@agocke, @jkotas I am going to disable the interpreter test in .NET 10 branch so that I don't need to pull in extra changes that are not needed for .NET 10. The change was not trivial.

@janvorli
janvorliforce-pushed the backport-move-eh-2nd-pass-to-native branch from c666bc0 to 288916dCompareOctober 1, 2025 21:46
@rbhandarbhanda added Servicing-approved Approved for servicing release and removed Servicing-consider Issue for next servicing release review labels Oct 2, 2025
@rbhandarbhanda added this to the 10.0.0 milestone Oct 2, 2025
@agocke
agocke merged commit 468465c into dotnet:release/10.0Oct 6, 2025
105 checks passed
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Nov 6, 2025
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-ExceptionHandling-coreclronly use for closed issuesServicing-approvedApproved for servicing release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@janvorli@jkotas@agocke@rbhanda
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

[release/10.0] Move coreclr EH second pass to native code - #120263

Merged
agocke merged 3 commits into
dotnet:release/10.0from
janvorli:backport-move-eh-2nd-pass-to-native
Oct 6, 2025
Merged

[release/10.0] Move coreclr EH second pass to native code#120263
agocke merged 3 commits into
dotnet:release/10.0from
janvorli:backport-move-eh-2nd-pass-to-native

Conversation

@janvorli

Copy link
Copy Markdown
Member

Backport of #119863 to release/10.0

Customer Impact

  • Customer reported
  • Found internally

There were some GC holes discovered caused by the fact that GC can be triggered during 2nd pass of EH in-between calls to finally handlers and catch handler. After considering options, moving the 2nd pass to native code seems the most reasonable solution.
The issue was reported by the RavenDB folks in #110769. It causes access violation crash on their system on Windows once in one or two days on average.

Regression

  • Yes
  • No

Introduced in .NET 8 by #88034 - the new exception handling, but got reported in .NET 9 since that's where the new EH was enabled by default.
It took almost 9 months to figure out the culprit due to the very narrow window in which it can occur and due to the fact that GC holes in general don't necessarily result in an observed bad behavior. That's why it was fixed this late in the release cycle.

Testing

  • Repro test created based on the customer code structure with instrumented coreclr where GC is injected at a specific problematic spot. It fails before the fix with a GC hole and passes with the fix.
  • Diagnostic tests
  • coreclr / libraries tests

Risk

Low, the change is 1:1 port of the 2nd pass of the EH from C# to C++ and then removal of a number of extra handling in the stack frame iterator and GC reference enumeration that was only necessary to support the cases when GC could have been happening in-between calls to finallys. That's no longer possible with the fix.

@janvorlijanvorli self-assigned this Sep 30, 2025
CopilotAI review requested due to automatic review settings September 30, 2025 17:14
@janvorlijanvorli added Servicing-consider Issue for next servicing release review area-ExceptionHandling-coreclr only use for closed issues labels Sep 30, 2025

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR moves the CoreCLR exception handling second pass from managed C# code to native C++ code to fix GC holes that occurred when the garbage collector was triggered between finally handler and catch handler calls during the second pass. This change addresses a customer-reported access violation crash in RavenDB that occurred intermittently due to these GC holes.

Key changes:

  • Remove managed second pass dispatch code and move logic to native implementation
  • Eliminate complex GC reference tracking infrastructure that was needed to handle GC during managed EH code
  • Simplify stack walking by removing special handling for managed exception handling frames

Reviewed Changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 3 comments.

Show a summary per file
FileDescription
src/coreclr/vm/stackwalk.hRemove GC reference tracking fields and methods for funclet handling
src/coreclr/vm/stackwalk.cppRemove complex GC reference tracking logic during stack walking
src/coreclr/vm/qcallentrypoints.cppRemove QCall entry points for managed funclet calls
src/coreclr/vm/metasig.hRemove method signature for managed unwind and intercept
src/coreclr/vm/gcenv.ee.common.cppRemove saved funclet info GC reference reporting
src/coreclr/vm/exinfo.hAdd new fields for catch handler info, remove saved funclet tracking
src/coreclr/vm/exinfo.cppRemove saved funclet initialization
src/coreclr/vm/exceptionhandlingqcalls.hRemove QCall declarations for managed funclet calls
src/coreclr/vm/exceptionhandling.hRemove second pass funclet caller marker
src/coreclr/vm/exceptionhandling.cppAdd native second pass implementation, convert funclet callers to native
src/coreclr/vm/excep.cppAdd second pass calls and remove old intercept unwind code
src/coreclr/vm/corelib.hRemove managed unwind and intercept method definition
src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.csAdd conditional compilation for CoreCLR vs NativeAOT paths
src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/InternalCalls.csRemove managed funclet call declarations
src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/AsmOffsets.csUpdate structure sizes and add new ExInfo field offsets

Comment threadsrc/coreclr/vm/exceptionhandling.cpp
Comment threadsrc/coreclr/vm/exceptionhandling.cpp
@janvorli
janvorliforce-pushed the backport-move-eh-2nd-pass-to-native branch 3 times, most recently from b8ea280 to bf4fa55CompareOctober 1, 2025 16:22
There were some GC holes discovered caused by the fact that GC can be
triggered during 2nd pass of EH in-between calls to finally handlers and
catch handler. After considering options, moving the 2nd pass to native
code seems the most reasonable solution.
@janvorli
janvorliforce-pushed the backport-move-eh-2nd-pass-to-native branch from bf4fa55 to 7fc9c86CompareOctober 1, 2025 17:50
@janvorli

Copy link
Copy Markdown
MemberAuthor

The interpreter test is failing because it would need another PR from main to work correctly with this changed EH. The reason is that resume after catch in the .NET 10 branch expects that the RhThrowEx and the like are on the stack. I've simplified / fixed that in main.
@agocke, @jkotas I am going to disable the interpreter test in .NET 10 branch so that I don't need to pull in extra changes that are not needed for .NET 10. The change was not trivial.

@janvorli
janvorliforce-pushed the backport-move-eh-2nd-pass-to-native branch from c666bc0 to 288916dCompareOctober 1, 2025 21:46
@rbhandarbhanda added Servicing-approved Approved for servicing release and removed Servicing-consider Issue for next servicing release review labels Oct 2, 2025
@rbhandarbhanda added this to the 10.0.0 milestone Oct 2, 2025
@agocke
agocke merged commit 468465c into dotnet:release/10.0Oct 6, 2025
105 checks passed
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Nov 6, 2025
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-ExceptionHandling-coreclronly use for closed issuesServicing-approvedApproved for servicing release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@janvorli@jkotas@agocke@rbhanda
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[release/10.0] Move coreclr EH second pass to native code - #120263

Merged
agocke merged 3 commits into
dotnet:release/10.0from
janvorli:backport-move-eh-2nd-pass-to-native
Oct 6, 2025
Merged

[release/10.0] Move coreclr EH second pass to native code#120263
agocke merged 3 commits into
dotnet:release/10.0from
janvorli:backport-move-eh-2nd-pass-to-native

Conversation

@janvorli

Copy link
Copy Markdown
Member

Backport of #119863 to release/10.0

Customer Impact

  • Customer reported
  • Found internally

There were some GC holes discovered caused by the fact that GC can be triggered during 2nd pass of EH in-between calls to finally handlers and catch handler. After considering options, moving the 2nd pass to native code seems the most reasonable solution.
The issue was reported by the RavenDB folks in #110769. It causes access violation crash on their system on Windows once in one or two days on average.

Regression

  • Yes
  • No

Introduced in .NET 8 by #88034 - the new exception handling, but got reported in .NET 9 since that's where the new EH was enabled by default.
It took almost 9 months to figure out the culprit due to the very narrow window in which it can occur and due to the fact that GC holes in general don't necessarily result in an observed bad behavior. That's why it was fixed this late in the release cycle.

Testing

  • Repro test created based on the customer code structure with instrumented coreclr where GC is injected at a specific problematic spot. It fails before the fix with a GC hole and passes with the fix.
  • Diagnostic tests
  • coreclr / libraries tests

Risk

Low, the change is 1:1 port of the 2nd pass of the EH from C# to C++ and then removal of a number of extra handling in the stack frame iterator and GC reference enumeration that was only necessary to support the cases when GC could have been happening in-between calls to finallys. That's no longer possible with the fix.

@janvorlijanvorli self-assigned this Sep 30, 2025
CopilotAI review requested due to automatic review settings September 30, 2025 17:14
@janvorlijanvorli added Servicing-consider Issue for next servicing release review area-ExceptionHandling-coreclr only use for closed issues labels Sep 30, 2025

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR moves the CoreCLR exception handling second pass from managed C# code to native C++ code to fix GC holes that occurred when the garbage collector was triggered between finally handler and catch handler calls during the second pass. This change addresses a customer-reported access violation crash in RavenDB that occurred intermittently due to these GC holes.

Key changes:

  • Remove managed second pass dispatch code and move logic to native implementation
  • Eliminate complex GC reference tracking infrastructure that was needed to handle GC during managed EH code
  • Simplify stack walking by removing special handling for managed exception handling frames

Reviewed Changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 3 comments.

Show a summary per file
FileDescription
src/coreclr/vm/stackwalk.hRemove GC reference tracking fields and methods for funclet handling
src/coreclr/vm/stackwalk.cppRemove complex GC reference tracking logic during stack walking
src/coreclr/vm/qcallentrypoints.cppRemove QCall entry points for managed funclet calls
src/coreclr/vm/metasig.hRemove method signature for managed unwind and intercept
src/coreclr/vm/gcenv.ee.common.cppRemove saved funclet info GC reference reporting
src/coreclr/vm/exinfo.hAdd new fields for catch handler info, remove saved funclet tracking
src/coreclr/vm/exinfo.cppRemove saved funclet initialization
src/coreclr/vm/exceptionhandlingqcalls.hRemove QCall declarations for managed funclet calls
src/coreclr/vm/exceptionhandling.hRemove second pass funclet caller marker
src/coreclr/vm/exceptionhandling.cppAdd native second pass implementation, convert funclet callers to native
src/coreclr/vm/excep.cppAdd second pass calls and remove old intercept unwind code
src/coreclr/vm/corelib.hRemove managed unwind and intercept method definition
src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.csAdd conditional compilation for CoreCLR vs NativeAOT paths
src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/InternalCalls.csRemove managed funclet call declarations
src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/AsmOffsets.csUpdate structure sizes and add new ExInfo field offsets

Comment threadsrc/coreclr/vm/exceptionhandling.cpp
Comment threadsrc/coreclr/vm/exceptionhandling.cpp
@janvorli
janvorliforce-pushed the backport-move-eh-2nd-pass-to-native branch 3 times, most recently from b8ea280 to bf4fa55CompareOctober 1, 2025 16:22
There were some GC holes discovered caused by the fact that GC can be
triggered during 2nd pass of EH in-between calls to finally handlers and
catch handler. After considering options, moving the 2nd pass to native
code seems the most reasonable solution.
@janvorli
janvorliforce-pushed the backport-move-eh-2nd-pass-to-native branch from bf4fa55 to 7fc9c86CompareOctober 1, 2025 17:50
@janvorli

Copy link
Copy Markdown
MemberAuthor

The interpreter test is failing because it would need another PR from main to work correctly with this changed EH. The reason is that resume after catch in the .NET 10 branch expects that the RhThrowEx and the like are on the stack. I've simplified / fixed that in main.
@agocke, @jkotas I am going to disable the interpreter test in .NET 10 branch so that I don't need to pull in extra changes that are not needed for .NET 10. The change was not trivial.

@janvorli
janvorliforce-pushed the backport-move-eh-2nd-pass-to-native branch from c666bc0 to 288916dCompareOctober 1, 2025 21:46
@rbhandarbhanda added Servicing-approved Approved for servicing release and removed Servicing-consider Issue for next servicing release review labels Oct 2, 2025
@rbhandarbhanda added this to the 10.0.0 milestone Oct 2, 2025
@agocke
agocke merged commit 468465c into dotnet:release/10.0Oct 6, 2025
105 checks passed
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Nov 6, 2025
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-ExceptionHandling-coreclronly use for closed issuesServicing-approvedApproved for servicing release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@janvorli@jkotas@agocke@rbhanda
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[release/10.0] Move coreclr EH second pass to native code - #120263

Merged
agocke merged 3 commits into
dotnet:release/10.0from
janvorli:backport-move-eh-2nd-pass-to-native
Oct 6, 2025
Merged

[release/10.0] Move coreclr EH second pass to native code#120263
agocke merged 3 commits into
dotnet:release/10.0from
janvorli:backport-move-eh-2nd-pass-to-native

Conversation

@janvorli

Copy link
Copy Markdown
Member

Backport of #119863 to release/10.0

Customer Impact

  • Customer reported
  • Found internally

There were some GC holes discovered caused by the fact that GC can be triggered during 2nd pass of EH in-between calls to finally handlers and catch handler. After considering options, moving the 2nd pass to native code seems the most reasonable solution.
The issue was reported by the RavenDB folks in #110769. It causes access violation crash on their system on Windows once in one or two days on average.

Regression

  • Yes
  • No

Introduced in .NET 8 by #88034 - the new exception handling, but got reported in .NET 9 since that's where the new EH was enabled by default.
It took almost 9 months to figure out the culprit due to the very narrow window in which it can occur and due to the fact that GC holes in general don't necessarily result in an observed bad behavior. That's why it was fixed this late in the release cycle.

Testing

  • Repro test created based on the customer code structure with instrumented coreclr where GC is injected at a specific problematic spot. It fails before the fix with a GC hole and passes with the fix.
  • Diagnostic tests
  • coreclr / libraries tests

Risk

Low, the change is 1:1 port of the 2nd pass of the EH from C# to C++ and then removal of a number of extra handling in the stack frame iterator and GC reference enumeration that was only necessary to support the cases when GC could have been happening in-between calls to finallys. That's no longer possible with the fix.

@janvorlijanvorli self-assigned this Sep 30, 2025
CopilotAI review requested due to automatic review settings September 30, 2025 17:14
@janvorlijanvorli added Servicing-consider Issue for next servicing release review area-ExceptionHandling-coreclr only use for closed issues labels Sep 30, 2025

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR moves the CoreCLR exception handling second pass from managed C# code to native C++ code to fix GC holes that occurred when the garbage collector was triggered between finally handler and catch handler calls during the second pass. This change addresses a customer-reported access violation crash in RavenDB that occurred intermittently due to these GC holes.

Key changes:

  • Remove managed second pass dispatch code and move logic to native implementation
  • Eliminate complex GC reference tracking infrastructure that was needed to handle GC during managed EH code
  • Simplify stack walking by removing special handling for managed exception handling frames

Reviewed Changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 3 comments.

Show a summary per file
FileDescription
src/coreclr/vm/stackwalk.hRemove GC reference tracking fields and methods for funclet handling
src/coreclr/vm/stackwalk.cppRemove complex GC reference tracking logic during stack walking
src/coreclr/vm/qcallentrypoints.cppRemove QCall entry points for managed funclet calls
src/coreclr/vm/metasig.hRemove method signature for managed unwind and intercept
src/coreclr/vm/gcenv.ee.common.cppRemove saved funclet info GC reference reporting
src/coreclr/vm/exinfo.hAdd new fields for catch handler info, remove saved funclet tracking
src/coreclr/vm/exinfo.cppRemove saved funclet initialization
src/coreclr/vm/exceptionhandlingqcalls.hRemove QCall declarations for managed funclet calls
src/coreclr/vm/exceptionhandling.hRemove second pass funclet caller marker
src/coreclr/vm/exceptionhandling.cppAdd native second pass implementation, convert funclet callers to native
src/coreclr/vm/excep.cppAdd second pass calls and remove old intercept unwind code
src/coreclr/vm/corelib.hRemove managed unwind and intercept method definition
src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.csAdd conditional compilation for CoreCLR vs NativeAOT paths
src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/InternalCalls.csRemove managed funclet call declarations
src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/AsmOffsets.csUpdate structure sizes and add new ExInfo field offsets

Comment threadsrc/coreclr/vm/exceptionhandling.cpp
Comment threadsrc/coreclr/vm/exceptionhandling.cpp
@janvorli
janvorliforce-pushed the backport-move-eh-2nd-pass-to-native branch 3 times, most recently from b8ea280 to bf4fa55CompareOctober 1, 2025 16:22
There were some GC holes discovered caused by the fact that GC can be
triggered during 2nd pass of EH in-between calls to finally handlers and
catch handler. After considering options, moving the 2nd pass to native
code seems the most reasonable solution.
@janvorli
janvorliforce-pushed the backport-move-eh-2nd-pass-to-native branch from bf4fa55 to 7fc9c86CompareOctober 1, 2025 17:50
@janvorli

Copy link
Copy Markdown
MemberAuthor

The interpreter test is failing because it would need another PR from main to work correctly with this changed EH. The reason is that resume after catch in the .NET 10 branch expects that the RhThrowEx and the like are on the stack. I've simplified / fixed that in main.
@agocke, @jkotas I am going to disable the interpreter test in .NET 10 branch so that I don't need to pull in extra changes that are not needed for .NET 10. The change was not trivial.

@janvorli
janvorliforce-pushed the backport-move-eh-2nd-pass-to-native branch from c666bc0 to 288916dCompareOctober 1, 2025 21:46
@rbhandarbhanda added Servicing-approved Approved for servicing release and removed Servicing-consider Issue for next servicing release review labels Oct 2, 2025
@rbhandarbhanda added this to the 10.0.0 milestone Oct 2, 2025
@agocke
agocke merged commit 468465c into dotnet:release/10.0Oct 6, 2025
105 checks passed
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Nov 6, 2025
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-ExceptionHandling-coreclronly use for closed issuesServicing-approvedApproved for servicing release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@janvorli@jkotas@agocke@rbhanda
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

[release/10.0] Move coreclr EH second pass to native code - #120263

Merged
agocke merged 3 commits into
dotnet:release/10.0from
janvorli:backport-move-eh-2nd-pass-to-native
Oct 6, 2025
Merged

[release/10.0] Move coreclr EH second pass to native code#120263
agocke merged 3 commits into
dotnet:release/10.0from
janvorli:backport-move-eh-2nd-pass-to-native

Conversation

@janvorli

Copy link
Copy Markdown
Member

Backport of #119863 to release/10.0

Customer Impact

  • Customer reported
  • Found internally

There were some GC holes discovered caused by the fact that GC can be triggered during 2nd pass of EH in-between calls to finally handlers and catch handler. After considering options, moving the 2nd pass to native code seems the most reasonable solution.
The issue was reported by the RavenDB folks in #110769. It causes access violation crash on their system on Windows once in one or two days on average.

Regression

  • Yes
  • No

Introduced in .NET 8 by #88034 - the new exception handling, but got reported in .NET 9 since that's where the new EH was enabled by default.
It took almost 9 months to figure out the culprit due to the very narrow window in which it can occur and due to the fact that GC holes in general don't necessarily result in an observed bad behavior. That's why it was fixed this late in the release cycle.

Testing

  • Repro test created based on the customer code structure with instrumented coreclr where GC is injected at a specific problematic spot. It fails before the fix with a GC hole and passes with the fix.
  • Diagnostic tests
  • coreclr / libraries tests

Risk

Low, the change is 1:1 port of the 2nd pass of the EH from C# to C++ and then removal of a number of extra handling in the stack frame iterator and GC reference enumeration that was only necessary to support the cases when GC could have been happening in-between calls to finallys. That's no longer possible with the fix.

@janvorlijanvorli self-assigned this Sep 30, 2025
CopilotAI review requested due to automatic review settings September 30, 2025 17:14
@janvorlijanvorli added Servicing-consider Issue for next servicing release review area-ExceptionHandling-coreclr only use for closed issues labels Sep 30, 2025

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR moves the CoreCLR exception handling second pass from managed C# code to native C++ code to fix GC holes that occurred when the garbage collector was triggered between finally handler and catch handler calls during the second pass. This change addresses a customer-reported access violation crash in RavenDB that occurred intermittently due to these GC holes.

Key changes:

  • Remove managed second pass dispatch code and move logic to native implementation
  • Eliminate complex GC reference tracking infrastructure that was needed to handle GC during managed EH code
  • Simplify stack walking by removing special handling for managed exception handling frames

Reviewed Changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 3 comments.

Show a summary per file
FileDescription
src/coreclr/vm/stackwalk.hRemove GC reference tracking fields and methods for funclet handling
src/coreclr/vm/stackwalk.cppRemove complex GC reference tracking logic during stack walking
src/coreclr/vm/qcallentrypoints.cppRemove QCall entry points for managed funclet calls
src/coreclr/vm/metasig.hRemove method signature for managed unwind and intercept
src/coreclr/vm/gcenv.ee.common.cppRemove saved funclet info GC reference reporting
src/coreclr/vm/exinfo.hAdd new fields for catch handler info, remove saved funclet tracking
src/coreclr/vm/exinfo.cppRemove saved funclet initialization
src/coreclr/vm/exceptionhandlingqcalls.hRemove QCall declarations for managed funclet calls
src/coreclr/vm/exceptionhandling.hRemove second pass funclet caller marker
src/coreclr/vm/exceptionhandling.cppAdd native second pass implementation, convert funclet callers to native
src/coreclr/vm/excep.cppAdd second pass calls and remove old intercept unwind code
src/coreclr/vm/corelib.hRemove managed unwind and intercept method definition
src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.csAdd conditional compilation for CoreCLR vs NativeAOT paths
src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/InternalCalls.csRemove managed funclet call declarations
src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/AsmOffsets.csUpdate structure sizes and add new ExInfo field offsets

Comment threadsrc/coreclr/vm/exceptionhandling.cpp
Comment threadsrc/coreclr/vm/exceptionhandling.cpp
@janvorli
janvorliforce-pushed the backport-move-eh-2nd-pass-to-native branch 3 times, most recently from b8ea280 to bf4fa55CompareOctober 1, 2025 16:22
There were some GC holes discovered caused by the fact that GC can be
triggered during 2nd pass of EH in-between calls to finally handlers and
catch handler. After considering options, moving the 2nd pass to native
code seems the most reasonable solution.
@janvorli
janvorliforce-pushed the backport-move-eh-2nd-pass-to-native branch from bf4fa55 to 7fc9c86CompareOctober 1, 2025 17:50
@janvorli

Copy link
Copy Markdown
MemberAuthor

The interpreter test is failing because it would need another PR from main to work correctly with this changed EH. The reason is that resume after catch in the .NET 10 branch expects that the RhThrowEx and the like are on the stack. I've simplified / fixed that in main.
@agocke, @jkotas I am going to disable the interpreter test in .NET 10 branch so that I don't need to pull in extra changes that are not needed for .NET 10. The change was not trivial.

@janvorli
janvorliforce-pushed the backport-move-eh-2nd-pass-to-native branch from c666bc0 to 288916dCompareOctober 1, 2025 21:46
@rbhandarbhanda added Servicing-approved Approved for servicing release and removed Servicing-consider Issue for next servicing release review labels Oct 2, 2025
@rbhandarbhanda added this to the 10.0.0 milestone Oct 2, 2025
@agocke
agocke merged commit 468465c into dotnet:release/10.0Oct 6, 2025
105 checks passed
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Nov 6, 2025
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-ExceptionHandling-coreclronly use for closed issuesServicing-approvedApproved for servicing release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@janvorli@jkotas@agocke@rbhanda