Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Invariant Synthesis for Incomplete Verification Engines

(authored by agents unless marked 🧑) Tag: neider2017invariant, paper

paper focus

The paper targets invariant generation when the ideal verification logic is undecidable but the checker uses a sound, incomplete reduction. It states: “We propose a framework for synthesizing inductive invariants for incomplete verification engines, which soundly reduce logical problems in undecidable theories to decidable theories.” (§abstract, p.1) It also states: “Our framework is based on the counter-example guided inductive synthesis principle (CEGIS) and allows verification engines to communicate non-provability information to guide invariant synthesis.” (§abstract, p.1)

Inference: the central input is not a conventional counterexample to program behavior. It is a model showing that the checker failed to prove a reduced verification condition. The model is mined for a constraint that rules out a family of future invariants.

The framework assumes an undecidable theory U, a decidable theory D to which verification conditions are conservatively reduced, a Boolean communication theory B, and a hypothesis class H of positive Boolean combinations over a predicate set P. The paper says: “The translation is assumed to be conservative in the sense that if the translated formula in D is valid, then we are assured that the original verification condition is U-valid.” (§1, p.2) It warns that a D-model of a failed reduction need not refute the original U formula: “Note that this model may not be a model for the negation of the formula in U.” (§2, p.4)

technique 1: counterexample guided non-provability information

what it is. A normal verification engine returns conjunctive/disjunctive non-provability information (CD-NPI) after a candidate invariant fails. The paper defines normality with two monotonicity requirements: “if the engine cannot prove the validity of the Hoare triple {α}s{γ} and B δ ⇒ γ, then it cannot prove the validity of the Hoare triple {α}s{δ}” and “if the engine cannot prove the validity of the Hoare triple {γ}s{β} and B γ ⇒ δ, then it cannot prove the validity of the Hoare triple {δ}s{β}.” (§2, p.5) These requirements justify discarding stronger postconditions or weaker preconditions after a failure.

how to apply it.

  1. Choose a finite predicate set P and a positive-Boolean hypothesis class H. (§2, pp.3–4) In the paper, each program fragment receives ghost variables whose values track the predicates. (§2.1, p.5)
  2. Propose candidate annotations γ at all holes and generate the ordinary verification conditions in U. (§2, p.4)
  3. Translate each condition to approx(VC) in D using the checker’s sound approximation, such as bounded quantifier instantiation, bounded recursive unfolding, or natural proofs. (§1, p.2; §2, p.4)
  4. If every reduced condition is D-valid, accept γ. (§2, p.4)
  5. Otherwise obtain a D-model of the negated reduced condition and read the pre-state valuation v and post-state valuation v′ of the predicate ghosts. (§2.1, pp.5–6)
  6. For a failed {α}s{γ}, form a weakening constraint χ as the disjunction of predicates false in v′. (§2.1, p.6)
  7. For a failed {γ}s{β}, form a strengthening constraint η as the conjunction of predicates true in v. (§2.1, p.6)
  8. For a failed inductivity triple {γ}s{γ}, return the pair (η, χ) built from the predicates true in v and false in v′; every future candidate γ″ must satisfy either ⊬B η ⇒ γ″ or ⊬B γ″ ⇒ χ. (§2.1, p.6) Similarly, a weakening constraint χ requires future candidates γ″ with ⊬B γ″ ⇒ χ, and a strengthening constraint η requires ⊬B η ⇒ γ″. (§2.1, p.6)
  9. Add the constraint to the sample and synthesize a new candidate. The paper repeats this until “the learner proposes a valid annotation or it detects that no valid annotation exists (e.g., if the class of candidate annotations is finite and all annotations are exhausted)”. (§2.1, p.7)

The paper summarizes the three extraction rules: “When a Hoare triple of the form {α}s{γ} fails, the verification engine returns the B-formula” followed by the disjunction over predicates false after execution; “When a Hoare triple of the form {γ}s{β} fails, the verification engine returns the B-formula” followed by the conjunction over predicates true before execution; and “When a Hoare triple of the form {γ}s{γ} fails, the verification engine returns the pair” of those formulas. (§2.1, p.6)

Verus form. Inference: represent P by spec fn predicates over a ghost view of the Rust state, and represent the candidate H formula by a Boolean spec fn used in a loop invariant or function ensures. A verifier driver can parse a failed Verus obligation, classify it as entry/postcondition or loop-inductivity failure, and generate the analogous η/χ candidate constraints. Inference: Verus normally reports failed proof obligations rather than a D-model with predicate ghost valuations, so obtaining v and v′ requires explicit ghost booleans or a separate model-producing reduction; this model extraction is not supplied by the paper. Inference: the executable code and ownership discipline remain unchanged; only proof annotations and the synthesis driver change.

what it proves and assumes. The paper’s theorem assumes a normal engine, finite P, and an annotation in H that the engine can prove. It states: “If there exists an annotation in H that the verification engine can use to prove P correct, then the CD-NPI framework described in Section 2.1 is guaranteed to converge to such an annotation in finite time.” (§2.4, p.11) The guarantee is about finding a provable annotation in the chosen class, not about completeness of U or correctness of the specification.

