[GH-ISSUE #2579] fix(hopper): binary overview reports zero bytes for uninterpretable segment coordinates #895

Closed
opened 2026-10-15 18:29:23 +00:00 by bsakaguchi · 1 comment
Owner

Originally created by @coygeek on GitHub (Oct 13, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/2579

Summary

When a provider inventory contains a segment whose coordinates cannot be interpreted, binary_overview returns a successful segment summary with length 0. This presents an unknown size as a known zero-byte range. A focused test reproduced the result at the application boundary with a fixture provider. The original coordinates remain available, and other inventory counts are unaffected.

Steps to reproduce

Use the source baseline and installed dependencies described under Runtime or environment. This reproduction uses a fixture provider; it does not require an installed analysis engine or demonstrate that a live provider emits these coordinates.

  1. Create src/application/segmentLengthRegression.test.ts with the following test.
  2. Run npm run test:focused -- src/application/segmentLengthRegression.test.ts from the repository root.
  3. Observe successful overview results with length 0 for both uninterpretable coordinate pairs. The two assertions rejecting that result fail, while the canonical-range and equal-endpoint controls pass.
import { expect, it } from "vitest";
import { EnhancedTools } from "./EnhancedTools.js";
import { addressDistance, parseSegments } from "../domain/hopperValues.js";
import { observed } from "../../tests/fixtures/analysisExecution.js";

const runOverview = (start: string, end: string) => {
  const segment = { name: "__TEXT", start, end, readable: true, writable: false, executable: null };
  const calls: string[] = [];
  const tools = new EnhancedTools({
    execute: async (name) => {
      calls.push(name);
      if (name === "current_document") return observed("fixture");
      if (name === "list_segments") return observed([segment]);
      if (name === "list_procedures" || name === "list_strings") return observed([]);
      throw new Error(`Unexpected operation: ${name}`);
    },
  });
  return { segment, calls, result: tools.execute("binary_overview", {}) };
};

it("preserves known lengths for canonical coordinates", async () => {
  expect(addressDistance("0x1000", "0x2000")).toBe(4096);
  expect(await runOverview("0x1000", "0x2000").result).toMatchObject({ ok: true, value: { segments: [{ length: 4096 }] } });
  expect(await runOverview("0x1000", "0x1000").result).toMatchObject({ ok: true, value: { segments: [{ length: 0 }] } });
});

it.each([
  ["0X1000", "0X2000"],
  ["not-an-address", "0x2000"],
])("does not report an uninterpreted range as zero bytes: %s", async (start, end) => {
  const probe = runOverview(start, end);
  const result = await probe.result;
  console.log(JSON.stringify({ start, end, parsed: parseSegments([probe.segment]).ok, distance: addressDistance(start, end), calls: probe.calls, result }));
  expect(result).not.toMatchObject({ ok: true, value: { segments: [{ length: 0 }] } });
});

Expected behavior

The overview promises segment lengths while the repository requires unknown provider values and derived facts to retain their uncertainty or produce a typed failure. When a coordinate cannot be interpreted, the overview must reject the record or explicitly report that its length is unknown. A successful numeric length of zero must not stand in for a failed interpretation. This does not require accepting noncanonical address spellings.

Actual behavior

Both fixtures pass segment parsing and return successful overview results with length 0. The first fixture uses start 0X1000 and end 0X2000; the second uses start not-an-address and end 0x2000. Each calls current_document, list_segments, list_procedures and list_strings and returns one segment, zero procedures and zero strings. The focused run exited with code 1 because both assertions rejecting a successful zero-length result failed. Its control test passed, with length 4096 for the canonical range and length 0 for equal canonical endpoints.

Evidence

  • Expected source: AGENTS.md:5 and :29 require observations, derivations and unknowns to remain distinct and unknown boundary values to use typed failures; src/contracts/enhancedToolContracts.ts:64 advertises every segment with its length; tests/boundary/mcp/enhancedTools.04-uses-complete-inventories-inline.test.ts:178 establishes rejection of omitted coordinates in this overview workflow.
  • Failure source: The executed fixture test above observes successful zero-length summaries. src/domain/hopperValues.ts:54 accepts coordinate strings without interpretation, :419 returns the parsed records, and :29 converts failed coordinate interpretation into zero; src/application/EnhancedTools.ts:529 consumes those records and :543 returns that value as the segment length.
  • Evidence provenance: observed
  • Local verification: reproduced
  • Reproduction completeness: complete

The bundled Hopper and Ghidra producers format their ordinary segment coordinates as canonical lowercase hexadecimal. This reproduction establishes malformed-coordinate handling at the application boundary, not an incident observed with either live engine. The case-insensitive CFG comparison parser does not establish uppercase support for segment inventories.

Read-only issue inspection found related overview reports with different triggers. Issue #2460 concerns missing IDA capabilities, and issue #28 concerns inventory completeness and argument handling. Neither covers unknown segment lengths being reported as zero.

Restoration check

Repeat the two fixture-provider cases. Neither may return a successful known zero-byte length while its coordinates remain uninterpreted. A typed failure or an explicit unknown length restores the contract; successful interpretation of an accepted spelling may instead produce its actual size. Retain the canonical-range control at 4096 bytes and the equal-endpoint control at zero so that rejection of all segments or removal of genuine zero lengths cannot hide the defect.

Runtime or environment

The failure was verified at source baseline a2d8b94dd5 and is still present at 8b181716ac. Node v26.10.0, npm 11.19.1, and existing installed dependencies. The reproduction ran through the focused source-test runner with a fixture provider and no live analysis engine.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: The observed application result gives misleading derived segment metadata for uninterpretable provider coordinates. Source coordinates and inventory counts remain available. Occurrence with a live provider and broader workflow harm were not demonstrated.
  • Workaround status: available
  • Workaround basis: Callers can inspect the returned coordinates and treat the size as unknown when they cannot interpret the range. For the uppercase numeric fixture, callers can derive 4096 bytes independently. This avoids trusting the false zero while preserving the source coordinates.
Originally created by @coygeek on GitHub (Oct 13, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/2579 ## Summary When a provider inventory contains a segment whose coordinates cannot be interpreted, binary_overview returns a successful segment summary with length 0. This presents an unknown size as a known zero-byte range. A focused test reproduced the result at the application boundary with a fixture provider. The original coordinates remain available, and other inventory counts are unaffected. ## Steps to reproduce Use the source baseline and installed dependencies described under Runtime or environment. This reproduction uses a fixture provider; it does not require an installed analysis engine or demonstrate that a live provider emits these coordinates. 1. Create src/application/segmentLengthRegression.test.ts with the following test. 2. Run npm run test:focused -- src/application/segmentLengthRegression.test.ts from the repository root. 3. Observe successful overview results with length 0 for both uninterpretable coordinate pairs. The two assertions rejecting that result fail, while the canonical-range and equal-endpoint controls pass. ```ts import { expect, it } from "vitest"; import { EnhancedTools } from "./EnhancedTools.js"; import { addressDistance, parseSegments } from "../domain/hopperValues.js"; import { observed } from "../../tests/fixtures/analysisExecution.js"; const runOverview = (start: string, end: string) => { const segment = { name: "__TEXT", start, end, readable: true, writable: false, executable: null }; const calls: string[] = []; const tools = new EnhancedTools({ execute: async (name) => { calls.push(name); if (name === "current_document") return observed("fixture"); if (name === "list_segments") return observed([segment]); if (name === "list_procedures" || name === "list_strings") return observed([]); throw new Error(`Unexpected operation: ${name}`); }, }); return { segment, calls, result: tools.execute("binary_overview", {}) }; }; it("preserves known lengths for canonical coordinates", async () => { expect(addressDistance("0x1000", "0x2000")).toBe(4096); expect(await runOverview("0x1000", "0x2000").result).toMatchObject({ ok: true, value: { segments: [{ length: 4096 }] } }); expect(await runOverview("0x1000", "0x1000").result).toMatchObject({ ok: true, value: { segments: [{ length: 0 }] } }); }); it.each([ ["0X1000", "0X2000"], ["not-an-address", "0x2000"], ])("does not report an uninterpreted range as zero bytes: %s", async (start, end) => { const probe = runOverview(start, end); const result = await probe.result; console.log(JSON.stringify({ start, end, parsed: parseSegments([probe.segment]).ok, distance: addressDistance(start, end), calls: probe.calls, result })); expect(result).not.toMatchObject({ ok: true, value: { segments: [{ length: 0 }] } }); }); ``` ## Expected behavior The overview promises segment lengths while the repository requires unknown provider values and derived facts to retain their uncertainty or produce a typed failure. When a coordinate cannot be interpreted, the overview must reject the record or explicitly report that its length is unknown. A successful numeric length of zero must not stand in for a failed interpretation. This does not require accepting noncanonical address spellings. ## Actual behavior Both fixtures pass segment parsing and return successful overview results with length 0. The first fixture uses start 0X1000 and end 0X2000; the second uses start not-an-address and end 0x2000. Each calls current_document, list_segments, list_procedures and list_strings and returns one segment, zero procedures and zero strings. The focused run exited with code 1 because both assertions rejecting a successful zero-length result failed. Its control test passed, with length 4096 for the canonical range and length 0 for equal canonical endpoints. ## Evidence - Expected source: AGENTS.md:5 and :29 require observations, derivations and unknowns to remain distinct and unknown boundary values to use typed failures; src/contracts/enhancedToolContracts.ts:64 advertises every segment with its length; tests/boundary/mcp/enhancedTools.04-uses-complete-inventories-inline.test.ts:178 establishes rejection of omitted coordinates in this overview workflow. - Failure source: The executed fixture test above observes successful zero-length summaries. src/domain/hopperValues.ts:54 accepts coordinate strings without interpretation, :419 returns the parsed records, and :29 converts failed coordinate interpretation into zero; src/application/EnhancedTools.ts:529 consumes those records and :543 returns that value as the segment length. - Evidence provenance: observed - Local verification: reproduced - Reproduction completeness: complete The bundled Hopper and Ghidra producers format their ordinary segment coordinates as canonical lowercase hexadecimal. This reproduction establishes malformed-coordinate handling at the application boundary, not an incident observed with either live engine. The case-insensitive CFG comparison parser does not establish uppercase support for segment inventories. Read-only issue inspection found related overview reports with different triggers. Issue [#2460](https://github.com/morluto/rea/issues/2460) concerns missing IDA capabilities, and issue [#28](https://github.com/morluto/rea/issues/28) concerns inventory completeness and argument handling. Neither covers unknown segment lengths being reported as zero. ## Restoration check Repeat the two fixture-provider cases. Neither may return a successful known zero-byte length while its coordinates remain uninterpreted. A typed failure or an explicit unknown length restores the contract; successful interpretation of an accepted spelling may instead produce its actual size. Retain the canonical-range control at 4096 bytes and the equal-endpoint control at zero so that rejection of all segments or removal of genuine zero lengths cannot hide the defect. ## Runtime or environment The failure was verified at source baseline a2d8b94dd50dab8d966239dd63ab84718aa8b4b4 and is still present at 8b181716aca2cb467be499cba226abfb5ae19141. Node v26.10.0, npm 11.19.1, and existing installed dependencies. The reproduction ran through the focused source-test runner with a fixture provider and no live analysis engine. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: The observed application result gives misleading derived segment metadata for uninterpretable provider coordinates. Source coordinates and inventory counts remain available. Occurrence with a live provider and broader workflow harm were not demonstrated. - Workaround status: available - Workaround basis: Callers can inspect the returned coordinates and treat the size as unknown when they cannot interpret the range. For the uppercase numeric fixture, callers can derive 4096 bytes independently. This avoids trusting the false zero while preserving the source coordinates.
bsakaguchi 2026-10-15 18:29:23 +00:00
Author
Owner

@morluto commented on GitHub (Oct 14, 2026):

Addressed on main in 36db19c59c9e7bd36b00a468b589d1cb5c3d2d49.

Malformed, cross-space, reversed, or overflowing Hopper segment coordinates produce an explicit unknown length. The binary_overview schema permits null; equal valid coordinates still mean zero.

Validation: Affected suites passed in the merged 65-file run (1169 passed, 2 explicit skips), with full static/architecture checks and documentation checks passing. Final installed-package verification is recorded separately in the audit.

Verification scope: Controlled source and production-boundary verification on macOS with pinned Node 24.18.0; real external engines and Windows hosts were not independently rerun. Closure covers the described source defect, not broader provider conformance or performance.

<!-- gh-comment-id:6159188219 --> @morluto commented on GitHub (Oct 14, 2026): Addressed on main in [36db19c59c9e7bd36b00a468b589d1cb5c3d2d49](https://github.com/morluto/rea/commit/36db19c59c9e7bd36b00a468b589d1cb5c3d2d49). Malformed, cross-space, reversed, or overflowing Hopper segment coordinates produce an explicit unknown length. The binary_overview schema permits null; equal valid coordinates still mean zero. Validation: Affected suites passed in the merged 65-file run (1169 passed, 2 explicit skips), with full static/architecture checks and documentation checks passing. Final installed-package verification is recorded separately in the audit. Verification scope: Controlled source and production-boundary verification on macOS with pinned Node 24.18.0; real external engines and Windows hosts were not independently rerun. Closure covers the described source defect, not broader provider conformance or performance.
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#895
No description provided.