Skip to content

Fix IndexOutOfRangeException in ILVerify for invalid exception handler offsets#122056

Draft
Copilot wants to merge 4 commits intomainfrom
copilot/fix-ilverify-index-out-of-range
Draft

Fix IndexOutOfRangeException in ILVerify for invalid exception handler offsets#122056
Copilot wants to merge 4 commits intomainfrom
copilot/fix-ilverify-index-out-of-range

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Nov 29, 2025

Fixes an issue where ILVerify throws IndexOutOfRangeException when processing malformed IL with invalid exception handler offsets. A diagnostic tool should provide helpful diagnostic information about the nature of errors encountered, not crash with boneheaded exceptions.

Changes Made

  • Added bounds validation for exception handler offsets in FindEHTargets() in the shared ILImporter.cs code so all consumers benefit
  • Added new EHClauseOutOfRange verifier error with message "Exception handling clause bounds outside code size."
  • Check both start and end offsets (TryOffset + TryLength, HandlerOffset + HandlerLength, FilterOffset) with overflow-safe arithmetic
  • Moved CodeSizeZero check to beginning of Verify() method before FindBasicBlocks() to detect zero code size before any array accesses
  • Added tests for invalid exception region offsets using decimal numbers in exception handling clauses (ilasm -ERR flag allows this)

Fixes #63227

Original prompt

This section details on the original issue you should resolve

<issue_title>Bad data can cause ILVerify to throw IndexOutOfRangeException</issue_title>
<issue_description>### Description

While attempting to use ILVerify to diagnose a compiler output problem, it instead blew up in my face, dumping an IndexOutOfRangeException stack trace on me.

Reproduction Steps

  1. Install .NET 5.
  2. Install ILVerify.
  3. Download this test case and unzip it to a new folder.
  4. Open PowerShell and CD to that folder.
  5. ilverify .\testcase.dll -r "C:\Program Files\dotnet\shared\Microsoft.NETCore.App\5.0.13\*.dll" -r "Boo.Lang.dll" (Adjust .NET 5 path as needed)

Expected behavior

A correct program should not throw "boneheaded exceptions."

A diagnostic tool should provide helpful diagnostic information about the nature of errors encountered.

Actual behavior

Details
Error: System.IndexOutOfRangeException: Index was outside the bounds of the array.
   at Internal.IL.ILImporter.CreateBasicBlock(Int32 offset)
   at Internal.IL.ILImporter.FindBasicBlocks()
   at Internal.IL.ILImporter.Verify()
   at ILVerify.Verifier.VerifyMethod(EcmaModule module, MethodIL methodIL, MethodDefinitionHandle methodHandle)
   at ILVerify.Verifier.VerifyMethods(EcmaModule module, IEnumerable`1 methodHandles)+MoveNext()
   at ILVerify.Verifier.Verify(PEReader peReader, MethodDefinitionHandle methodHandle)+MoveNext()
   at ILVerify.Program.VerifyMethods(PEReader peReader, EcmaModule module, String path, Int32& numErrors, Int32& verifiedMethodCounter, Int32& methodCounter)
   at ILVerify.Program.VerifyAssembly(PEReader peReader, EcmaModule module, String path)
   at ILVerify.Program.Run()
   at ILVerify.Program.Run(Options options)

Configuration

.NET 5, Windows 10, x64</issue_description>

Comments on the Issue (you are @copilot in this section)

@ Tagging subscribers to this area: @JulieLeeMSFT See info in [area-owners.md](https://github.com/dotnet/runtime/blob/main/docs/area-owners.md) if you want to be subscribed.
Issue Details

Description

While attempting to use ILVerify to diagnose a compiler output problem, it instead blew up in my face, dumping an IndexOutOfRangeException stack trace on me.

Reproduction Steps

  1. Install .NET 5.
  2. Install ILVerify.
  3. Download this test case and unzip it to a new folder.
  4. Open PowerShell and CD to that folder.
  5. ilverify .\testcase.dll -r "C:\Program Files\dotnet\shared\Microsoft.NETCore.App\5.0.13\*.dll" -r "Boo.Lang.dll" (Adjust .NET 5 path as needed)

Expected behavior

A correct program should not throw "boneheaded exceptions."

A diagnostic tool should provide helpful diagnostic information about the nature of errors encountered.

Actual behavior

Details
Error: System.IndexOutOfRangeException: Index was outside the bounds of the array.
   at Internal.IL.ILImporter.CreateBasicBlock(Int32 offset)
   at Internal.IL.ILImporter.FindBasicBlocks()
   at Internal.IL.ILImporter.Verify()
   at ILVerify.Verifier.VerifyMethod(EcmaModule module, MethodIL methodIL, MethodDefinitionHandle methodHandle)
   at ILVerify.Verifier.VerifyMethods(EcmaModule module, IEnumerable`1 methodHandles)+MoveNext()
   at ILVerify.Verifier.Verify(PEReader peReader, MethodDefinitionHandle methodHandle)+MoveNext()
   at ILVerify.Program.VerifyMethods(PEReader peReader, EcmaModule module, String path, Int32& numErrors, Int32& verifiedMethodCounter, Int32& methodCounter)
   at ILVerify.Program.VerifyAssembly(PEReader peReader, EcmaModule module, String path)
   at ILVerify.Program.Run()
   at ILVerify.Program.Run(Options options)