technique 2: reduce CD-NPI learning to ICE and use Houdini

what it is. CD-NPI constraints are formulas, while ICE learning consumes positive, negative, and implication valuations. The reduction treats each p ∈ P as an ICE Boolean variable. A weakening disjunction becomes a positive example, a strengthening conjunction becomes a negative example, and an inductivity pair becomes an implication example. (§2.2, pp.7–9)

how to apply it.

  1. Maintain a CD-NPI sample S = (W, S, I), where W is weakening disjunctions, S is strengthening conjunctions, and I is (conjunction, disjunction) inductivity pairs. (Definition 2, §2.1, p.7)
  2. Convert each disjunction J in W to valuation d(J), setting exactly predicates in J false; put these valuations in ICE positives. (§2.2, p.8; Definition 3, p.9)
  3. Convert each conjunction J in S to valuation c(J), setting exactly predicates in J true; put these valuations in ICE negatives. (§2.2, p.8)
  4. Convert each pair (J1,J2) in I to implication (c(J1), d(J2)). (§2.2, p.8)
  5. Run any ICE learner for positive Boolean formulas. (§2.2, pp.8–9)
  6. If using Houdini, first retain the largest conjunction satisfying all positive examples; propagate implication targets into the positive set until a fixed point; then check every negative example. (§2.2, pp.9–10)
  7. Return the conjunction as the next invariant, or report that no consistent conjunctive invariant exists. (§2.2, p.10)

The paper gives the operational convergence bound for Houdini: “when used in an iterative setting, [Houdini] is guaranteed to converge in at most n + 1 rounds or report that no conjunctive invariant over P exists.” (§2.2, p.10) The reduction itself is justified by: “Then, γ is consistent with S if and only if γ is consistent with S̃.” (Theorem 1, p.9)

worked example. In the inverse-array program, the candidate loop predicate is b1 and return predicates are b2 and b3. After the conjectured γL = b1 and γR = b2 ∧ b3, a path from loop exit to the return statement is valid in U but not provable in D, and the engine extracts (η, χ) with η = b1 and χ = b2; the paper says this is reduced to ICE implication ((1, 0, 0), (1, 0, 1)) for b1,b2,b3. (§2.3, pp.10–11) The next candidate γL = b1 and γR = b3 satisfies the ICE and CD-NPI constraints, and the paper reports that the verification conditions along all program paths can then be proved valid in D. (§2.3, p.11)

Verus form. Inference: an ICE valuation can be encoded as a ghost Seq<bool> or a tuple of Boolean spec fn results, while Houdini’s conjunction is emitted as a conjunction of invariant clauses. Candidate predicates over arrays, maps, or pointer views need explicit Verus spec fns and triggers; Rust ownership does not supply the logical predicate vocabulary automatically. Inference: an ICE implication corresponds to preserving a relation between pre-state and post-state ghost views across a loop iteration, expressed as a loop invariant and helper proof fn; the paper’s learner can remain external to Verus and write the selected clauses into source.

where it fails or assumes. H is restricted to positive combinations, though the paper notes that adding negated predicates to P recovers arbitrary finite Boolean combinations. (§2.2, p.9) Candidate-predicate discovery is left open: “In this work, our learning algorithm synthesizes invariants over a fixed set of predicates.” (Related Work, p.3) Inference: a Verus deployment therefore fails when the needed relation is absent from P, even if the program is otherwise verifiable.

technique 3: natural-proof heap verification with generated predicates

what it is. The heap application combines Dryad separation logic, natural proofs, Boogie, and Houdini. The paper states: “Natural proofs [50,48] is a sound but incomplete strategy for deciding satisfiability of Dryad formulas.” (§3, p.13) The verifier translates recursive heap predicates to classical logic with explicit heaplets, abstracts recursive definitions with uninterpreted functions and adds finite-depth unfoldings where locations are dereferenced, models heaplets and other sets with a decidable theory of maps, and inserts frame reasoning. (§3, p.13)

how to apply it.

  1. Annotate a C program in Dryad and convert it with VCDryad to Boogie. (§3, p.13)
  2. Generate predicates from templates instantiated with all combinations of program variables. (§3, pp.13–14)
  3. Select predicate categories according to the specification: shape; shape plus keys; or shape, keys, and sizes. (§3, pp.13–14)
  4. Create annotation holes by erasing user loop invariants, by weakening contracts of purely recursive functions (typically removing formulas from post-conditions of recursively-called functions), or by deleting the post-conditions of five straight-line programs. (§3, p.15)
  5. Run the CD-NPI-to-ICE transformation with Houdini against the Boogie verifier. (§3, p.13)
  6. Accept the learned conjunction when all transformed verification conditions prove. (§3, pp.13–15)

