[GH-ISSUE #4247] [Feature] Let trace_call_path decide several goals in one traversal and report the roots it reaches #1670

Open
opened 2026-10-16 21:08:49 +00:00 by bsakaguchi · 0 comments
Owner

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

Before submitting

  • I searched the open and closed issues and pull requests for a related proposal.
  • I understand the contribution scope must be reviewed before integration; open questions are welcome here.
  • This request does not include credentials, private samples, or sensitive target data.

Area

Native analysis or decompilation

Current limitation

A common static question in vulnerability analysis is "can this sink function be reached from any externally callable function?" REA cannot answer it in one call. trace_call_path accepts one start, at most one goal, and a direction (src/contracts/enhancedInputs.ts:52-59). A backward trace from the sink returns nodes, edges, one traversal_path, and goal_status, but it does not mark the roots it reaches (visited functions whose caller read completed and returned no callers). To test N candidate entry points or exports, an agent has to run N backward traces that repeat the same traversal, or derive roots by hand from the edge list.

Use cases

  • Triage of a dangerous call such as memcpy or system in a parser: does any exported function, main, or a constructor reach it, and through which shortest chain?
  • Attack-surface review of a library: which of its exports can reach a given internal function?
  • Explaining a negative: when no goal is reached, which call-graph roots were reached instead, and which expansions remain unresolved?

Desired outcome and success criteria

trace_call_path (or an equivalent operation) accepts a set of goals and decides each one in a single traversal, keeping today's unknown-preserving semantics. Success criteria:

  • A caller can pass several goal selectors; each goal gets its own status (reached, not_reached, or unknown) under the same rules as today's goal_status.
  • Each reached goal has one shortest path from the start.
  • A backward trace reports explicit roots: visited functions whose caller relationships were read with complete coverage and were empty. Functions with partial or unknown caller coverage are never reported as roots.
  • When unresolved expansions remain, an unreached goal stays unknown, and the existing indirect-call limitation stays in the result: a static negative is not proof that the sink is unreachable.
  • The single-goal form keeps working unchanged.

Expected interaction with targets

Read existing files or captured data

Target and scope details (optional)

Read-only traversal of the provider's existing call relationships for the open document. Non-goals: a depth limit by default (docs/tool-design.md reserves limits for measured constraints), runtime reachability, taint or data-flow analysis, and automatic discovery of entry points or exports. A preset goal set of "all entry points and exports" needs a provider-neutral inventory of entry points, exports, and TLS callbacks that REA does not have yet; that is a possible follow-up, not part of this request.

Verification available so far

I can suggest a sample or manual check

Verification details (optional)

A self-compiled C program with two exported functions, one of which reaches a sink through three calls, plus an unexported function that reaches it through a function pointer. Expected: the first export is reached with the three-call path; the second is not_reached on providers that report complete caller coverage; the pointer-reached caller is absent from the direct graph and the indirect-call limitation is present. Run it on Ghidra and Hopper. Modern headless IDA cannot read callers yet (PR #4200), and #3988 can turn missing IDA callee coverage into a definite negative, so IDA negatives need those fixed first.

Alternatives considered

  • A separate reachability tool: it mixes entry-point discovery with traversal and duplicates trace_call_path, which docs/tool-design.md advises against.
  • Client-side composition: N single-goal traces repeat the traversal and still give no root list.
  • build_call_path: offline over supplied analyze_function dossiers, forward-only, with one start and one goal.

Possible direction

Add an optional goals array next to goal and per-goal results next to goal_status, plus a roots list for backward traces, and continue traversal until every goal is decided or the graph is exhausted. The result should report the reached goals with a shortest path for each, the roots reached when a goal is not found, and the explicit caveat that indirect calls are not in the static graph. It should keep REA's unbounded, coverage-aware semantics rather than a fixed depth limit or an early stop after a few goals are reached.

References

  • REA baseline: main at 0cc56493be (package 6.5.0): src/application/CallPathTracing.ts (traversal lines 98-117, result lines 239-279), src/contracts/enhancedToolContracts.ts:74-78, docs/mcp-contracts.md:81-91, docs/native-investigation.md.
  • Related, not duplicates: #348 (original single-goal trace_call_path), #3988 (definite IDA negative), PR #4200 (headless IDA callers), #3767 (DOS table-dispatched handlers invisible), #4057 (PE/ELF export inventory for boundary verification).
  • Search: issues and pull requests searched for reachability, reachable, entry point, attack surface, source to sink, sink, taint, multiple goals, trace_call_path, and goal_status; no existing proposal found.
Originally created by @coygeek on GitHub (Oct 15, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/4247 ### Before submitting - [X] I searched the open and closed issues and pull requests for a related proposal. - [X] I understand the [contribution scope](https://github.com/morluto/rea/blob/main/CONTRIBUTING.md#contribution-scope) must be reviewed before integration; open questions are welcome here. - [X] This request does not include credentials, private samples, or sensitive target data. ### Area Native analysis or decompilation ### Current limitation A common static question in vulnerability analysis is "can this sink function be reached from any externally callable function?" REA cannot answer it in one call. `trace_call_path` accepts one `start`, at most one `goal`, and a `direction` (`src/contracts/enhancedInputs.ts:52-59`). A backward trace from the sink returns nodes, edges, one `traversal_path`, and `goal_status`, but it does not mark the roots it reaches (visited functions whose caller read completed and returned no callers). To test N candidate entry points or exports, an agent has to run N backward traces that repeat the same traversal, or derive roots by hand from the edge list. ### Use cases - Triage of a dangerous call such as `memcpy` or `system` in a parser: does any exported function, `main`, or a constructor reach it, and through which shortest chain? - Attack-surface review of a library: which of its exports can reach a given internal function? - Explaining a negative: when no goal is reached, which call-graph roots were reached instead, and which expansions remain unresolved? ### Desired outcome and success criteria `trace_call_path` (or an equivalent operation) accepts a set of goals and decides each one in a single traversal, keeping today's unknown-preserving semantics. Success criteria: - A caller can pass several goal selectors; each goal gets its own status (`reached`, `not_reached`, or `unknown`) under the same rules as today's `goal_status`. - Each reached goal has one shortest path from the start. - A backward trace reports explicit roots: visited functions whose caller relationships were read with complete coverage and were empty. Functions with partial or unknown caller coverage are never reported as roots. - When unresolved expansions remain, an unreached goal stays `unknown`, and the existing indirect-call limitation stays in the result: a static negative is not proof that the sink is unreachable. - The single-`goal` form keeps working unchanged. ### Expected interaction with targets Read existing files or captured data ### Target and scope details (optional) Read-only traversal of the provider's existing call relationships for the open document. Non-goals: a depth limit by default (`docs/tool-design.md` reserves limits for measured constraints), runtime reachability, taint or data-flow analysis, and automatic discovery of entry points or exports. A preset goal set of "all entry points and exports" needs a provider-neutral inventory of entry points, exports, and TLS callbacks that REA does not have yet; that is a possible follow-up, not part of this request. ### Verification available so far I can suggest a sample or manual check ### Verification details (optional) A self-compiled C program with two exported functions, one of which reaches a sink through three calls, plus an unexported function that reaches it through a function pointer. Expected: the first export is `reached` with the three-call path; the second is `not_reached` on providers that report complete caller coverage; the pointer-reached caller is absent from the direct graph and the indirect-call limitation is present. Run it on Ghidra and Hopper. Modern headless IDA cannot read callers yet (PR #4200), and #3988 can turn missing IDA callee coverage into a definite negative, so IDA negatives need those fixed first. ### Alternatives considered - A separate `reachability` tool: it mixes entry-point discovery with traversal and duplicates `trace_call_path`, which `docs/tool-design.md` advises against. - Client-side composition: N single-goal traces repeat the traversal and still give no root list. - `build_call_path`: offline over supplied `analyze_function` dossiers, forward-only, with one start and one goal. ### Possible direction Add an optional `goals` array next to `goal` and per-goal results next to `goal_status`, plus a `roots` list for backward traces, and continue traversal until every goal is decided or the graph is exhausted. The result should report the reached goals with a shortest path for each, the roots reached when a goal is not found, and the explicit caveat that indirect calls are not in the static graph. It should keep REA's unbounded, coverage-aware semantics rather than a fixed depth limit or an early stop after a few goals are reached. ### References - REA baseline: `main` at 0cc56493be167309bd72040273de603ad75ad9e0 (package 6.5.0): `src/application/CallPathTracing.ts` (traversal lines 98-117, result lines 239-279), `src/contracts/enhancedToolContracts.ts:74-78`, `docs/mcp-contracts.md:81-91`, `docs/native-investigation.md`. - Related, not duplicates: #348 (original single-goal `trace_call_path`), #3988 (definite IDA negative), PR #4200 (headless IDA callers), #3767 (DOS table-dispatched handlers invisible), #4057 (PE/ELF export inventory for boundary verification). - Search: issues and pull requests searched for reachability, reachable, entry point, attack surface, source to sink, sink, taint, multiple goals, `trace_call_path`, and `goal_status`; no existing proposal found.
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#1670
No description provided.