Bounded model checking C
SkillDocs & knowledgeUse when C or C++ code needs memory-safety or undefined-behavior guarantees proved with CBMC, or ACSL contracts checked with Frama-C Eva or WP. Not for choosing the proof policy: use proof-driven.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Bounded model checking C skill
What this skill tells your AI
The instructions your AI receives, as published by outlinedriven/odin-claude-plugin in plugins/odin-formal/skills/bounded-model-checking-c/SKILL.md and read by ahel’s review.
Contract
| Field | Bound contract |
|---|---|
| Trigger | A C or C++ function or module needs its memory safety, arithmetic, or user assertions checked exhaustively up to a bound (CBMC), its runtime errors bounded by abstract interpretation (Frama-C Eva), or its ACSL contracts proved deductively (Frama-C WP), or an existing run's trace or alarm must be read. |
| Authority | Reversible local: writes harness .c files, ACSL annotations in the source under analysis, and the tools' output directories; rollback is reverting those files. No remote mutation. |
| Side effect | Harness and annotated source on disk, CBMC GOTO binaries when goto-cc is used, WP proof-obligation files under -wp-out, and Frama-C session files when -save is used. |
| Done | Every property in scope is reported safe by CBMC under a recorded unwind bound, or has no Eva alarm, or is Valid under WP with a named prover, or has a trace or alarm mapped to a code defect and a fix. |
Inputs
The C sources, the entry function, and the properties: absence of undefined behavior (out-of-bounds access, null or dangling dereference, overflow, division by zero), or functional contracts. Tool pins from the grounded set: CBMC cbmc-6.11.0 (brew install cbmc on macOS, .deb or .msi packages, or Docker ghcr.io/diffblue/cbmc) and Frama-C 33.0 "Arsenic" (opam install frama-c, or the Linux .run installer frama-c-linux-x86-64-33.0-Arsenic.run); WP needs Why3 and at least one prover on PATH, and Alt-Ergo is the documented first choice. Optional: per-loop unwind bounds, the target data model (--LP64, --ILP32), and an Eva precision level.
Procedure
- Pick the analysis. CBMC answers "is there any input within the bound that reaches a failing check" and produces a concrete trace, so it is the default for a function with a small loop bound. Eva answers "which operations may be unsafe for any input" over the whole program without a bound, at the price of alarms that may be false. WP answers "does this function meet its ACSL contract" and needs the contract written first. Done when: one analysis is named with the reason.
- Write a CBMC harness. In a new file, declare unconstrained inputs with the
nondet_prefix convention (int nondet_int();,_Bool nondet_bool();), which CBMC treats as a fresh value on every call. Write aharnessfunction that builds the inputs, restricts them with__CPROVER_assume(cond), calls the function under test, and states the property with__CPROVER_assert(cond, "description")or plainassert. Since CBMC 6.0 the standard checks (bounds, pointer, division by zero, shift, signed overflow, unwinding assertions) are on by default; add--unsigned-overflow-check,--conversion-check, or--memory-leak-checkwhen those classes matter, and--no-standard-checksonly with a written reason. Done when:cbmc harness.c src.c --function harness --show-propertieslists the checks the run will decide. - Run CBMC and read the result.
cbmc harness.c src.c --function harness --unwind 10 --trace.--unwind Nbounds every loop;--unwindset L:Bbounds one loop by the id shown by--show-loops. A run ends withVERIFICATION SUCCESSFUL(exit 0),VERIFICATION FAILED(exit 10), orVERIFICATION INCONCLUSIVE(exit 5); exit 1, 2, and 6 are usage, parse, and internal errors. On failure, each violated property is listed with its id and description, and--traceprints the counterexample as numbered states with every assignment from the entry to the failing line. Read the assignments to the harness inputs first: they are the concrete input that breaks the property. A failed unwinding assertion means the bound is too small, not that the code is wrong; raise the bound and rerun. Use--property idto rerun one property and--json-uiwhen a script reads the result. Done when: every property passes, or the trace's concrete inputs are recorded with the source line they break. - Reduce the CBMC problem when it does not finish. Use
--slice-formulato drop assignments that cannot reach the property,--depth Nto cap the path length, and--object-bits nwhen the run reports too many objects. For a multi-file program, compile withgoto-cc -c src.c -o src.gotoand link the GOTO binaries once, then runcbmc program.goto --function harness ...for each property. Swap the backend with--z3or--cvc5when the default SAT solver stalls. Done when: the run finishes at a recorded bound, or the smallest harness that reproduces the stall is saved. - Run Eva.
frama-c -eva -main entry src.c. Each alarm prints as[eva:alarm] file.c:LINE: Warning: <description>.followed by the ACSL assertion Eva could not prove, for exampleassert \valid(p);. The summary at the end counts alarms and the proportion of statements reached. Raise-eva-precision N(0 to 11) to trade time for fewer false alarms;-eva-slevel Nallows N separate states per program point, which removes alarms caused by merging branches. InsertFrama_C_show_each(expr)in the source to print Eva's value set at that point when an alarm is not obvious. Classify each remaining alarm as a true defect (a concrete input reaches it, which a CBMC harness on that function can confirm) or a precision loss. Done when: every alarm is classified, and true defects carry a fix. - Write ACSL and run WP. Above the function, write
requiresfor preconditions (\valid(a+(0..n-1))for array access),assignsfor the exact write set, andensuresfor the postcondition using\resultand\old(x). Above each loop, writeloop invariant,loop assigns, andloop variant; WP cannot prove a loop without them. Runframa-c -wp -wp-rte -wp-prover alt-ergo,z3 -wp-timeout 10 src.c -then -report.-wp-rteadds the runtime-error guards to the obligations;-wp-proverlists provers in order (-wp-list-proversshows what is installed);-wp-timeoutis seconds per goal (default 2). The report prints each property with its status,[ Valid]when proved, with the prover that closed it, and ends with a success percentage line. Done when: every property isValid, or each unproved goal is named with the missing invariant or lemma. - Read a WP failure.
-wp-printpretty-prints the unproved goal; the hypothesis list shows what the prover knew, and the goal shows what it could not derive. A goal that is true but unproved usually lacks a loop invariant strong enough to imply it, or anassignsclause too wide to preserve a fact; a goal that is false is a contract or code defect. Enable-wp-counter-examplesto have WP ask the prover for a model of the failing goal. Done when: the goal is classified and the invariant, lemma, or code fix is applied and the goal isValid. - Record the result. For CBMC, write the unwind bound, the checks enabled, and the data model beside each property. For Eva, write the precision and slevel with the alarm count. For WP, list the prover and timeout per property and every unproved goal or trusted annotation left in the session. Done when: every property line carries its bound or prover.
Failure and recovery
On a CBMC parse error (exit 2), check include paths and the data model flags; CBMC uses its own front end and needs the same -I paths and macro definitions as the build. On a run whose trace shows a nondet_ value the harness never constrained, tighten __CPROVER_assume and rerun; a counterexample from an impossible input is a harness defect. On an Eva run that reports many alarms in library code, add -main on a narrower entry or raise precision before reading them; do not silence alarms with annotations you cannot prove. On a WP goal that times out, try a second prover through -wp-prover and raise -wp-timeout once with a written reason; then split the goal with an ACSL assert between the steps the prover cannot join. On Frama-C exit 1, the command line or source is invalid; on exit 4, 5, or 6, it is an internal error, so save the session with -save and report it with the minimal input. When a property cannot be discharged at any useful bound, report the bound reached, the tools tried, and the obligation left open; do not narrow the property.
Output
Harness and annotated source on disk; per property, the tool, version, bound or precision or prover, and verdict; for each failure, the concrete trace (CBMC), the classified alarm (Eva), or the unproved goal with its missing invariant (WP), and the fix applied; the list of every remaining assumed or unproved obligation.
Signals
- GitHub stars
- 35
- Last commit
- Sep 2026
ahel review
K1binfo
installs-packages
Automated review, not a security audit. Ruleset v1+k2.
Others that do the same job
Advanced
- Catalog kind
- skill
- Gateway key
bounded-model-checking-c- Source
- github.com/outlinedriven/odin-claude-plugin