CHARON/AENEAS Rust Verification Pipeline
- The CHARON/AENEAS pipeline is an integrated framework that extracts Rust’s MIR as IRs and translates them into pure functional definitions for automated verification.
- It employs explicit semantic extraction via CHARON and symbolic execution with AENEAS to generate logical proof obligations for theorem provers.
- The system supports rapid extract-function refactoring using minimal virtual crates, ensuring behavior preservation through machine-checked equivalence proofs.
The CHARON/AENEAS pipeline is an integrated verification and program analysis framework addressing the extraction and verification of Rust code through the coupling of explicit semantic extraction (via CHARON) with higher-order functional translation and proof obligation discharge (via AENEAS). The pipeline is central to recent advances in Rust verification, supporting annotation-free, machine-checked equivalence reasoning for nontrivial transformations, particularly extract-function refactorings within REM2.0 and beyond (Ho et al., 2024, Britton et al., 27 Jan 2026).
1. Architectural Overview
The CHARON/AENEAS pipeline consists of two main stages. CHARON acts at the compiler interface level, intercepting compilation via custom driver hooks and harvesting a fully-resolved intermediate representation (IR) from Rust's Mid-level Intermediate Representation (MIR). AENEAS consumes this IR, converting program fragments into pure, functional definitions in theorem provers such as Coq, F*, Lean, or HOL4.
The canonical workflow is as follows:
- The user invokes verification via the CHARON CLI:
1
$ charon verify --tool=aeneas /path/to/crate
- CHARON wraps cargo by redirecting RUSTC invocations to charon-driver, which employs rustc callbacks to extract IR just before code generation.
- CHARON constructs two IRs: ULLBC (Unstructured Low-Level Borrow Calculus, a decorated control-flow graph) and LLBC (Low-Level Borrow Calculus, a structured AST).
- IRs are serialized as JSON (.ullbc/.llbc).
- AENEAS imports LLBC, applies symbolic execution, functional translation, and proof-condition extraction.
- The output comprises pure logical definitions and proof obligations, which are discharged by SMT solvers or interactive theorem provers (Ho et al., 2024).
In REM2.0, this pipeline is integrated into a VSCode/JSON-RPC-based daemon, supporting rapid extract-function refactoring and optional equivalence verification using minimal "virtual crates" (Britton et al., 27 Jan 2026).
2. Intermediate Representations
A central contribution is the explicit separation and formalization of program IRs suitable for analysis:
- Def-IDs and Name Resolution: CHARON refines rustc's undifferentiated DefId with strongly-typed IDs (FunctionId, StructId, TraitId, etc.) carrying fully qualified, human-readable paths.
- ULLBC (Unstructured LLBC): Represents the Rust program as a typified CFG with instructions such as Assign, Drop, Assertion, and control terms (Goto, Switch, Return). All type and trait resolution is performed, yielding monomorphic call targets; constants are reified as ASTs.
- LLBC (Structured LLBC): Reconstructs a block-nested, gotoless AST from ULLBC using demand-set analysis, natural loop detection, and control-flow translation. Covers all MIR patterns and preserves original Rust scoping (Ho et al., 2024).
Table: CHARON/AENEAS IR Properties
| IR | Structure | Key Features |
|---|---|---|
| ULLBC | CFG | Full type/trait resolution, explicit borrows/moves |
| LLBC | AST | No gotos, pure let/if/while/match, scoping as in Rust |
3. Pipeline Stages and Translation Process
The end-to-end translation comprises the following stages (Ho et al., 2024, Britton et al., 27 Jan 2026):
- Extraction and Repair: For REM, Rust-Analyzer hoists user-selected code, inferring parameters/types; a "repairer" iteratively fixes lifetimes and signatures until the code type-checks.
- Virtual Crate Construction: Minimal Cargo projects are constructed for both original and refactored versions to isolate input for CHARON.
- CHARON (Rust → LLBC): Converts Rust code post-MIR optimization to ULLBC and LLBC, exposing explicit borrows, lifetime tracking, and normalized trait resolution.
- AENEAS (LLBC → Theorem Prover Language): Interprets LLBC functionally, abstracting stack/heap effects away, handling borrow regions as pure value-passing, and emits pure functional definitions.
- Proof Obligation Emission: AENEAS generates verification conditions (VCs) as SMT2 or proof assistant scripts (e.g., Coq, F*, Lean).
Example: The LLBC for a simple increment function with pre/postconditions will serialize a structured AST, which AENEAS translates to logical definitions and VCs, then emits proof scripts that can be dispatched to Z3, Coq, etc. (Ho et al., 2024).
4. Verification Methodology
AENEAS employs symbolic execution, region abstraction, and weakest-precondition (WP) generation over LLBC to produce logical VCs:
- For each function, logical variables are introduced for all relevant program entities (parameters, locals, borrow regions).
- The system maintains a symbolic state , mapping variables to immutable values and tracking live borrows with invariants.
- Each kind of statement (let, if, match, call) generates explicit WP formulas on paths; function calls are abstracted via pre/postregion contracts.
- Proof obligations emerge as universally quantifed formulas, over input variables and regions, to be checked externally.
Classic WP rules in LaTeX:
$\inference[\textsc{WP-Let}] {Q[x \mapsto e]\ =\ Q'} {\mathit{wp}(\texttt{let }x=e;\,S, Q) = \mathit{wp}(S,\,Q')}$
$\inference[\textsc{WP-If}] {\mathit{wp}(S_t, Q)\;=\;R_t \quad \mathit{wp}(S_f, Q)\;=\;R_f} {\mathit{wp}(\texttt{if }c\ S_t\ \texttt{else}\ S_f,\,Q) = (c\Rightarrow R_t) \land (\neg c\Rightarrow R_f)}$
Equivalence proofs require extraction of both original and refactored definitions, translation to Coq, and generation of the lemma:
In most purely functional cases, Coq's reflexivity suffices; unsound or non-equivalent transformations result in proof failure at this stage (Britton et al., 27 Jan 2026).
5. Applications: Verification and Refactoring
A primary application domain is machine-checked equivalence verification for extract-function refactorings. REM2.0 leverages the pipeline to provide interactive, annotation-free proof of behaviour preservation after automated code transformations.
The process sequence is as follows (Britton et al., 27 Jan 2026):
- Code extraction and refactoring in the editor.
- Automatic fixing of type and lifetime signatures until type checking succeeds.
- Generation of minimal proof artifacts (virtual crates).
- CHARON/AENEAS translation to Coq.
- Automated proof obligation discharge, surfacing results to the user for accept/rollback.
This has enabled fast (generally sub-2s for small examples, ~4s for more substantial cases) equivalence checking for a substantial Rust subset (safe, sequential code with trait bounds but without closures, dynamic dispatch, or unsafe). Deliberately incorrect refactorings correctly yield failed proof obligations, ensuring no false positives.
Other applications demonstrated include Rust verification frameworks, taint checkers, and cross-language translation tools that harness the intermediate LLBC/ULLBC representations (Ho et al., 2024).
6. APIs, Extensibility, and Engineering
The pipeline's extensibility arises from explicit, stable IRs, open APIs, and customizable cleanup/analysis passes (Ho et al., 2024):
- APIs expose Rust data structures for IR manipulation (e.g.,
pub struct ULLBCFunction,Instr,Expr, etc.). - Extensibility hooks permit the registration of custom passes (
charon::PassManager::add_pass), as well as pattern-matching for trait instances. - Language bindings: LLBC/ULLBC can be parsed and manipulated from Rust, OCaml, and other host languages.
Workflow scripts and pseudocode are provided for typical usage patterns: parsing LLBC, symbolic execution over statement lists, VC accumulation, and downstream emission of backend-prover scripts.
7. Empirical Results and Limitations
Empirical evaluation demonstrates the pipeline's practicality and reliability:
- End-to-end verification over nontrivial Rust crates (20KLOC): ~80s for CHARON extraction, 2–5s for AENEAS/VC generation, <50ms per function for SMT discharge (batch), ~90s for full translation of a 200-function crate (Ho et al., 2024).
- REM2.0 benchmarks: For 20 verification cases (10 trivial, 10 real-world), all trivial and 90% real-world cases proved equivalence under 10s wall-clock time. No false positives were observed; explicit support and rejection for non-supported constructs prevent unsound proofs (Britton et al., 27 Jan 2026).
- The supported Rust subset intentionally omits interior mutability, closures, concurrency, unsafe code, and advanced generics (e.g., nested for<’a> patterns). Ongoing work aims to lift these restrictions.
A plausible implication is that further expansion of CHARON/AENEAS to richer fragments of Rust may incrementally generalize automated verification for the language, driven by the stability and invertibility of the intermediate representations.
References:
- "Charon: An Analysis Framework for Rust" (Ho et al., 2024)
- "Refactoring and Equivalence in Rust: Expanding the REM Toolchain with a Novel Approach to Automated Equivalence Proofs" (Britton et al., 27 Jan 2026)