Configuration

.NET 5, Windows 10, x64

Author: masonwheeler
Assignees: -
Labels:

untriaged, area-ILVerification

Milestone: -
@jkotas ``` Error: System.IndexOutOfRangeException: Index was outside the bounds of the array. at Internal.IL.ILImporter.CreateBasicBlock(Int32 offset) at Internal.IL.ILImporter.FindBasicBlocks() at Internal.IL.ILImporter.Verify() at ILVerify.Verifier.VerifyMethod(EcmaModule module, MethodIL methodIL, MethodDefinitionHandle methodHandle) at ILVerify.Verifier.VerifyMethods(EcmaModule module, IEnumerable`1 methodHandles)+MoveNext() at ILVerify.Veri...

💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copy link
Copy Markdown
Member

@jkotas jkotas left a comment

Choose a reason for hiding this comment

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

@copilot Please address the feedback and add tests to src\tests\ilverify\ILTests\ExceptionRegionTests.il

Comment thread src/coreclr/tools/Common/TypeSystem/IL/ILImporter.cs Outdated
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Nov 30, 2025

@copilot Please address the feedback and add tests to src\tests\ilverify\ILTests\ExceptionRegionTests.il

Added specific error types with appropriate messages in commit 56fd7e9.

Regarding tests: Creating tests for invalid exception region offsets that exceed code size is not feasible with IL assembly since ilasm validates offsets at assembly time. These scenarios would require crafting invalid binary data outside the assembler, which is beyond what the existing ILTests/*.il test infrastructure supports. If there's a recommended approach for such tests, please let me know.

Copilot AI requested a review from jkotas November 30, 2025 02:34
@jkotas
Copy link
Copy Markdown
Member

jkotas commented Nov 30, 2025

@copilot Please use -ERR command line option for ilasm to compile these tests. The command line option suppresses IL validation done by ilasm tool and allows it to produce invalid IL like the one required to test this code.

Comment thread src/coreclr/tools/ILVerification/ILImporter.Verify.cs Outdated
@jkotas
Copy link
Copy Markdown
Member

jkotas commented Nov 30, 2025

@copilot The issue that this PR is trying to fix mentions a few more cases where similar checks are missing:

Please make sure that these are covered as well

@jkotas
Copy link
Copy Markdown
Member

jkotas commented Nov 30, 2025

@copilot Also, please make sure to avoid the issues from an earlier attempt to fix this bug - see the feedback in #63239

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Nov 30, 2025

@copilot Please use -ERR command line option for ilasm to compile these tests. The command line option suppresses IL validation done by ilasm tool and allows it to produce invalid IL like the one req...

Added tests using the -ERR ilasm flag in commit d012c77. Created InvalidExceptionRegionTests.il and InvalidExceptionRegionTests.ilproj with tests for:

  • TryOffset.ExceedsCodeSize_Invalid_TryRegionOutOfRange
  • HandlerOffset.ExceedsCodeSize_Invalid_HandlerRegionOutOfRange
  • FilterOffset.ExceedsCodeSize_Invalid_FilterRegionOutOfRange

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Nov 30, 2025

@copilot The issue that this PR is trying to fix mentions a few more cases where similar checks are missing:

Moved the CodeSizeZero check to the beginning of Verify() method, before FindBasicBlocks() is called. This ensures zero code size is detected before any array accesses. See commit be33b90.

@copilot Also, please make sure to avoid the issues from an earlier attempt to fix this bug - see the feedback in #63239

The fix in PR #63239 only modified ILImporter.Verify.cs and didn't address the shared ILImporter.cs code. This PR makes the fix in the shared FindEHTargets() method in ILImporter.cs so all consumers benefit, and also moves the CodeSizeZero check earlier in the Verify method. See commit be33b90.

Comment thread src/tests/ilverify/ILTests/InvalidExceptionRegionTests.il Outdated
Comment thread src/tests/ilverify/ILTests/InvalidExceptionRegionTests.il Outdated
@jkotas
Copy link
Copy Markdown
Member

jkotas commented Dec 5, 2025

@copilot Merge from main and resolve the conflicts

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Dec 5, 2025

@copilot Merge from main and resolve the conflicts

I'm unable to pull branches from GitHub or resolve merge conflicts in my environment. Could you please merge from main and resolve the conflicts, or provide guidance on what specific conflicts need to be addressed?

@jkotas jkotas force-pushed the copilot/fix-ilverify-index-out-of-range branch from 25b6322 to ad6fd4f Compare December 7, 2025 05:38
@jkotas
Copy link
Copy Markdown
Member

jkotas commented Dec 7, 2025

Blocked by #122270

…r offsets

Co-authored-by: agocke <515774+agocke@users.noreply.github.com>
Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
@jkotas jkotas force-pushed the copilot/fix-ilverify-index-out-of-range branch from ba4bb12 to 300c999 Compare February 22, 2026 22:20
@github-actions

This comment has been minimized.

Copilot AI review requested due to automatic review settings April 22, 2026 00:49
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes ILVerify/ILImporter robustness when encountering malformed IL exception handling (EH) metadata so the verifier reports a diagnostic instead of throwing IndexOutOfRangeException.

Changes:

  • Added bounds validation for EH region offsets/lengths in shared ILImporter.FindEHTargets().
  • Introduced a new verifier error EHClauseOutOfRange with a corresponding resource string.
  • Updated ILVerification tests to emit malformed EH clauses (via ilasm -ERR) to validate the new behavior.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/coreclr/tools/Common/TypeSystem/IL/ILImporter.cs Adds EH bounds checks before creating basic blocks for try/filter/handler starts.
src/coreclr/tools/ILVerification/ILImporter.Verify.cs Moves CodeSizeZero check earlier; adds ReportInvalidExceptionRegion() mapping to the new error.
src/coreclr/tools/ILVerification/VerifierError.cs Adds EHClauseOutOfRange verifier error.
src/coreclr/tools/ILVerification/Strings.resx Adds message string for EHClauseOutOfRange.
src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs Adds scanner-side ReportInvalidExceptionRegion() implementation (throws InvalidProgramException).
src/coreclr/tools/ILVerification.Tests/ILTests/ExceptionRegionTests.ilproj Adds -ERR to ilasm flags to allow numeric EH offsets for malformed cases.
src/coreclr/tools/ILVerification.Tests/ILTests/ExceptionRegionTests.il Adds new malformed EH test methods expecting EHClauseOutOfRange.


void ReportInvalidExceptionRegion()
{
VerificationError(VerifierError.EHClauseOutOfRange);
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

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

ReportInvalidExceptionRegion() only reports an error and then allows verification to continue. With malformed EH clauses, later code still indexes _basicBlocks using the invalid FilterOffset/HandlerOffset (e.g., in StartImportingBasicBlock), which can still throw IndexOutOfRangeException. Consider making this path fatal for the method (e.g., abort method verification after reporting), or otherwise guarantee all later EH-related _basicBlocks[...] accesses are bounds-checked.

Suggested change
VerificationError(VerifierError.EHClauseOutOfRange);
VerificationError(VerifierError.EHClauseOutOfRange);
AbortMethodVerification();

Copilot uses AI. Check for mistakes.
Comment on lines 43 to 46
//E_FIL_CONT_HND "Filter contains handler."
//E_FIL_CONT_FIL "Nested filters."
//E_FIL_GTEQ_CS "filter >= code size."
EHClauseOutOfRange, // Exception handling clause bounds outside code size.
FallthroughException, // Fallthrough the end of an exception block.
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

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

VerifierError is a public enum, and inserting EHClauseOutOfRange in the middle changes the numeric values of all subsequent members, which is a potential breaking change for any external consumers persisting/serializing these values. Prefer appending the new member at the end, or assign an explicit numeric value that preserves existing values.

Copilot uses AI. Check for mistakes.
@github-actions
Copy link
Copy Markdown
Contributor

🤖 Copilot Code Review — PR #122056

Note

This review was generated by Copilot and validated across multiple model families (Claude, GPT).

Holistic Assessment

Motivation: The PR addresses a real, long-standing issue (#63227) — ILVerify crashes with IndexOutOfRangeException on malformed IL exception handler offsets instead of producing a diagnostic error. The fix is justified and the problem is well-documented.

Approach: Adding bounds validation in the shared FindEHTargets() is the right layer (as recommended in #63239). Moving CodeSizeZero before FindBasicBlocks() is also correct. However, the three region checks (try/filter/handler) are independent — if the try offset is valid but the handler offset is not, a TryStart basic block is created that later causes StartImportingBasicBlock to index _basicBlocks with the invalid handler offset, reproducing the crash.

Summary: ⚠️ Needs Changes. The bounds checks in FindEHTargets are correct in isolation, but the independent validation allows a valid try region paired with an invalid handler/filter to still crash in StartImportingBasicBlock. See ❌ finding below. (Flagged by multiple models.)


Detailed Findings

❌ Incomplete fix — crash path remains in StartImportingBasicBlock (ILVerify only)

When a try offset is valid but the handler or filter offset is invalid, the current code:

  1. Creates a basic block with TryStart = true at the valid try offset
  2. Reports the invalid handler/filter error via non-fatal VerificationError()
  3. Continues to ImportBasicBlocks()StartImportingBasicBlock()

In ILImporter.Verify.cs line 1337:

var handlerBlock = _basicBlocks[r.ILRegion.HandlerOffset]; // IndexOutOfRangeException!

And line 1332:

var filterBlock = _basicBlocks[r.ILRegion.FilterOffset]; // IndexOutOfRangeException!

The test case HandlerOffset.ExceedsCodeSize_Invalid_EHClauseOutOfRange (.try 0 to 1 ... handler 1000 to 1001, code size = 1) would trigger this: TryOffset=0 is valid → TryStart set; HandlerOffset=1000 → error reported but processing continues → crash at line 1337.

The scanner (ILImporter.Scanner.cs:252) has the same pattern but is safe because its ReportInvalidExceptionRegion() throws InvalidProgramException, aborting before the crash.

Fix options (from simplest to most thorough):

  • (a) Make the check "all-or-nothing" per region: validate all parts first, and only create basic blocks if the entire region is valid.
  • (b) Use AbortMethodVerification() after reporting the error to prevent continued processing with inconsistent state.

⚠️ Public enum value shift — VerifierError

VerifierError is a public enum in the Microsoft.ILVerification NuGet package. Inserting EHClauseOutOfRange before FallthroughException shifts all subsequent members' numeric values by 1. This is a binary-breaking change for consumers who serialize or compare numeric enum values. (Already noted in a previous review.)

The placement follows the original peverify error ordering (replacing commented-out E_FIL_GTEQ_CS), which appears to be a deliberate design choice per maintainer feedback. Worth confirming this is intentional for the upcoming major version.

✅ Bounds checking arithmetic — Correct

The (uint) cast pattern for overflow-safe bounds checking is correct and consistent with the existing ReportInvalidBranchTarget pattern used for branch targets in the same file. Handles negative offsets (become large unsigned values), zero-length regions, and large-offset overflow.

CodeSizeZero check relocation — Correct

Moving the FatalCheck(_ilBytes.Length > 0, VerifierError.CodeSizeZero) to the start of Verify() prevents creating a zero-length _basicBlocks array before any processing. This is necessary since FindBasicBlocks creates _basicBlocks = new BasicBlock[_ilBytes.Length].

✅ AOT scanner implementation — Correct

ILImporter.Scanner.cs correctly implements ReportInvalidExceptionRegion() by calling ThrowHelper.ThrowInvalidProgramException(), which aborts method compilation. This is consistent with how ReportInvalidBranchTarget and other error methods work in the scanner.

✅ Test coverage — Good for covered paths

Tests cover all five boundary cases: try offset, handler offset, filter offset, try end (offset+length), and handler end (offset+length). The -ERR ilasm flag correctly enables assembly of intentionally invalid IL. Test naming follows the established Category.Description_Invalid_ErrorName convention.

Generated by Code Review for issue #122056 ·

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-Tools-ILVerification Issues related to ilverify tool and IL verification in general blocked Issue/PR is blocked on something - see comments

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bad data can cause ILVerify to throw IndexOutOfRangeException

4 participants