Aeneas Verification Toolchain
repository·main·Indexed 21 days ago
https://github.com/aeneasverif/aeneasA verification toolchain for Rust programs that translates Rust's Mid-level Intermediate Representation (MIR) into a pure lambda calculus. It enables formal verification by targeting backends such as F*, Coq, HOL4, and Lean. The toolchain utilizes Charon to generate .llbc files before translation. For the Lean backend, it includes the #decompose command to refactor functions into smaller, independently verifiable auxiliary definitions with automatic proof generation.
What's inside Aeneas
- Aeneas is a verification toolchain designed for Rust programs. Its primary purpose is to embed a purely functional translation of Rust programs into theorem provers, specifically Coq or Lean, to facilitate formal verification.
Understand Aeneas core concepts and terminology
mainAeneas is a tool that translates Rust programs into pure Lean code via the LLBC (Low-Level Borrow Calculus) intermediate representation. This enables formal verification of Rust code within the Lean proof assistant.
Key technical concepts include:
- LLBC: An intermediate representation between Rust MIR and Lean that captures ownership, borrowing, and lifetime information.
Resultmonad: The error monad used in all generated code. Success is represented byok v, and errors (panics, overflows, out-of-bounds) are represented byfail. All generated functions returnResult T.- Backward continuation: A function returned alongside a borrowed value to propagate updates back to the original owner (used for
&mut T). In Lean, this appears as an extra return value:Result (T × (T → Result OriginalType)). ControlFlow: An inductive type used for loop translation. UseControlFlow.cont xto continue a loop with new state, orControlFlow.done yto break with a result.loop(fixed-point operator): The combinator used to translate Rust loops. Its signature isloop (body : α → Result (ControlFlow α β)) (x : α) : Result β. You can reason about it usingloop.specorloop.spec_decr_nat.
Using Auxiliary Specifications for Complex Refinement
mainWhen a refinement proof is too complex, use an Auxiliary Specification as a bridge. This creates a chain of equivalences:
Nist spec ⟷₁ Lean spec ⟷₂ Auxiliary spec ⟷₃ Aeneas translation- Nist spec: Mathematical specification (e.g., FIPS).
- Lean spec: Direct Lean translation of the Nist spec (pure, uses
Idmonad). - Auxiliary spec: Intermediate spec that mirrors the implementation structure (pure).
- Aeneas translation: The generated Lean code (lives in the
Resultmonad).
By proving equivalences between adjacent levels, each individual proof becomes simpler because the structures are closely aligned.
Understand the limitations of Aeneas
mainAeneas provides verification facilities for safe Rust, but it currently has several technical limitations regarding the subset of Rust it can translate. Users should be aware of the following constraints:
- Loops: Currently, nested loops are not supported.
- Function Pointers/Closures: Support for traits is available, but direct support for function pointers and closures is under development.
- Type Parametricity: You cannot currently instantiate a type parameter with a type that contains a borrow.
- Function Signatures: Nested borrows within function signatures are not supported.
- Interior Mutability: Support is under development (the project is exploring modeling these effects via ghost states).
- Concurrency: Concurrent execution is not supported; addressing coarse-grained parallelism is a long-term goal.
Prohibited Git and Sub-agent actions
mainCertain actions are strictly banned to protect the integrity of the shared codebase and system resources.
Banned Git Commands
NEVER run the following commands:
git checkoutgit restoregit stashgit reset
Reasoning: These commands can silently destroy uncommitted changes made by other agents. Even reverting a file you own is dangerous because it may revert to a
HEADstate that lacks changes from a different agent.Banned Sub-agent Spawning
Agents must NEVER spawn sub-agents that run heavy processes (e.g., Lean LSP, builds, solvers).
- Resource Exhaustion: Sub-agents bypass the supervisor's budget control.
- File Conflicts: Sub-agents may bypass the supervisor's file ownership tracking.
- Loss of Control: The supervisor cannot observe or manage sub-agents spawned by other agents.
Note: Lightweight sub-agents (e.g.,
exploreagents for searching) are permitted.Requirements for High-Quality Postconditions
mainTo prevent agents from proving trivially weak theorems, all postconditions must adhere to the following standards:
- Full Functional Correctness (FC): The postcondition must express a direct equality between the implementation output and the specification function:
repr(output) = Spec.algorithmName(repr(input1), ...). - Avoid Weakness: Do not use only length preservation,
True, or relational specs (simulation relations). Use direct equalities. - The Vacuity Test: Ask: "Would this postcondition hold if the implementation returned arbitrary/zero data?" If yes, it is too weak.
- The Composability Test: Ask: "Could the caller derive its own FC equality knowing only this postcondition?" If no, it is too weak.
- Structural Properties: Properties like
wfArrayorlengthsare supplementary conjuncts and should not be the primary specification.
- Full Functional Correctness (FC): The postcondition must express a direct equality between the implementation output and the specification function:
Verify postconditions for functional correctness
mainWhen reviewing or writing specifications, ensure postconditions express full functional correctness (FC), not just structural properties. A postcondition that only describes metadata (e.g.,
index advanced,flags preserved, orres.length = n) without specifying the actual computed values is critically weak and insufficient for callers to derive their own FC.Key Requirements:
- Relate to Pure Spec: The postcondition must relate the output to the pure specification function (e.g.,
output = Spec.SHAKE128(input)[offset..offset+len]). - Strength for Callers: Trace the call chain. If an outer loop or caller requires both the length and the element-wise values of a slice, the inner loop's postcondition must provide both.
- Avoid Weak Patterns: Reject postconditions that are only length-based, vacuously true (
Trueorfun _ => True), or missing existential quantifiers for length combined with functional properties.
- Relate to Pure Spec: The postcondition must relate the output to the pure specification function (e.g.,
Verify loop specifications
mainWhen a function contains loops (translated as_loop,_loop0,_loop1, etc.), the proof requires@[step]specs for each loop. A function specification without its corresponding loop specifications is unprovable. Ensure every loop has a spec with a full postcondition (even if marked assorry).How to handle pure and monadic constants in Lean
mainWhen working with Rust constants or globals translated by Aeneas, you must treat pure and monadic constants differently to enable automation.
Pure Constants
If a constant is not in the
Resultmonad, mark it with appropriate attributes so automation can use it:- Use
@[simp],@[agrind],@[scalar_tac_simps], or@[bvify_simps]. - Use
@[simp_lists_simps]if the constant involves lists or arrays.
Monadic Constants (in the
Resultmonad)Treat monadic constants as functions by proving a
@[step]theorem. This involves a two-step process:- Prove a raw equation using
native_decide. - Prove the step form using the equation and
WP.spec_ok.
Once these are defined, the
stepandstep*tactics will handle the constant automatically.// 1. Prove a raw equation theorem MyConst_eq : MyConst = ok value := by native_decide // 2. Prove the step form @[step] theorem MyConst : MyConst ⦃ fun res => res = value ⦄ := by rw [MyConst_eq] simp [WP.spec_ok]- Use
Handling unreadable file formats
mainWhen working with files that cannot be parsed directly (e.g., PDFs, binary files, images, or encrypted files), follow the principle of Transparency over convenience.
Critical Rule: If a file format is unreadable, state this immediately and upfront. Do not rely on training knowledge or guess the content.
Recommended Fallback Strategies:
- Install
popplerto usepdftohtml(requires user permission). - Request manual input: Ask the user to paste relevant sections into the chat.
- Manual conversion: Ask the user to convert the PDF to text themselves.
- Search for alternatives: Look for HTML versions of the document (e.g., IETF drafts on
datatracker.ietf.org).
- Install
Workspace and Git safety rules for agents
mainTo prevent destroying the work of other parallel agents, adhere to these strict workspace constraints:
Git Safety (CRITICAL):
- NEVER run
git checkout,git restore,git stash,git reset, or any command that reverts or discards file changes. Other agents have uncommitted work on disk; reverting will destroy their work silently. - NEVER commit or push without explicit user approval.
File Modification Rules:
- In
same-clonemode: ONLY modify the specific target file (e.g.,/path/to/MyModule.lean). NEVER edit any other.leanfile, as other agents are working on them in parallel. Use localprivate axiomandTODOcomments for cross-file requirements. - In
separate-clonemode: Your primary task is the target file, but you MAY modify other files in your clone (e.g., shared defs, axiom files, bridge lemmas) if necessary. The supervisor handles merging across clones.
- NEVER run
Use agrind as the default tactic
mainIn Aeneas proofs,
agrindis the primary tactic. It is fast and handles arithmetic, equalities, and most structural goals.Workflow:
- Always try
agrindfirst. - If
agrindfails, trygrind(which is slower but more powerful).
Warning: Do NOT use
simp_all. It is extremely slow in the large contexts typical of Aeneas proofs and can silently drop hypotheses required for later steps.agrind- Always try