[GH-ISSUE #4972] [Feature] Add semantic-equivalence verification for reconstructed native code #3076

Open
opened 2026-10-31 00:36:02 +00:00 by bsakaguchi · 1 comment
Owner

Originally created by @heaven-hm on GitHub (Oct 18, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/4972

Problem

REA can inspect native binaries and collect runtime observations, but it does not yet provide a general, trustworthy workflow for checking whether reconstructed C/C++ preserves the original native function's behavior.

The goal is semantic equivalence, not source/CFG similarity. The candidate may use a different algorithm, control-flow graph, or set of helper functions; it should pass when the observable behavior is equivalent within a clearly declared scope. Incorrect calculations, missing branches/calls, wrong return values, omitted memory/global-state updates, or changed side effects should be reported as mismatches.

This complements the function-dossier/conformance work in #31/#55 and the evidence-trust issue #3980; it is specifically about comparing an original native function with a source-level reconstruction.

Proposed direction

Integrate existing analysis/verification engines through provider adapters rather than attempting to implement every solver and emulator inside REA. Keep tool availability explicit and optional; do not treat a tool being installed or imported as proof that a particular target/function was analyzed successfully.

Relevant tools:

REA already supports native-analysis providers such as Hopper, Ghidra and IDA, plus selected Z3 proofs and differential observations. Reuse those capabilities where appropriate. No single listed tool proves arbitrary binary-to-source equivalence by itself; the implementation must combine them and declare limitations accurately.

Desired workflow

  1. Bind the original executable and candidate source/build to hashes, architecture, ABI, function address/symbol and tool versions.
  2. Recover the target's supported input/output contract and relevant state: return value, output buffers, memory/global writes, errors, and observable calls/side effects.
  3. Use symbolic/concolic exploration or solver-backed obligations where feasible to generate inputs and find counterexamples.
  4. Run a differential harness against the original function and candidate with equivalent initial state and inputs. For pointer/ABI-sensitive functions, require an appropriate native harness or dual-emulation strategy; do not compare incompatible calling conventions as if they were equivalent.
  5. Where the original and candidate can be lifted to compatible semantic representations, attempt a formal equivalence/refinement check (for example, Remill + an appropriate LLVM checker). If not feasible, report tested behavior and its coverage rather than claiming a proof.
  6. Preserve every mismatch as a regression case. Expose the same result contract through CLI and MCP.

Result model

Every check should return one of:

  • PROVEN — a supported formal check established equivalence under explicit assumptions and scope.
  • MISMATCH — a reproducible counterexample or observable difference was found.
  • TESTED_ONLY — all declared tests passed, but general equivalence was not proven.
  • INCONCLUSIVE — required observations, a trustworthy reference execution, or tool capability were unavailable.

CFG similarity, decompiler agreement, comments, a manifest status label, or a passing finite test suite must never independently produce PROVEN.

Acceptance criteria

  • Add an explicit native semantic-verification capability/provider contract and CLI/MCP operation.
  • Detect a wrong-calculation control (e.g. original x * 256, candidate x * 25) and return MISMATCH with the failing input and observed values.
  • Allow a different implementation/control-flow shape when the supported proof or tests establish equivalent observable behavior.
  • Compare return values, output bytes, relevant memory/global-state changes, error outcomes and declared side effects; identify unsupported/unobserved dimensions.
  • Include boundary, signedness, overflow, null/pointer and branch-transition cases when applicable to the function contract.
  • Bind results to the exact original binary, candidate source/build, harness, test corpus and analysis-tool versions; reject stale evidence.
  • Report proof assumptions, explored/tested coverage, limits and counterexamples in machine-readable Evidence.
  • Missing providers, timeouts, unsupported instructions, unknown state or incomplete observations result in INCONCLUSIVE—not a false PASS.
  • Add source-owned fixtures including both a semantically equivalent alternative implementation and deliberately incorrect candidates; test through public CLI/MCP contracts.
  • Respect REA's existing workflow direction: compose authorized tools/scripts and comparisons; don't reintroduce a custom replay/execution subsystem without a separately justified requirement.
  • Document supported target formats/architectures and the distinction between structural similarity, differential testing and formal proof.

Non-goals

  • Requiring candidate source or CFG to resemble the original.
  • Claiming universal equivalence from finite tests or symbolic exploration with incomplete coverage.
  • Requiring every optional engine to be installed for ordinary REA usage.
  • Automatically downloading or redistributing external/proprietary tools without explicit approval and license review.
  • #31 — Complete truthful analyze_function dossiers
  • #55 — Strengthen semantic Hopper conformance across source-built fixtures
  • #3980 — Overall replay verification passes despite unresolved capture evidence
Originally created by @heaven-hm on GitHub (Oct 18, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/4972 ## Problem REA can inspect native binaries and collect runtime observations, but it does not yet provide a general, trustworthy workflow for checking whether reconstructed C/C++ preserves the original native function's behavior. The goal is **semantic equivalence, not source/CFG similarity**. The candidate may use a different algorithm, control-flow graph, or set of helper functions; it should pass when the observable behavior is equivalent within a clearly declared scope. Incorrect calculations, missing branches/calls, wrong return values, omitted memory/global-state updates, or changed side effects should be reported as mismatches. This complements the function-dossier/conformance work in #31/#55 and the evidence-trust issue #3980; it is specifically about comparing an original native function with a source-level reconstruction. ## Proposed direction Integrate existing analysis/verification engines through provider adapters rather than attempting to implement every solver and emulator inside REA. Keep tool availability explicit and optional; do not treat a tool being installed or imported as proof that a particular target/function was analyzed successfully. Relevant tools: - **angr** — binary analysis, symbolic execution, path exploration and counterexample search: https://github.com/angr/angr - **Triton** — dynamic symbolic execution, taint analysis and SMT-backed expression analysis: https://github.com/JonathanSalwan/Triton - **Remill** — lift machine instructions to LLVM bitcode for compatible downstream analysis: https://github.com/lifting-bits/remill - **Alive2** — LLVM IR refinement/equivalence checking within its supported model and scope: https://github.com/AliveToolkit/alive2 - **CBMC** — bounded model checking of C/C++ assertions and properties: https://github.com/diffblue/cbmc - **Z3** — SMT solver for supported expression/state obligations: https://github.com/Z3Prover/z3 - **Radare2** — binary inspection, disassembly and analysis (supporting evidence, not an equivalence proof): https://github.com/radareorg/radare2 - **Binary Ninja API** — optional integration for IL, CFG and data-flow evidence: https://github.com/Vector35/binaryninja-api and https://docs.binary.ninja/ REA already supports native-analysis providers such as Hopper, Ghidra and IDA, plus selected Z3 proofs and differential observations. Reuse those capabilities where appropriate. No single listed tool proves arbitrary binary-to-source equivalence by itself; the implementation must combine them and declare limitations accurately. ## Desired workflow 1. Bind the original executable and candidate source/build to hashes, architecture, ABI, function address/symbol and tool versions. 2. Recover the target's supported input/output contract and relevant state: return value, output buffers, memory/global writes, errors, and observable calls/side effects. 3. Use symbolic/concolic exploration or solver-backed obligations where feasible to generate inputs and find counterexamples. 4. Run a differential harness against the original function and candidate with equivalent initial state and inputs. For pointer/ABI-sensitive functions, require an appropriate native harness or dual-emulation strategy; do not compare incompatible calling conventions as if they were equivalent. 5. Where the original and candidate can be lifted to compatible semantic representations, attempt a formal equivalence/refinement check (for example, Remill + an appropriate LLVM checker). If not feasible, report tested behavior and its coverage rather than claiming a proof. 6. Preserve every mismatch as a regression case. Expose the same result contract through CLI and MCP. ## Result model Every check should return one of: - **PROVEN** — a supported formal check established equivalence under explicit assumptions and scope. - **MISMATCH** — a reproducible counterexample or observable difference was found. - **TESTED_ONLY** — all declared tests passed, but general equivalence was not proven. - **INCONCLUSIVE** — required observations, a trustworthy reference execution, or tool capability were unavailable. CFG similarity, decompiler agreement, comments, a manifest status label, or a passing finite test suite must never independently produce PROVEN. ## Acceptance criteria - [ ] Add an explicit native semantic-verification capability/provider contract and CLI/MCP operation. - [ ] Detect a wrong-calculation control (e.g. original `x * 256`, candidate `x * 25`) and return MISMATCH with the failing input and observed values. - [ ] Allow a different implementation/control-flow shape when the supported proof or tests establish equivalent observable behavior. - [ ] Compare return values, output bytes, relevant memory/global-state changes, error outcomes and declared side effects; identify unsupported/unobserved dimensions. - [ ] Include boundary, signedness, overflow, null/pointer and branch-transition cases when applicable to the function contract. - [ ] Bind results to the exact original binary, candidate source/build, harness, test corpus and analysis-tool versions; reject stale evidence. - [ ] Report proof assumptions, explored/tested coverage, limits and counterexamples in machine-readable Evidence. - [ ] Missing providers, timeouts, unsupported instructions, unknown state or incomplete observations result in INCONCLUSIVE—not a false PASS. - [ ] Add source-owned fixtures including both a semantically equivalent alternative implementation and deliberately incorrect candidates; test through public CLI/MCP contracts. - [ ] Respect REA's existing workflow direction: compose authorized tools/scripts and comparisons; don't reintroduce a custom replay/execution subsystem without a separately justified requirement. - [ ] Document supported target formats/architectures and the distinction between structural similarity, differential testing and formal proof. ## Non-goals - Requiring candidate source or CFG to resemble the original. - Claiming universal equivalence from finite tests or symbolic exploration with incomplete coverage. - Requiring every optional engine to be installed for ordinary REA usage. - Automatically downloading or redistributing external/proprietary tools without explicit approval and license review. ## Related work - #31 — Complete truthful `analyze_function` dossiers - #55 — Strengthen semantic Hopper conformance across source-built fixtures - #3980 — Overall replay verification passes despite unresolved capture evidence
Author
Owner

@XinwangTao commented on GitHub (Oct 18, 2026):

Strong proposal — the result model is the most important part, especially INCONCLUSIVE instead of a false PASS and the rule that structural similarity alone can never yield PROVEN. A few implementation notes from the differential-harness side: (1) bind the ABI explicitly per target (calling convention, register/stack layout, volatile state) — comparing two functions under different implicit ABIs is the fastest route to fake MISMATCHes; (2) start the corpus with scalar/pointer-free contracts (the x256 vs x25 control) before adding heap and global-state dimensions, since equivalent initial state is the hardest thing to pin down for pointer-heavy functions; (3) scope PROVEN to what the formal layer can actually establish (Remill + Alive2 is strong on arithmetic/integer logic, weak on aliasing and side-effect ordering) and fall back to TESTED_ONLY everywhere else — the contract already says this, worth making it the default rather than the exception; (4) keep each provider's verdict plus its assumptions in the Evidence so a MISMATCH can be audited back to a concrete counterexample.

<!-- gh-comment-id:6247532632 --> @XinwangTao commented on GitHub (Oct 18, 2026): Strong proposal — the result model is the most important part, especially INCONCLUSIVE instead of a false PASS and the rule that structural similarity alone can never yield PROVEN. A few implementation notes from the differential-harness side: (1) bind the ABI explicitly per target (calling convention, register/stack layout, volatile state) — comparing two functions under different implicit ABIs is the fastest route to fake MISMATCHes; (2) start the corpus with scalar/pointer-free contracts (the x*256 vs x*25 control) before adding heap and global-state dimensions, since equivalent initial state is the hardest thing to pin down for pointer-heavy functions; (3) scope PROVEN to what the formal layer can actually establish (Remill + Alive2 is strong on arithmetic/integer logic, weak on aliasing and side-effect ordering) and fall back to TESTED_ONLY everywhere else — the contract already says this, worth making it the default rather than the exception; (4) keep each provider's verdict plus its assumptions in the Evidence so a MISMATCH can be audited back to a concrete counterexample.
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#3076
No description provided.