[PR #1595] [CLOSED] fix(dotnet): map managed RVAs through section virtual size #2994

Closed
opened 2026-10-17 21:12:39 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/1595
Author: @Adolanium
Created: 10/10/2026
Status: ❌ Closed

Base: main ← Head: fix/pe-rva-virtual-size


📝 Commits (1)

  • f94ffcd fix(dotnet): map managed RVAs through section virtual size

📊 Changes

3 files changed (+55 additions, -8 deletions)

View changed files

📝 src/dotnet/ManagedMemberInspector.test.ts (+40 -0)
📝 src/dotnet/ManagedPe.fixture.ts (+4 -1)
📝 src/dotnet/ManagedPeReader.ts (+11 -7)

📄 Description

Summary

Fixes #1596

Managed PE mapping treated each section as max(VirtualSize, SizeOfRawData) bytes and kept the first match. An RVA in the raw padding past VirtualSize was read as file-backed method bytes. A method there could decode with status present and a normalized CIL hash.

Problem and expected behavior

mapRva used mappedSize = Math.max(section.virtualSize, section.rawSize). For section virtual address 0x2000 and raw offset 0x200, RVA 0x2010 in that padding maps to file offset 0x210. A later section at virtual address 0x3000 can lose to the earlier section and map to file offset 0x1200 when the earlier raw size reaches that far.

rvaAvailableBytes then returned the remainder of SizeOfRawData. methodBody accepts that extent. When the CIL decodes, the body status is present and normalized_il_sha256 is set. docs/managed-code-analysis.md says that hash exists only when the entire CIL stream decodes with status present.

A section addresses [virtualAddress, virtualAddress + virtualSize). Bytes past VirtualSize are file-alignment padding. An RVA inside VirtualSize and past SizeOfRawData still fails because those virtual bytes have no file data. VirtualSize 0 addresses nothing.

Change and scope

mapRva selects a section only when the requested range sits inside VirtualSize, then requires the same range to sit in SizeOfRawData. rvaAvailableBytes caps the returned length at the remaining virtual size and the remaining raw size. The fixture can set virtualSectionSize so a test can place the method at the first byte outside VirtualSize while the raw bytes still contain the body. Default fixtures still use at least one page and at least the raw size.

Contract and boundary impact

  • Semantic owner and earliest changed stage: managed PE layout, at RVA mapping, before metadata and CIL decoding.
  • CLI and MCP/tool-catalog contract: inspect_managed_members can report a method body as malformed with normalized_il_sha256: null, and coverage partial, when the method RVA is outside VirtualSize. No new fields.
  • Provider, bridge, target-format, or platform compatibility: PE32 and PE32+ on every supported platform. Images whose RVAs already sit inside VirtualSize keep the same mapping.
  • Evidence, artifact, provenance, or reconstruction contract: status present stays limited to CIL that was read from the section's virtual range and its file bytes. The artifact bytes are unchanged.
  • Process execution, authorization, cleanup, or containment impact: none.
  • Generated metadata (docs/public/product-catalog.json), package, or installation impact: none.

Evidence and regression coverage

  • Tests added or updated: ManagedMemberInspector.test.ts builds the fixture with virtualSectionSize: 0x800. The method stays at RVA 0x2800 and file offset 0x0a00, which is the first byte outside the section. The body is malformed, normalized_il_sha256 is null, and coverage is partial. A second case sets VirtualSize to 0 and expects the CLI directory to be unmapped. Existing fixture tests still expect status present.
  • Base reproduction or other evidence: on revision 02ef2b1c, mapRva uses Math.max(virtualSize, rawSize), so both new fixtures map through the raw bytes. The tests assert the unmapped outcome and fail on that revision.
  • User-visible CLI/MCP output (if applicable): method status malformed, normalized_il_sha256 null, coverage state partial, issue method.body RVA is not covered by a PE section.
  • Remaining proof gaps: the fixture is synthetic. This run did not load a third-party assembly in Ghidra or a Windows loader.

For evidence-bearing changes:

  • Observed, derived, and inferred claims remain distinguishable.
  • Artifact identity, source provenance, and failed attempts remain preserved.
  • Unsupported, incomplete, unavailable, or uncertain outcomes remain visible.

Validation performed

  • npm run test:focused -- src/dotnet/ManagedArtifactFailures.test.ts src/dotnet/ManagedArtifactInspector.test.ts src/dotnet/ManagedCilDecoding.test.ts src/dotnet/ManagedMemberInspector.test.ts src/dotnet/ManagedMemberInspectorCore.test.ts src/dotnet/ManagedMetadataHeaps.test.ts src/dotnet/ManagedNativeBoundaryInspector.test.ts — 7 files, 87 passed.
  • npx tsc --noEmit --pretty false — exit 0.
  • npx oxlint on the three changed files — 0 errors. Three warnings remain in ManagedPe.fixture.ts on lines this change does not touch.
  • git push pre-push npm run check:fast — typecheck and lint, 2 successful.

Compatibility, safety, and release

  • Breaking changes or migration steps: none. Callers inspecting a PE whose method RVA sits in raw padding past VirtualSize now receive a malformed body and partial coverage. Conformant images keep status present.
  • Real Hopper/Ghidra, browser, or OS coverage: none required. The mapping is local PE parsing.
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: none. The reader still does not execute target code. It stops treating file-alignment padding as CIL.

Review checklist

  • This PR addresses a concrete problem or an agreed enhancement.
  • The PR has one focused outcome and the title follows type(scope): outcome.
  • Related issue is linked, or the reason for not linking one is stated above.
  • Tests cover changed observable behavior and meaningful failure paths.
  • Owning docs, contracts, and generated metadata are updated where needed.
  • User-visible CLI/MCP changes include representative output.
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/1595 **Author:** [@Adolanium](https://github.com/Adolanium) **Created:** 10/10/2026 **Status:** ❌ Closed **Base:** `main` ← **Head:** `fix/pe-rva-virtual-size` --- ### 📝 Commits (1) - [`f94ffcd`](https://github.com/morluto/rea/commit/f94ffcdb208d6e1d8084418c49bb4fe4ff2ad6b5) fix(dotnet): map managed RVAs through section virtual size ### 📊 Changes **3 files changed** (+55 additions, -8 deletions) <details> <summary>View changed files</summary> 📝 `src/dotnet/ManagedMemberInspector.test.ts` (+40 -0) 📝 `src/dotnet/ManagedPe.fixture.ts` (+4 -1) 📝 `src/dotnet/ManagedPeReader.ts` (+11 -7) </details> ### 📄 Description ## Summary Fixes #1596 Managed PE mapping treated each section as `max(VirtualSize, SizeOfRawData)` bytes and kept the first match. An RVA in the raw padding past `VirtualSize` was read as file-backed method bytes. A method there could decode with status `present` and a normalized CIL hash. ## Problem and expected behavior `mapRva` used `mappedSize = Math.max(section.virtualSize, section.rawSize)`. For section virtual address `0x2000` and raw offset `0x200`, RVA `0x2010` in that padding maps to file offset `0x210`. A later section at virtual address `0x3000` can lose to the earlier section and map to file offset `0x1200` when the earlier raw size reaches that far. `rvaAvailableBytes` then returned the remainder of `SizeOfRawData`. `methodBody` accepts that extent. When the CIL decodes, the body status is `present` and `normalized_il_sha256` is set. `docs/managed-code-analysis.md` says that hash exists only when the entire CIL stream decodes with status `present`. A section addresses `[virtualAddress, virtualAddress + virtualSize)`. Bytes past `VirtualSize` are file-alignment padding. An RVA inside `VirtualSize` and past `SizeOfRawData` still fails because those virtual bytes have no file data. `VirtualSize` 0 addresses nothing. ## Change and scope `mapRva` selects a section only when the requested range sits inside `VirtualSize`, then requires the same range to sit in `SizeOfRawData`. `rvaAvailableBytes` caps the returned length at the remaining virtual size and the remaining raw size. The fixture can set `virtualSectionSize` so a test can place the method at the first byte outside `VirtualSize` while the raw bytes still contain the body. Default fixtures still use at least one page and at least the raw size. ## Contract and boundary impact - Semantic owner and earliest changed stage: managed PE layout, at RVA mapping, before metadata and CIL decoding. - CLI and MCP/tool-catalog contract: `inspect_managed_members` can report a method body as `malformed` with `normalized_il_sha256: null`, and coverage `partial`, when the method RVA is outside `VirtualSize`. No new fields. - Provider, bridge, target-format, or platform compatibility: PE32 and PE32+ on every supported platform. Images whose RVAs already sit inside `VirtualSize` keep the same mapping. - Evidence, artifact, provenance, or reconstruction contract: status `present` stays limited to CIL that was read from the section's virtual range and its file bytes. The artifact bytes are unchanged. - Process execution, authorization, cleanup, or containment impact: none. - Generated metadata (`docs/public/product-catalog.json`), package, or installation impact: none. ## Evidence and regression coverage - Tests added or updated: `ManagedMemberInspector.test.ts` builds the fixture with `virtualSectionSize: 0x800`. The method stays at RVA `0x2800` and file offset `0x0a00`, which is the first byte outside the section. The body is `malformed`, `normalized_il_sha256` is null, and coverage is `partial`. A second case sets `VirtualSize` to 0 and expects the CLI directory to be unmapped. Existing fixture tests still expect status `present`. - Base reproduction or other evidence: on revision `02ef2b1c`, `mapRva` uses `Math.max(virtualSize, rawSize)`, so both new fixtures map through the raw bytes. The tests assert the unmapped outcome and fail on that revision. - User-visible CLI/MCP output (if applicable): method `status` `malformed`, `normalized_il_sha256` null, coverage `state` `partial`, issue `method.body RVA is not covered by a PE section`. - Remaining proof gaps: the fixture is synthetic. This run did not load a third-party assembly in Ghidra or a Windows loader. For evidence-bearing changes: - [x] Observed, derived, and inferred claims remain distinguishable. - [x] Artifact identity, source provenance, and failed attempts remain preserved. - [x] Unsupported, incomplete, unavailable, or uncertain outcomes remain visible. ## Validation performed - `npm run test:focused -- src/dotnet/ManagedArtifactFailures.test.ts src/dotnet/ManagedArtifactInspector.test.ts src/dotnet/ManagedCilDecoding.test.ts src/dotnet/ManagedMemberInspector.test.ts src/dotnet/ManagedMemberInspectorCore.test.ts src/dotnet/ManagedMetadataHeaps.test.ts src/dotnet/ManagedNativeBoundaryInspector.test.ts` — 7 files, 87 passed. - `npx tsc --noEmit --pretty false` — exit 0. - `npx oxlint` on the three changed files — 0 errors. Three warnings remain in `ManagedPe.fixture.ts` on lines this change does not touch. - `git push` pre-push `npm run check:fast` — typecheck and lint, 2 successful. ## Compatibility, safety, and release - Breaking changes or migration steps: none. Callers inspecting a PE whose method RVA sits in raw padding past `VirtualSize` now receive a malformed body and partial coverage. Conformant images keep status `present`. - Real Hopper/Ghidra, browser, or OS coverage: none required. The mapping is local PE parsing. - Package or release metadata impact: none. - Security, privacy, process, or containment review: none. The reader still does not execute target code. It stops treating file-alignment padding as CIL. ## Review checklist - [x] This PR addresses a concrete problem or an agreed enhancement. - [x] The PR has one focused outcome and the title follows `type(scope): outcome`. - [x] Related issue is linked, or the reason for not linking one is stated above. - [x] Tests cover changed observable behavior and meaningful failure paths. - [x] Owning docs, contracts, and generated metadata are updated where needed. - [x] User-visible CLI/MCP changes include representative output. - [x] I checked the final diff for secrets, unrelated cleanup, and unsupported claims. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-17 21:12:39 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#2994
No description provided.