[GH-ISSUE #3656] [Design] Scope and authority for the GDB, objdump and Rizin providers #1375

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

Originally created by @dandgabr on GitHub (Oct 14, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/3656

Decision needed

Should REA integrate the caller-supplied debugger and static-inspection providers proposed in #1960 and #1961 — GNU objdump, Rizin, and GDB (persistent MI sessions plus a one-shot console command) — and under which boundaries?

#1961's branch notes already record that "GDB integration scope and real-provider verification remain pending". This issue asks for the scope decision covering the final diff, so the pull requests can stop being blocked on it.

Context and evidence

Both pull requests are complete and stacked (#1960 foundation, #1961 debugger integration on top of #1959's lifecycle). What is observed on this host (Fedora 44, Linux 7.2.9, x86-64) with the providers installed by the operator:

  • objdump 2.46.1: every CLI operation (file_headers, section_headers, symbols, relocations, dwarf, disassemble, disassemble_all) returned exit code 0 with retained stdout, completion_status: complete, and identical artifact digests before and after.
  • Rizin 0.7.4: one caller-selected command (iI) returned exit code 0; Evidence kept parameters.command = "iI" and recorded the resolved executable separately as provider_executable.
  • GDB 17.2: a persistent MCP session reported mi_version: mi3; the session loaded a REA-built target, set a breakpoint, ran an inferior through a real terminal, read a register, reported state: ready with inferior_tty_verified: true, and closed with target_state: "unknown" while the inferior was live. No owned debugger process and no inferior survived the close.
  • One-shot debug-with-gdb rejects shell, !, and a trailing &; the MCP gdb_console executes shell, which its contract documents: "Run one unrestricted GDB CLI command… Commands can execute shell, alter target state, access files, and use network."

Source-derived: the two debugger paths differ deliberately. The ephemeral one-shot refuses shell execution because nothing can reap a child that outlives it; the persistent MCP session is documented as unrestricted.

Unknowns: whether a shell child launched through the MCP path can survive the session close (not measured, to avoid creating orphan processes on the test host); Windows and macOS behavior (persistent GDB sessions are refused on Windows by design); behaviour inherited from operator-installed Rizin plugins and GDB extensions.

Expected interaction with targets

  • Read existing files or captured data
  • Launch or attach to a process
  • Inject instrumentation or run scripts/code
  • Change process memory or registers
  • Write or change files/configuration
  • Contact network services
  • Recover protected code or work around protections
  • No target interaction (documentation or usability only)
  • Not sure yet

Network access and file mutation are reachable only through caller-selected provider commands or plugins; REA itself initiates neither.

Boundary details

Target selection is always the caller's: the artifact path for objdump/Rizin, the target and console command for GDB. REA owns the provider process and its cleanup, retains ownership when a stop is incomplete, and refuses to signal while inferior detach is unverified. Persistent sessions exist only for the MCP server's lifetime. Non-goals: protection recovery, anti-debugging evasion, and attack automation — none of the three providers is integrated for those purposes.

Options and tradeoffs

  1. Integrate as proposed: static objdump/Rizin plus the GDB provider, with the one-shot containment as shipped and the persistent path documented as unrestricted.
  2. Integrate with extended containment: keep the one-shot classifier and additionally bound or refuse shell-initiated children on the persistent MCP path before integration.
  3. Narrow: ship objdump/Rizin only, and keep the GDB provider as a proposal until the containment question is settled.
  4. Narrow further: keep persistent GDB sessions read-only (no shell, no !, no target-state mutation) and leave the unrestricted path to the ephemeral one-shot.

Verification available so far

Observed results: the verification lane npm run verify:reverse-providers drives the installed providers through the public CLI and MCP surfaces and asserts versions, artifact identity, recorded provenance, verified inferior-TTY readback, the one-shot classifier, and owned-debugger cleanup; it fails when an assertion cannot be established. It covers Linux only and records the remaining unknowns in its receipt.

Success criteria and open questions

Success: the maintainer's decision covers the final diff of #1960 and #1961, including the persistent session's shell-execution authority and the platform gaps. Open: is option 2's containment required before integration? Should the persistent session's unrestricted command remain, given the contract already declares it?

References

  • #1960 (local inspection foundation), #1961 (persistent debugger integration), #1959 (owned debugger shutdown lifecycle)
  • docs/reverse-engineering-tools.md — provider guide and declared limitations
  • docs/testing.md §"Reverse-engineering providers" — the verification lane above
  • CONTRIBUTING.md §"Contribution scope" and §"Fail-closed integration"
Originally created by @dandgabr on GitHub (Oct 14, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/3656 ## Decision needed Should REA integrate the caller-supplied debugger and static-inspection providers proposed in #1960 and #1961 — GNU objdump, Rizin, and GDB (persistent MI sessions plus a one-shot console command) — and under which boundaries? #1961's branch notes already record that "GDB integration scope and real-provider verification remain pending". This issue asks for the scope decision covering the final diff, so the pull requests can stop being blocked on it. ## Context and evidence Both pull requests are complete and stacked (#1960 foundation, #1961 debugger integration on top of #1959's lifecycle). What is **observed** on this host (Fedora 44, Linux 7.2.9, x86-64) with the providers installed by the operator: - **objdump 2.46.1**: every CLI operation (`file_headers`, `section_headers`, `symbols`, `relocations`, `dwarf`, `disassemble`, `disassemble_all`) returned exit code 0 with retained stdout, `completion_status: complete`, and identical artifact digests before and after. - **Rizin 0.7.4**: one caller-selected command (`iI`) returned exit code 0; Evidence kept `parameters.command = "iI"` and recorded the resolved executable separately as `provider_executable`. - **GDB 17.2**: a persistent MCP session reported `mi_version: mi3`; the session loaded a REA-built target, set a breakpoint, ran an inferior through a real terminal, read a register, reported `state: ready` with `inferior_tty_verified: true`, and closed with `target_state: "unknown"` while the inferior was live. No owned debugger process and no inferior survived the close. - **One-shot `debug-with-gdb`** rejects `shell`, `!`, and a trailing `&`; the MCP `gdb_console` **executes `shell`**, which its contract documents: "Run one unrestricted GDB CLI command… Commands can execute shell, alter target state, access files, and use network." **Source-derived**: the two debugger paths differ deliberately. The ephemeral one-shot refuses shell execution because nothing can reap a child that outlives it; the persistent MCP session is documented as unrestricted. **Unknowns**: whether a `shell` child launched through the MCP path can survive the session close (not measured, to avoid creating orphan processes on the test host); Windows and macOS behavior (persistent GDB sessions are refused on Windows by design); behaviour inherited from operator-installed Rizin plugins and GDB extensions. ## Expected interaction with targets - [x] Read existing files or captured data - [x] Launch or attach to a process - [x] Inject instrumentation or run scripts/code - [x] Change process memory or registers - [x] Write or change files/configuration - [x] Contact network services - [ ] Recover protected code or work around protections - [ ] No target interaction (documentation or usability only) - [ ] Not sure yet Network access and file mutation are reachable only through caller-selected provider commands or plugins; REA itself initiates neither. ## Boundary details Target selection is always the caller's: the artifact path for objdump/Rizin, the target and console command for GDB. REA owns the provider process and its cleanup, retains ownership when a stop is incomplete, and refuses to signal while inferior detach is unverified. Persistent sessions exist only for the MCP server's lifetime. Non-goals: protection recovery, anti-debugging evasion, and attack automation — none of the three providers is integrated for those purposes. ## Options and tradeoffs 1. **Integrate as proposed**: static objdump/Rizin plus the GDB provider, with the one-shot containment as shipped and the persistent path documented as unrestricted. 2. **Integrate with extended containment**: keep the one-shot classifier and additionally bound or refuse shell-initiated children on the persistent MCP path before integration. 3. **Narrow**: ship objdump/Rizin only, and keep the GDB provider as a proposal until the containment question is settled. 4. **Narrow further**: keep persistent GDB sessions read-only (no `shell`, no `!`, no target-state mutation) and leave the unrestricted path to the ephemeral one-shot. ## Verification available so far Observed results: the verification lane `npm run verify:reverse-providers` drives the installed providers through the public CLI and MCP surfaces and asserts versions, artifact identity, recorded provenance, verified inferior-TTY readback, the one-shot classifier, and owned-debugger cleanup; it fails when an assertion cannot be established. It covers Linux only and records the remaining unknowns in its receipt. ## Success criteria and open questions Success: the maintainer's decision covers the final diff of #1960 and #1961, including the persistent session's shell-execution authority and the platform gaps. Open: is option 2's containment required before integration? Should the persistent session's unrestricted command remain, given the contract already declares it? ## References - #1960 (local inspection foundation), #1961 (persistent debugger integration), #1959 (owned debugger shutdown lifecycle) - `docs/reverse-engineering-tools.md` — provider guide and declared limitations - `docs/testing.md` §"Reverse-engineering providers" — the verification lane above - `CONTRIBUTING.md` §"Contribution scope" and §"Fail-closed integration"
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#1375
No description provided.