Repository navigation
Mono: give EH clauses nested in an earlier-listed handler their own region - #135645
Closed
pcshrosbree wants to merge 1 commit into
Closed
pcshrosbree wants to merge 1 commit into
pcshrosbree wants to merge 1 commit into
Conversation
…egion ECMA-335 I.12.4.2.5 orders nested try blocks only. A clause nested in the handler of another clause may be listed after that clause. compute_bb_regions marked regions in table order and kept the first handler region a block received. The blocks of such a nested filter or catch handler therefore stayed in the enclosing handler's region. Runtime_135592 hits this. Its FilterInEarlierFinally scenario, with shipped Mono 8.0.28, fails with an access violation on windows-x64 and hangs on linux-arm64 and osx-arm64. In CI the test crashed the windows-x64 work item and hung the linux-arm64 one. Its CatchInEarlierFinally scenario makes the LLVM AOT compiler assert in OP_ENDFINALLY (mini-llvm.c, `lhs' not met). mono_find_block_region_notry, ip_get_finally_clause_index and the rethrow handler lookup assume the same order. Each returns the first matching clause. In mark_bb_in_region, let a strictly nested handler or filter region replace the enclosing one. In the three lookups, choose the innermost (shortest) enclosing handler. Handler and filter ranges are disjoint or nested, so the shortest one is the innermost. If nested clauses are listed first, every block gets the same region as before. In addition, the rethrow lookup now considers only catch and filter handlers, whose exception variable holds the exception to rethrow. It used to accept a fault handler, which has no exception variable: a rethrow in a fault nested in a catch crashed when the fault clause was listed first, and choosing the innermost handler would have made it crash in either order. It now rethrows the catch's exception in both orders.
|
Azure Pipelines: Successfully started running 4 pipeline(s). 12 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
Contributor
|
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch |
Member
|
We are not interested in fixing corner case bugs in Mono runtime. Mono runtime is going to be deleted in near future. |
This was referenced Oct 13, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #135641.
ECMA-335 I.12.4.2.5 orders nested try blocks only. A clause nested in the handler of another clause may be listed after that clause. Mono's JIT assumed it could not be:
compute_bb_regions(method-to-ir.c) marks regions in EH-table order, andmark_bb_in_regionkeeps the first handler region a block receives. When the enclosing clause is listed first, the blocks of the nested filter or catch handler stay in the enclosing handler's region.MONO_VERBOSE_METHOD=FilterInEarlierFinallyon main shows the nested filter (IL_0016) and its handler (IL_001b) in region0x112, the finally of clause 0.mono_find_block_region_notry(mini.c),ip_get_finally_clause_indexand theCEE_RETHROWhandler lookup (method-to-ir.c) return the first matching clause. The comment onip_get_finally_clause_indexsays "The clauses are ordered from inner to outer, so the first match is the innermost one".JIT/Regression/JitBlue/Runtime_135592, added in #135601, has valid IL in this shape, and Mono failed it in that PR's CI (build 1631268): the mono windows-x64 work item crashed with 0xC0000005, the mono linux-arm64 work item hung until the watchdog stopped it, and the llvmaot leg's AOT compile step failed with* Assertion at src/mono/mono/mini/mini-llvm.c:12710, condition 'lhs' not met(OP_ENDFINALLY, fromCatchInEarlierFinally).Change
mark_bb_in_region: when a block already belongs to a handler or filter region, a handler or filter region that is strictly nested in it replaces it. Otherwise the block keeps its region, as before.mono_find_block_region_notry,ip_get_finally_clause_indexand theCEE_RETHROWlookup: choose the innermost (shortest) enclosing handler or filter instead of the first match. Handler and filter ranges are disjoint or nested, so the shortest one is the innermost.CEE_RETHROWlookup also now considers only catch and filter handlers, whose exception variable holds the exception to rethrow. It used to accept a fault handler, which has no exception variable: arethrowin a fault nested in a catch crashed when the fault clause was listed first, and choosing the innermost handler alone would have made it crash in either order.When nested clauses are listed first, every block gets the same region as before. With the change, the nested filter and handler above get their own regions (
0x281and0x221).Validation
Own Mono builds of main
523a36f9e98and of this change on main29d644d27d0. The PR is on main0d21247817e; the three commits since523a36f9e98changesrc/coreclr/jit/helperexpansion.cpp, the cDAC andSystem.Net.NetworkInformation, and nothing undersrc/mono.Runtime_135592assembled from #135601's IL (at319d70bb424, before the Mono exclusion was added), run undercorerunfrom the MonoCore_Root, one process per run, 30 s timeout. "Whole test" runsMain; the scenarios are invoked one per process through a small reflection driver.FilterInEarlierFinally: 0xC0000005, 20 of 20 each; the other three scenarios pass, 20 of 20 eachlhsat mini-llvm.c:12710FilterInEarlierFinally: timeout, 20 of 20 each; the other three scenarios pass, 20 of 20 eachrethrowin a handler nested in a catch handler, both clause orders (an ad hoc probe, not part of this PR), JIT, 5 runs per cell on each of windows-x64, linux-x64 and osx-arm64:rethrowinEH-related tests (
JIT/Methodical/eh,JIT/Directed/ExcepFilters,JIT/Regression/Dev11,baseservices/exceptionsand twoJIT/Regression/JitBluetests; priority 1, standalone; 335 tests), main vs this PR:Test_HndIndex_10_Reorderednow compiles)ParallelCrashTester, which failed 3 of 10 JIT reruns on main and 2 of 10 with this PRRuns after LLVM AOT compilation allow JIT fallback, including for assemblies whose AOT compilation failed, so they do not show that every method ran AOT-compiled. On windows-x64, the test IL was built on linux-x64 and each test was run directly under
corerunand checked for its expected exit code, instead of through a generated Windows script.Tests that fail both before and after
Test_HndIndex_10_Plain,Test_HndIndex_10_Reordered,b473131_struct,OOMException01.rethrowinfinallyinsidecatch_d,rethrowinfinallyinsidecatch_r,Test_HndIndex_10_Plain,Test_HndIndex_10_Reordered.CSharpPart_132534,ParallelCrashTesterandunhandledTester.throwinfilter_d,throwinfilter_r,ParallelCrashTesterandunhandledTester.Test_HndIndex_10_Reorderedstays disabled on Mono. It now LLVM-AOT-compiles, but under JIT, under the interpreter and after LLVM AOT compilation, before and after this change, it still reports "The double ctor call worked without generating an exception". On main its AOT compilation fails, so that run used JIT fallback.The interpreter is not changed. Its
initialize_clause_bblocks(interp/transform.c) also keeps, for each IL offset, the first handler in table order. The fourRuntime_135592scenarios pass under the interpreter before and after, but in therethrowprobe above the interpreter rethrows the outer exception when the outer catch is listed first, and throwsNullReferenceExceptionfor the fault listed first, on main and with this PR.Runtime_135592is disabled on Mono in #135601 (ActiveIssuefor #135641 andMonoAotIncompatible). Once #135601 merges, that exclusion can be lifted, either here by rebasing onto it or in a follow-up.Shipped Mono
Mono 8.0.28 runtime packs, running the compiled
Runtime_135592through a self-contained reflection host, one process per run, 30 s timeout:FilterInEarlierFinallyNote
This PR was prepared with AI assistance (Anthropic Claude and OpenAI Codex), under my direction. I reviewed it before posting.