CBMC
|
Back to Code Contracts User Documentation
Take a look at the example below.
Function sum
writes the sum of a
and b
to out
, and returns SUCCESS
; unless the result of the addition is too large to be represented as an uint32_t
, in which case it returns FAILURE
. Let's write a function contract for this function.
Function contracts are specified by attaching specification clauses to function declarations or definitions:
The following built-in constructs can also be used with functions contracts:
In our example, the developer may require from the caller to properly allocate all arguments, thus, pointers must be valid. We can specify the preconditions of a function using __CPROVER_requires
(see the Requires & Ensures Clauses documentation for details) and we can specify an allocated object using a predicate called __CPROVER_is_fresh
(see Memory Predicate for details). Thus, for the sum
function, the set of preconditions are
We can use __CPROVER_ensures
to specify postconditions (see Requires & Ensures Clauses for details). In our example, developers can use the built-in construct __CPROVER_return_value
, which represents the return value of a function. As postconditions, one may list possible return values (in this case, either SUCCESS
or FAILURE
) as well as describe the main property of this function: if the function returns SUCCESS
, then *out
stores the result of a + b
. We can also check that the value in *out
will be preserved in case of failure by using __CPROVER_old
, which refers to the value of a given object in the pre-state of a function (see History Variables for details). Thus, for the sum
function, the set of postconditions are
Finally, the assigns clause allows developers to define a frame condition (see Assigns Clause for details). In general, systems for describing the frame condition of a function use either writes or modifies semantics; this design is based on the former. This means that memory not specified by the assigns clause must not be written within the given function scope, even if the value(s) therein are not modified. In our example, since we expect that only the value that out
points to may be modified, we annotate the function using __CPROVER_assigns(*out)
.
Here is the whole function with its contract.
First, we have to prove that the function satisfies the contract.
The first command just compiles the GOTO program as usual, the second command instruments the code to check the function satisfies the contract, and the third one runs CBMC to do the checking.
In order to start the analysis in an arbitrary state when a function contract gets checked, the contract instrumentation pass automatically havocs all static variables of the program. To avoid havocing certain static variables, the command line switch --nondet-static-exclude name-of-variable
can be passed to goto-instrument
in addition to the other swtiches specifying the contract to check.
Now that we have proved that the function satisfies the contract, we can use the function contract in place of the function implementation wherever the function is called.
The first command just compiles the GOTO program as usual, the second command instruments the code to use the function contract in place of the function implementation wherever is invoked, and the third one runs CBMC to check the program using contracts.