Skip to content

Fix crash decompiling reference assemblies with stripped method bodies - #3958

Merged
siegfriedpammer merged 1 commit into
masterfrom
fix-refasm-accessor-oob
Aug 7, 2026
Merged

siegfriedpammer merged 1 commit into
masterfrom
fix-refasm-accessor-oob

Conversation

@siegfriedpammer

Copy link
Copy Markdown
Member

Problem

CSharpDecompiler.IsAccessorInterfaceImplementationRuntimeHelper bounds the IL body of a candidate accessor from above, but never from below. Reference assemblies keep the method RVA while stripping the body to zero bytes, so an explicit interface accessor there passes the size check and the very first opcode read runs off the end of the blob. The BadImageFormatException escapes MemberIsHidden and aborts decompilation of the entire type.

Repro with any Microsoft.NETFramework.ReferenceAssemblies pack:

ilspycmd .../microsoft.netframework.referenceassemblies.net48/1.0.3/build/.NETFramework/v4.8/System.Web.dll -t System.Web.HttpApplication

---> System.BadImageFormatException: Read out of bounds.
   at ICSharpCode.Decompiler.Disassembler.ILParser.DecodeOpCode(BlobReader& blob)
   at ICSharpCode.Decompiler.CSharp.CSharpDecompiler.IsAccessorInterfaceImplementationRuntimeHelper(...)
   at ICSharpCode.Decompiler.CSharp.CSharpDecompiler.MemberIsHidden(...)

Fix

A minimumMethodSize counterpart to the existing maximumMethodSize, plus three guards the bound cannot cover on its own: minimumMethodSize assumes the one-byte ldarg.N encoding, but ldarg encodes as four bytes, so a body that passes the bound can still be consumed by the argument loop before the trailing call/ret.

The sibling fixed-sequence matcher, TransformDisplayClassUsage.IsCallToObjectCtor, already guards its lower bound (reader.Length < 7) and catches BadImageFormatException; this one did not. I audited every DecodeOpCode call site: all others are while (blob.RemainingBytes > 0) scanners whose opcode read is guarded by the loop condition, so no change is needed there and no bounds-checking overhead is added to normal IL blobs.

Testing

  • New ILPretty fixture TruncatedAccessorBody, red before the fix with the same stack trace, green after. ilasm cannot emit a zero-byte body, so the fixture uses the truncated-tail variant of the same defect.
  • System.Web, System.Web.Extensions, mscorlib, System and System.Core from the net48 reference pack now decompile in full with zero errors.
  • Full ILSpy.XPlat.slnf suite: 3367 tests, 0 failed, 44 skipped (Windows-only).

How it was found

Fuzzing the nuget.org catalog through the decompiler. Every package whose dependency closure resolved to the Microsoft.NETFramework.ReferenceAssemblies packs hit this.


Written by an AI agent (Claude) on Siegfried's behalf.

The accessor forwarding-stub matcher bounded the IL body from above but not
from below. Reference assemblies keep the method RVA while stripping the body
to zero bytes, so an explicit interface accessor there sailed past the size
check and the first opcode read ran off the end of the blob, aborting the
whole type with a BadImageFormatException. The sibling matcher in
TransformDisplayClassUsage already guards its lower bound; this one did not.

Found by fuzzing nuget.org: every package resolving to the
Microsoft.NETFramework.ReferenceAssemblies packs was affected.

Assisted-by: Claude:claude-opus-5[1m]:Claude Code
@siegfriedpammer
siegfriedpammer merged commit d9c60f0 into master Aug 7, 2026
9 of 10 checks passed
@siegfriedpammer
siegfriedpammer deleted the fix-refasm-accessor-oob branch August 7, 2026 04:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant