Skip to content

Fix CordbReJitILCode reading small-format EH sections and tiny headers - #135920

Merged
max-charlamb merged 2 commits into
dotnet:mainfrom
pcshrosbree:fix/dbi-rejit-small-eh-clauses
Oct 29, 2026
Merged

max-charlamb merged 2 commits into
dotnet:mainfrom
pcshrosbree:fix/dbi-rejit-small-eh-clauses

Conversation

@pcshrosbree

@pcshrosbree pcshrosbree commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

TL;DR: ICorDebugILCode::GetEHClauses on ReJIT IL returned no clauses for a small-format EH section, and could return clauses for a tiny-header body, which has no EH section. This PR fixes CordbReJitILCode::Init in two commits that have to ship together. A live repro on linux-x64, windows-x64 and osx-arm64 and a harness that compiles the copied source give the expected results. I found no in-repo test harness for these APIs, so the PR adds no test; I am happy to add one if you would like.

CordbReJitILCode::Init mis-read ReJIT IL in two ways. ICorDebugILCode::GetEHClauses returned no clauses when the EH section used the small format. It could also return clauses for a tiny-header body, which has no EH section. The issue has the details. The change touches only CordbReJitILCode::Init and is split into two commits.

Fix CordbReJitILCode reading small EH sections. This commit changes only the small-format branch:

  • assign the m_cClauses member instead of declaring a local that shadows it;
  • divide DataSize - 4 by sizeof(IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_SMALL) (12) instead of sizeof(IMAGE_COR_ILMETHOD_SECT_SMALL) (2);
  • read the clauses from offsetof(IMAGE_COR_ILMETHOD_SECT_EH_SMALL, Clauses) (4), after the Reserved field, instead of from +2.

The loop that converts the clauses is unchanged; it already maps each field the same way the fat branch does. A DataSize below 4 is still rejected with CORDBG_E_TARGET_INCONSISTENT, because the unsigned subtraction wraps and the existing > 10000 check catches it.

Skip EH sections after a tiny ReJIT IL header. CorILMethod_MoreSects was tested against the tiny header byte, where that bit belongs to the code size. A tiny header has no sections (COR_ILMETHOD_TINY::GetSect), so the check now applies only to fat headers. This commit has to ship with the first. Before the first commit, the small branch never returned clauses, so a tiny body followed by bytes shaped like a small EH section showed nothing. With the first commit alone, such a body reports a bogus clause; I measured that below.

I kept direct struct reads, the same way the fat branch reads. The COR_ILMETHOD_SECT_EH helpers in corhlpr.h work on an in-process pointer, so using them here would mean copying the whole section out of the target first.

Testing

I did not find an in-repo test harness for these ICorDebug ReJIT APIs, so this PR does not add a test. If you would like one, I am happy to add it in whatever form you prefer. I validated the change in two ways.

A live repro with a small app, a profiler that ReJITs one method, and an ICorDebug client. The issue has the details. Each mscordbi was paired with its own DAC and runtime. The "before" builds are of main; the "after" builds are of this branch. All 40 runs, across the shipped and main runtimes, completed. For the first two rows, the counts were the same through GetActiveReJitRequestILCode and through GetCodeEx(ILCODE_REJIT_IL) on a frame inside the method being ReJITted. The tiny-body rows were measured through GetActiveReJitRequestILCode only, because no frame of the tiny method exists.

ReJIT body Expected Shipped 10.0.12 (linux-x64, windows-x64, osx-arm64) main before (all three) First commit only (linux-x64) This PR (all three)
original IL, small EH section 1 0 0 1 1
same clause, fat EH section 1 1 1 1 1
tiny body + bytes shaped like a small EH section 0 0 0 1 0
tiny body + bytes shaped like a fat EH section 0 1 1 1 0

Whenever a run reports the expected clause, its fields match the IL: try offset 0 and length 54, handler offset 54 and length 5, class token 0x01000013. Correctly handled tiny bodies report no clauses.

A harness that runs the source itself. It compiles Init and GetEHClauses copied verbatim from module.cpp, together with the real corhdr.h, against a stub process that serves synthetic IL bodies. It has 18 cases, covering:

  • 1, 2 and 20 small clauses, and a filter clause;
  • padding before the section;
  • a DataSize that does not hold a whole number of clauses, and DataSize 0 and 3;
  • fat-format EH sections as controls;
  • tiny bodies.
Source Cases failing (of 18)
main 9
first commit only 2 (both tiny-header cases)
this PR 0

I also put each of the four defects (the shadowing local, the wrong divisor, the wrong clause offset and the tiny-header check) back into the fixed code one at a time. The harness catches each one on its own.

The fixed mscordbi builds on linux-x64, osx-arm64 and windows-x64; those builds are the ones used in the live runs above.

Resolves #135919

Note

This PR was prepared with AI assistance (Anthropic Claude and OpenAI Codex), under my direction. I reviewed it before posting.

In the small-format branch of CordbReJitILCode::Init, `ULONG32 m_cClauses`
declared a local that shadowed the member. The member stayed 0, so
ICorDebugILCode::GetEHClauses reported no clauses for any ReJIT IL body
whose EH section uses the small format.

The same branch divided DataSize - 4 by the 2-byte section header size,
sizeof(IMAGE_COR_ILMETHOD_SECT_SMALL), instead of by the 12-byte clause
size. It also started reading clauses 2 bytes into the section instead
of 4 bytes in, after the Reserved field. It therefore read about six
times too much target memory, from the wrong offset, and failed Init
when that memory was not readable.

Assign the member, divide by sizeof(IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_SMALL)
and read the clauses from offsetof(IMAGE_COR_ILMETHOD_SECT_EH_SMALL, Clauses).
The fat-format branch is unchanged.
A tiny method header has no section flags: its upper six bits are the
code size. CordbReJitILCode::Init tested CorILMethod_MoreSects (0x08)
against the tiny header byte, so a tiny body whose code size has bit 1
set (2, 3, 6, 7, ...) was treated as having an extra section. The
debugger then read the byte at the next 4-byte-aligned address after
the code and, if it looked like an EH section kind, parsed whatever
followed as EH clauses; if it was not readable, Init failed.

Only consult CorILMethod_MoreSects for a fat header, matching
COR_ILMETHOD_TINY::GetSect in corhlpr.h, which returns no sections.
@dotnet-policy-service dotnet-policy-service Bot added the community-contribution Indicates that the PR has been added by a community member label Oct 16, 2026
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke
See info in area-owners.md if you want to be subscribed.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @steveisok, @tommcdon, @dotnet/dotnet-diag
See info in area-owners.md if you want to be subscribed.

@max-charlamb max-charlamb left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

thanks!

@max-charlamb

Copy link
Copy Markdown
Member

/ba-g build monitor helix failing

@max-charlamb
max-charlamb merged commit e111d4c into dotnet:main Oct 29, 2026
121 of 123 checks passed
@pcshrosbree

Copy link
Copy Markdown
Contributor Author

Thank you for the review and merge!

@pcshrosbree
pcshrosbree deleted the fix/dbi-rejit-small-eh-clauses branch October 29, 2026 22:00
@dotnet-milestone-bot dotnet-milestone-bot Bot added this to the 12.0-preview1 milestone Oct 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-Diagnostics-coreclr community-contribution Indicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ICorDebugILCode::GetEHClauses returns no clauses for a ReJIT IL body with a small-format EH section

3 participants