Skip to content

Bound WebCilFile section access against mapped-view length#3842

Open
christophwille wants to merge 1 commit into
masterfrom
christophwille/98
Open

Bound WebCilFile section access against mapped-view length#3842
christophwille wants to merge 1 commit into
masterfrom
christophwille/98

Conversation

@christophwille

Copy link
Copy Markdown
Member

Problem

WebCilFile (the loader for WebCIL / .wasm modules) resolves RVAs by building raw native pointers into the memory-mapped view directly from section-header fields read out of the module's metadata. Unlike PEFile.GetSectionData, which delegates to the bounds-checked System.Reflection.Metadata.PEReader, this hand-rolled path validated nothing.

A crafted section header (for example VirtualAddress=0, VirtualSize=0xFFFFFFFF, RawDataPtr=0xFFFFFFFF, RawDataSize=0x7FFFFFFF) combined with an in-range RVA makes GetSectionData return a SectionData (and therefore a BlobReader) pointing far outside the mapped view: an out-of-bounds read (CWE-125) that can surface as an access violation or as information disclosure. It is reachable during ordinary decompilation, since method-body resolution (GetMethodBody -> GetSectionData(rva).GetReader()) and field-data resolution feed it RVAs taken straight from the metadata. The (int)RawDataSize narrowing cast could additionally produce a negative length.

The same file had related robustness gaps: a crafted or truncated module could drive the structural reads (the Wasm section loop, the data-segment loop, the WebCIL header, and RVA translation feeding Seek/ReadInt32) past the end of the view, throwing an uncaught EndOfStreamException / OverflowException / BadImageFormatException out of the loader.

Fix

GetSectionData now resolves the raw-data range through a single helper that bounds-checks it against the mapped-view length before constructing SectionData. All of the arithmetic is widened to long so crafted uint header fields cannot wrap the range check or narrow into a length that looks valid, and the length is rejected if it does not fit in int. The range checks in TranslateRVA and GetContainingSectionIndex are widened the same way. FromFile now reports a crafted or truncated module as "not a WebCIL file" (returns null, the loader's existing contract) instead of letting those parsing exceptions escape.

Tests

A new WebCilFileTests fixture covers the bounds helper directly (the exploit shape, an oversized RawDataSize, a raw-data range running past the view, an RVA in no section, and a valid section that still resolves correctly) and exercises FromFile end-to-end against crafted Wasm containers (truncated section-header table; a CLI-header RVA that falls in no section), asserting it returns null rather than throwing.

🤖 Generated with Claude Code

Copilot AI 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 hardens WebCilFile’s section/RVA handling to prevent out-of-bounds reads from crafted WebCIL/Wasm inputs, aligning the loader’s behavior more closely with the bounds-checked PE path and ensuring malformed inputs are rejected safely.

Changes:

  • Adds mapped-view-length-aware bounds checking for section raw-data access and widens arithmetic to long to avoid overflow/narrowing pitfalls.
  • Broadens structural robustness by catching key parsing exceptions in FromFile and returning null (the loader contract) rather than throwing.
  • Introduces a new WebCilFileTests fixture covering the bounds helper and end-to-end “return null, don’t throw” behavior for crafted containers.

Reviewed changes

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

File Description
ICSharpCode.Decompiler/Metadata/WebCilFile.cs Adds view-length bounds checking and exception-to-null behavior for malformed/truncated WebCIL/Wasm inputs.
ICSharpCode.Decompiler.Tests/Metadata/WebCilFileTests.cs Adds targeted unit tests for the new bounds helper and FromFile robustness on crafted inputs.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread ICSharpCode.Decompiler/Metadata/WebCilFile.cs Outdated
Comment thread ICSharpCode.Decompiler/Metadata/WebCilFile.cs Outdated
Comment thread ICSharpCode.Decompiler.Tests/Metadata/WebCilFileTests.cs Outdated
WebCilFile builds raw native pointers into the memory-mapped view directly
from section-header fields read out of attacker-controlled metadata. Unlike
PEFile.GetSectionData, which delegates to the bounds-checked PEReader, this
hand-rolled path validated nothing: a crafted section header could produce a
SectionData (and hence a BlobReader) pointing far outside the view, an
out-of-bounds read reachable on normal decompilation through method-body and
field-data RVA resolution. The (int)RawDataSize narrowing cast could also
yield a negative length.

Resolve and bounds-check the raw-data range against the view length before
constructing SectionData, widening the arithmetic to long so crafted uint
fields cannot wrap the range check or narrow into an apparently valid length.
Structural parsing in FromFile now reports a crafted or truncated module as
"not a WebCIL file" (null) rather than letting EndOfStreamException,
OverflowException or BadImageFormatException escape the loader.

Assisted-by: Claude:claude-opus-4-8:Claude Code
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.

2 participants