CBMC
|
Back to Code Contracts User Documentation
The built-in and user-defined predicates discussed in this section are meant to let users describe the shape of the memory accessed through pointers in requires clauses and ensures clauses. Attempting to call these predicates outside of a requires or ensures clause context will result in a verification error.
To specify memory footprint we use a special function called __CPROVER_is_fresh
. The meaning of __CPROVER_is_fresh
is that we are borrowing a pointer from the external environment (in a precondition), or returning it to the calling context (in a postcondition).
__CPROVER_is_fresh
takes two arguments: a pointer and an allocation size. The first argument is the pointer to be checked for "freshness" (i.e., not previously allocated), and the second is the expected size in bytes for the memory available at the pointer.
It returns a bool
value, indicating whether the pointer is fresh.
To illustrate the semantics for __CPROVER_is_fresh
, consider the following implementation of sum
function.
When checking the contract abstracts a function a __CPROVER_is_fresh
in a requires clause will cause fresh memory to be allocated. In an ensures clause it will check that memory was freshly allocated.
In our example, consider that a function foo
may call sum
.
When using a contract as an abstraction in place of a call to the function a __CPROVER_is_fresh
in a requires clause will check that the argument supplied is fresh and __CPROVER_is_fresh
in an ensures clause will result in a fresh malloc.
CBMC models memory allocation failure modes. When activated, these modesl result in different behaviours for __CPROVER_is_fresh
in assumption contexts (i.e. when used in a requires clause of a contract being checked against a function, or in an ensures clause of a contract being used to abstract a function call).
malloc
and __CPROVER_is_fresh
never fail and will accept a size parameter up to SIZE_MAX
without triggerring errors. However, pointer overflow and assigns clause checking errors will happen any time one tries to access such objects beyond an offset of __CPROVER_max_malloc_size
(in bytes), by executing ptr[size-1]
or ptr[size]
in user-code, or by writing __CPROVER_assigns(__CPROVER_object_from(ptr))
in a contract;--malloc-may-fail --malloc-fail-null
): In this mode, if size
is larger than __CPROVER_max_malloc_size
, malloc
returns a NULL pointer, and imposes an implicit assumption that size is less than __CPROVER_max_malloc_size
when returning a non-NULL pointer. __CPROVER_is_fresh
never fails in assumption contexts, so it adds an implicit assumption that size
is less than __CPROVER_max_malloc_size
.--malloc-may-fail --malloc-fail-assert
): In this mode, if size
is larger than __CPROVER_max_malloc_size
, an max allocation size exceeded
assertion is triggered in malloc
and execution continues under the assumption that size
is less than __CPROVER_max_malloc_size
, with malloc
returning a non-NULL pointer. __CPROVER_is_fresh
never fails in assumption contexts, so it will trigger a max allocation size exceeded
assertion and continue execution under the implicit assumption that size
is less than __CPROVER_max_malloc_size
.This predicate holds if lb
, p
and ub
are valid pointers within the same object and the pointers are ordered such that lb <= p && p <= ub
holds.
In assertion contexts, the predicate checks the conditions described above. In assumption contexts, the predicate checks that lb
and ub
are valid pointers into the same object, and updates p
using a side effect to be a non-deterministic pointer ranging between lb
and ub
.
Users can write their own memory predicates based on the core predicates described above. __CPROVER_is_fresh
allows to specify pointer validity and separation. __CPROVER_pointer_in_range
allows to specify aliasing constraints.
For instance, one could write a predicate defining linked lists of at most len
elements as follows:
One can also simply describe finite nested structures:
And one can then use these predicates in requires or ensures clauses for function contracts.
The main limitation with user defined predicates are:
For instance, in the is_list
example above, recursion is bounded by the use of the explicit len
parameter. The is_double_buffer
predicate also describes a bounded structure.