The templates cover pointer and shape facts, key-set relations, lengths/heights, arithmetic, heaplet membership, disjointness, and equality. (§3, pp.13–14) The evaluation used 10 benchmark suites containing 82 routines. (§3, p.15) The paper reports: “Our prototype was successful in learning invariants and contracts for all 82 benchmarks.” (§3, p.15) It also reports a single-core Ubuntu 16.04.1 virtual machine with an Intel Core i7-7820 HK 2.9 GHz CPU and 2 GB memory, and says the median time for most suites was below 10 seconds. (§3, p.15) The denominator is 82 routines across ten suites; the paper does not give one aggregate mean time. Fig. 4 (p.16) labels each suite with its program count, and Appendix A (Table 2, pp.21–23) lists per-program values.

Verus form. Inference: a Rust implementation would replace Dryad heaplets with ownership-checked references, ghost sets or sequences, and spec fn shape predicates. A list or tree mutator would carry requires ownership and shape facts, ensures updated shape/key facts, and loop invariant clauses generated from the selected templates. Inference: finite unfolding and frame reasoning do not transfer as library calls automatically; Verus proofs need explicit recursive lemmas, reveal/fuel choices where appropriate, and proof functions about unchanged portions. The reusable idea is template-driven predicate generation plus solver feedback, not Dryad syntax.

technique 4: bounded quantifier instantiation

what it is. The second implementation handles quantified specifications by instantiating universal formulas with finitely many ground terms and checking the resulting quantifier-free D formula. The paper says: “The key idea of instantiation-based heuristics is to instantiate universally quantified formulas with a finite number of ground terms and then check for validity of the resulting quantifier-free formulas.” (§4, p.16) A valid D approximation proves the U formula; a failed approximation supplies a model for CD-NPI extraction. (§4, p.16)

how to apply it.

  1. Use the pre-/post-conditions of the program as templates, instantiate them over all combinations of program variables, and add octagonal predicates ±x ± y ≤ c (for a few programs also over array access expressions). (§4, p.17)
  2. Run Boogie/Z3 with bounded quantifier instantiation. (§4, p.16)
  3. Feed failed-model valuations into the same CD-NPI extraction rules. (§4, p.16)
  4. Use Houdini to learn a conjunctive invariant and repeat until all obligations prove. (§4, p.16)

The benchmark suite contained twelve programs obtained by simplifying programs from IronFleet, VSComp, ExpressOS, and sparse-matrix multiplication. (§4, p.17) Table 1 gives each program’s candidate count |P|, learner iterations, final invariant size |Inv|, and time. For example, inverse used 414 candidates, 126 iterations, 73 invariant predicates, and 9.04 seconds; sparseMatrixMul used 768 candidates, 313 iterations, 91 invariant predicates, and 14.49 seconds. (Table 1, p.17) The denominator is 12 programs; all rows have reported values, and the paper says the average was less than a minute per routine. (§4, p.17)

Verus form. Inference: Verus’s SMT pipeline already reasons about quantified spec fns and array/sequence views, but it does not expose the paper’s bounded-instantiation driver as a user API. A practical adaptation would generate finite witness predicates and candidate invariant clauses from loop bounds, array accesses, and contract terms, then rerun Verus after each CD-NPI update. Inference: this is a synthesis wrapper around Verus rather than a change to Verus’s soundness; quantifier completeness and model extraction remain external assumptions.

evidence, trusted parts, and limits

The empirical evidence consists of two implementations and their stated environments: 82 heap routines, all reported solved; and 12 quantified programs with the per-program values in Table 1. The paper does not compare the heap result against another tool on the whole set: “Our goal in this evaluation, however, is not to compete with other, mature tools on a subset of benchmarks, but to measure the efficacy of our proposed CD-NPI based invariant synthesis framework on the whole benchmark set.” (§3, p.15)

Trusted components include the soundness of the U-to-D approximation, D’s model construction, normality of the engine, the predicate templates, and the program specifications. The paper explicitly excludes timeout-only engines because “our framework requires model construction in the theory D.” (§2, p.4) Inference: a Verus product must also trust the Rust compiler, Verus translation, solver, proof erasure, external specifications, and any #[verifier::external_body] contracts; CD-NPI does not remove those trust boundaries.

The principal failures are incompleteness and search-space dependence. Natural proofs and quantifier instantiation can fail even when a U proof exists; no candidate in H can be synthesized if P omits a necessary predicate; and finite-H convergence says nothing about the truth of the specification. Inference: agent-written verified Rust remains threatened by missing or misleading predicates, vacuous contracts, and model extraction that overfits a reduction artifact. The technique lowers manual invariant search cost, but it does not establish that the chosen Rust contract expresses the product requirement.

techniques for the catalog

  • cd-npi invariant synthesis — extract weakening, strengthening, or inductivity constraints from models of failed reduced verification conditions and iterate candidates
  • cd-npi to ice reduction — map disjunctions, conjunctions, and inductivity pairs to positive, negative, and implication ICE examples
  • houdini conjunction learning — compute the strongest conjunction consistent with propagated ICE examples and stop in at most n + 1 rounds when one exists
  • natural-proof heap templates — instantiate shape, key, size, and heaplet predicates over program variables before learning heap invariants
  • bounded-quantifier invariant learning — reduce quantified obligations to finite ground instances, then feed failed models into the same CD-NPI learner

Last edited: