[PR #2639] [CLOSED] fix(native): retain partial metadata when a protocol-list slot is unmapped #5300

Closed
opened 2026-10-31 01:05:33 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/2639
Author: @sx4im
Created: 10/13/2026
Status: ❌ Closed

Base: main ← Head: fix/2610-native-protocol-list-unmapped-partial


📝 Commits (10+)

  • c0b7781 fix(native): retain partial metadata when a protocol-list slot is unmapped
  • 9a80fb3 fix(native): bound failed class-list slots by the record budget
  • 794b734 fix(native): admit each failed objc list slot exactly once
  • 7b9b666 refactor(native): own failed Objective-C slot accounting once
  • 31137e5 fix(native): bound every Objective-C list slot attempt
  • 22b76ee fix(native): scope Objective-C list scan truncation
  • 92f72f4 fix(native): retain class-dependent scan coverage limits
  • 40e0de4 fix(native): give each Objective-C list scan its own slot bound
  • 05f9b3a fix(native): retain unresolved pointer coverage reasons
  • 98f985b refactor(test): split Apple dispatch budget cases

📊 Changes

5 files changed (+334 additions, -28 deletions)

View changed files

📝 src/native/AppleDispatchCoverage.ts (+19 -14)
📝 src/native/AppleDispatchMetadata.test.ts (+215 -0)
📝 src/native/AppleDispatchMetadata.ts (+18 -9)
📝 src/native/AppleObjcDispatchFacets.ts (+78 -5)
📝 src/native/AppleSwiftVtables.test.ts (+4 -0)

📄 Description

Summary

Fixes #2610.

A Mach-O image with valid Objective-C class metadata and an unmapped __objc_protolist slot failed its entire static dispatch-metadata decode. The class-list loop already records per-slot mapping failures into partial coverage, but the protocol-list loop read its slot pointer unguarded, so the same unmapped pointer aborted the whole decode before protocolReader.record could handle the record. Now the protocol-list slot uses the same guard as the class-list slot: the failure is recorded, the objc_class_method_ivar_metadata facet coverage becomes partial with a reason naming the mapping failure, and the independently decodable class is retained.

Problem and expected behavior

Smallest trigger: build the 4096-byte fixture from the issue with the protocol-list section address pointing at an unmapped page (base + 0x9000). The decode throws RangeError: Unmapped or ambiguous metadata pointer 0x100009000 out of decodeObjcDispatchFacets at the protolist loop (AppleObjcDispatchFacets.ts:203). Expected: partial coverage with a reason identifying the mapping failure while RetainedClass and its coverage records still reach the caller, matching the existing behavior for an unmapped class-list slot.

Change and scope

Wrapped the per-slot pointer read in the __objc_protolist loop in the same try/catch shape the __objc_classlist loop already uses in the same function: the field address is hoisted, the failure is pushed to failures as <field>: <message>, and the loop continues. pushDispatchCoverage already turns those failure strings into partial facet coverage with a reason, so no other code changes were needed. Added three regression tests in src/native/AppleDispatchMetadata.test.ts using the fixture from the issue verbatim (controls neither, class, protocol).

Scope decision: not applicable. This is a routine fix inside the existing decode boundary; no new providers, no target execution, no new scope.

Contract and boundary impact

  • Semantic owner and earliest changed stage: Apple dispatch metadata decode (decodeObjcDispatchFacets, src/native/AppleObjcDispatchFacets.ts).
  • Contribution scope fit and linked maintainer decision: routine bug fix within existing boundaries; no scope expansion.
  • CLI and MCP/tool-catalog contract: none; no tool or contract changes.
  • Provider, bridge, target-format, or platform compatibility: Mach-O arm64 static decode only; fully mapped images decode exactly as before.
  • Evidence, artifact, provenance, or reconstruction contract: the coverage reason now names the unmapped protocol slot instead of the whole decode failing with no result.
  • Target selection, authority, upstream behavior, and cleanup details: not applicable.
  • Generated metadata, package, or installation impact: none.

Expected effects:

  • Read existing files or captured data.
  • Launch or attach to a process.
  • Inject instrumentation or run scripts/code.
  • Change process memory, registers, files, or configuration.
  • Contact network services.
  • Recover protected code or work around protections.
  • Documentation or usability only; no target interaction.
  • Not sure yet; investigation or maintainer guidance needed.

Evidence and regression coverage

  • Tests added or updated: 3 tests in src/native/AppleDispatchMetadata.test.ts ("Apple dispatch unmapped protocol-list slot"), fixture builder taken verbatim from the issue.
  • Base reproduction or other evidence: on the base revision the bad-protocol control threw RangeError: Unmapped or ambiguous metadata pointer 0x100009000 (new test failed red); after the fix the same control returns RetainedClass with the facet coverage partial and the reason containing 0x100009000. The neither control still returns the class with complete coverage, and the class control still yields partial coverage as before.
  • User-visible CLI/MCP output: not applicable.
  • Remaining proof gaps: the issue's NativeMacOSProvider provider-level repro was not exercised; the decoder test covers the root cause directly and the provider path only wraps this decoder.

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:local -- src/native/AppleDispatchMetadata.test.ts - 25/25 passed (22 existing + 3 new; the protocol control failed before the fix, passed after)
  • npm run test:local -- src/native/AppleSwiftVtables.test.ts - 3/3 passed (only other test file importing this code path)
  • npm run check:fast - typecheck + lint green
  • npm run verify:test-discovery - all 1024 test files discovered exactly once

Compatibility, safety, and release

  • Breaking changes or migration steps: none.
  • Real Hopper/Ghidra, browser, or OS coverage: none; source tests exercise the Mach-O decode path directly.
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: none; decode-only change, no new execution paths.

Before merge

  • This PR addresses a concrete problem or an agreed enhancement.
  • Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff.
  • 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/2639 **Author:** [@sx4im](https://github.com/sx4im) **Created:** 10/13/2026 **Status:** ❌ Closed **Base:** `main` ← **Head:** `fix/2610-native-protocol-list-unmapped-partial` --- ### 📝 Commits (10+) - [`c0b7781`](https://github.com/morluto/rea/commit/c0b7781bb1bf1a0d95ec4a33bab1d0d9d335100d) fix(native): retain partial metadata when a protocol-list slot is unmapped - [`9a80fb3`](https://github.com/morluto/rea/commit/9a80fb3d9714a0ba0fe2bdb8c154543fa66eee63) fix(native): bound failed class-list slots by the record budget - [`794b734`](https://github.com/morluto/rea/commit/794b7346c39e1d295898bb7fd744e7e45a8c9717) fix(native): admit each failed objc list slot exactly once - [`7b9b666`](https://github.com/morluto/rea/commit/7b9b666548a930a40655ee10e1b858f6118a317c) refactor(native): own failed Objective-C slot accounting once - [`31137e5`](https://github.com/morluto/rea/commit/31137e5ea88f9dd4bba9fed6fb392e02b766ae7c) fix(native): bound every Objective-C list slot attempt - [`22b76ee`](https://github.com/morluto/rea/commit/22b76eec963e28b78e4dfd56daf77f7d453dd434) fix(native): scope Objective-C list scan truncation - [`92f72f4`](https://github.com/morluto/rea/commit/92f72f4d0848b6731417f3699ceff8613cf60c4b) fix(native): retain class-dependent scan coverage limits - [`40e0de4`](https://github.com/morluto/rea/commit/40e0de4f2fdd80645687593fe9caa03acf7b264e) fix(native): give each Objective-C list scan its own slot bound - [`05f9b3a`](https://github.com/morluto/rea/commit/05f9b3aaf04c0d20d684605015e11d0db9451f1b) fix(native): retain unresolved pointer coverage reasons - [`98f985b`](https://github.com/morluto/rea/commit/98f985b2ddf4e22100feb5fb988f49748ac2f0a7) refactor(test): split Apple dispatch budget cases ### 📊 Changes **5 files changed** (+334 additions, -28 deletions) <details> <summary>View changed files</summary> 📝 `src/native/AppleDispatchCoverage.ts` (+19 -14) 📝 `src/native/AppleDispatchMetadata.test.ts` (+215 -0) 📝 `src/native/AppleDispatchMetadata.ts` (+18 -9) 📝 `src/native/AppleObjcDispatchFacets.ts` (+78 -5) 📝 `src/native/AppleSwiftVtables.test.ts` (+4 -0) </details> ### 📄 Description ## Summary Fixes #2610. A Mach-O image with valid Objective-C class metadata and an unmapped `__objc_protolist` slot failed its entire static dispatch-metadata decode. The class-list loop already records per-slot mapping failures into partial coverage, but the protocol-list loop read its slot pointer unguarded, so the same unmapped pointer aborted the whole decode before `protocolReader.record` could handle the record. Now the protocol-list slot uses the same guard as the class-list slot: the failure is recorded, the `objc_class_method_ivar_metadata` facet coverage becomes partial with a reason naming the mapping failure, and the independently decodable class is retained. ## Problem and expected behavior Smallest trigger: build the 4096-byte fixture from the issue with the protocol-list section address pointing at an unmapped page (`base + 0x9000`). The decode throws `RangeError: Unmapped or ambiguous metadata pointer 0x100009000` out of `decodeObjcDispatchFacets` at the protolist loop (AppleObjcDispatchFacets.ts:203). Expected: partial coverage with a reason identifying the mapping failure while `RetainedClass` and its coverage records still reach the caller, matching the existing behavior for an unmapped class-list slot. ## Change and scope Wrapped the per-slot pointer read in the `__objc_protolist` loop in the same try/catch shape the `__objc_classlist` loop already uses in the same function: the field address is hoisted, the failure is pushed to `failures` as `<field>: <message>`, and the loop continues. `pushDispatchCoverage` already turns those failure strings into partial facet coverage with a reason, so no other code changes were needed. Added three regression tests in `src/native/AppleDispatchMetadata.test.ts` using the fixture from the issue verbatim (controls `neither`, `class`, `protocol`). Scope decision: not applicable. This is a routine fix inside the existing decode boundary; no new providers, no target execution, no new scope. ## Contract and boundary impact - Semantic owner and earliest changed stage: Apple dispatch metadata decode (`decodeObjcDispatchFacets`, `src/native/AppleObjcDispatchFacets.ts`). - Contribution scope fit and linked maintainer decision: routine bug fix within existing boundaries; no scope expansion. - CLI and MCP/tool-catalog contract: none; no tool or contract changes. - Provider, bridge, target-format, or platform compatibility: Mach-O arm64 static decode only; fully mapped images decode exactly as before. - Evidence, artifact, provenance, or reconstruction contract: the coverage reason now names the unmapped protocol slot instead of the whole decode failing with no result. - Target selection, authority, upstream behavior, and cleanup details: not applicable. - Generated metadata, package, or installation impact: none. Expected effects: - [x] Read existing files or captured data. - [ ] Launch or attach to a process. - [ ] Inject instrumentation or run scripts/code. - [ ] Change process memory, registers, files, or configuration. - [ ] Contact network services. - [ ] Recover protected code or work around protections. - [ ] Documentation or usability only; no target interaction. - [ ] Not sure yet; investigation or maintainer guidance needed. ## Evidence and regression coverage - Tests added or updated: 3 tests in `src/native/AppleDispatchMetadata.test.ts` ("Apple dispatch unmapped protocol-list slot"), fixture builder taken verbatim from the issue. - Base reproduction or other evidence: on the base revision the bad-protocol control threw `RangeError: Unmapped or ambiguous metadata pointer 0x100009000` (new test failed red); after the fix the same control returns `RetainedClass` with the facet coverage `partial` and the reason containing `0x100009000`. The `neither` control still returns the class with complete coverage, and the `class` control still yields partial coverage as before. - User-visible CLI/MCP output: not applicable. - Remaining proof gaps: the issue's `NativeMacOSProvider` provider-level repro was not exercised; the decoder test covers the root cause directly and the provider path only wraps this decoder. 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:local -- src/native/AppleDispatchMetadata.test.ts` - 25/25 passed (22 existing + 3 new; the protocol control failed before the fix, passed after) - `npm run test:local -- src/native/AppleSwiftVtables.test.ts` - 3/3 passed (only other test file importing this code path) - `npm run check:fast` - typecheck + lint green - `npm run verify:test-discovery` - all 1024 test files discovered exactly once ## Compatibility, safety, and release - Breaking changes or migration steps: none. - Real Hopper/Ghidra, browser, or OS coverage: none; source tests exercise the Mach-O decode path directly. - Package or release metadata impact: none. - Security, privacy, process, or containment review: none; decode-only change, no new execution paths. ## Before merge - [x] This PR addresses a concrete problem or an agreed enhancement. - [x] Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff. - [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-31 01:05:33 +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#5300
No description provided.