Aeneas Verification Toolchain

repository·main·Indexed 21 days ago

https://github.com/aeneasverif/aeneas

A 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.

Tokens
64.3K
Snippets
142
Records
248
Agent score
74%

What's inside Aeneas

  1. Understand Aeneas core concepts and terminology

    main

    Aeneas 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.
    • Result monad: The error monad used in all generated code. Success is represented by ok v, and errors (panics, overflows, out-of-bounds) are represented by fail. All generated functions return Result 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. Use ControlFlow.cont x to continue a loop with new state, or ControlFlow.done y to break with a result.
    • loop (fixed-point operator): The combinator used to translate Rust loops. Its signature is loop (body : α → Result (ControlFlow α β)) (x : α) : Result β. You can reason about it using loop.spec or loop.spec_decr_nat.
  2. Using Auxiliary Specifications for Complex Refinement

    main

    When 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 Id monad).
    • Auxiliary spec: Intermediate spec that mirrors the implementation structure (pure).
    • Aeneas translation: The generated Lean code (lives in the Result monad).

    By proving equivalences between adjacent levels, each individual proof becomes simpler because the structures are closely aligned.

  3. Understand the limitations of Aeneas

    main

    Aeneas 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.
  4. Prohibited Git and Sub-agent actions

    main

    Certain actions are strictly banned to protect the integrity of the shared codebase and system resources.

    Banned Git Commands

    NEVER run the following commands:

    • git checkout
    • git restore
    • git stash
    • git 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 HEAD state 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., explore agents for searching) are permitted.

  5. Requirements for High-Quality Postconditions

    main

    To 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 wfArray or lengths are supplementary conjuncts and should not be the primary specification.
  6. Verify postconditions for functional correctness

    main

    When 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, or res.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 (True or fun _ => True), or missing existential quantifiers for length combined with functional properties.
  7. How to handle pure and monadic constants in Lean

    main

    When 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 Result monad, 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 Result monad)

    Treat monadic constants as functions by proving a @[step] theorem. This involves a two-step process:

    1. Prove a raw equation using native_decide.
    2. Prove the step form using the equation and WP.spec_ok.

    Once these are defined, the step and step* 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]
  8. Handling unreadable file formats

    main

    When 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:

    1. Install poppler to use pdftohtml (requires user permission).
    2. Request manual input: Ask the user to paste relevant sections into the chat.
    3. Manual conversion: Ask the user to convert the PDF to text themselves.
    4. Search for alternatives: Look for HTML versions of the document (e.g., IETF drafts on datatracker.ietf.org).
  9. Workspace and Git safety rules for agents

    main

    To 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-clone mode: ONLY modify the specific target file (e.g., /path/to/MyModule.lean). NEVER edit any other .lean file, as other agents are working on them in parallel. Use local private axiom and TODO comments for cross-file requirements.
    • In separate-clone mode: 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.
  10. Use agrind as the default tactic

    main

    In Aeneas proofs, agrind is the primary tactic. It is fast and handles arithmetic, equalities, and most structural goals.

    Workflow:

    1. Always try agrind first.
    2. If agrind fails, try grind (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