CBMC
Rewriting Declarative Assign and Frees Specification Functions

Back to top Code Contracts Transformation Specification

The front end allows to express parametric assignable sets and freeable sets declaratively by calling special built-ins from regular C functions, as described in Assigns Clauses and Frees Clauses.

Such functions require a rewriting pass in order to turn them into active functions that can effectively populate a write set with new targets.

Rewriting Assigns Clause Functions

GOTO functions which specify assignable locations have the following type signature:

void foo(parameter-list);

They can be generated from an assigns clause or written by the user.

To convert them to active functions, the first step is to inline their body and remove all function calls, the result of which must be loop-free.

Then, a second pass adds the write set to be filled as new parameter to the function:

void foo(
foo-parameters,
// write set to populate with new targets
Runtime representation of a write set.

In the inlined body, calls to built in functions __CPROVER_assignable, __CPROVER_object_upto, __CPROVER_object_from, __CPROVER_object_whole are numbered from 0 to N in increasing order from their position in the instruction sequence:

CALL __CPROVER_object_upto(ptr, size); // index 0
CALL __CPROVER_object_whole(ptr); // index 1
CALL __CPROVER_object_from(ptr); // index 2
...
CALL __CPROVER_assignable(ptr, size, is_ptr_to_ptr); // index N
void __CPROVER_object_upto(void *ptr, __CPROVER_size_t size)
void __CPROVER_object_whole(void *ptr)
void __CPROVER_assignable(void *ptr, __CPROVER_size_t size, __CPROVER_bool is_ptr_to_ptr)
void __CPROVER_object_from(void *ptr)

They are then rewritten into calls to the corresponding instrumentation hook provided by the cprover_contracts.c library:

__write_set_to_fill, 0, ptr, size);
__write_set_to_fill, 1, ptr);
__write_set_to_fill, 2, ptr);
...
__write_set_to_fill, N, ptr, size, is_ptr_to_ptr);
void __CPROVER_contracts_write_set_insert_object_whole(__CPROVER_contracts_write_set_ptr_t set, __CPROVER_size_t idx, void *ptr)
Inserts a snapshot of the range of bytes covering the whole object pointed to by ptr in set->contact_...
void __CPROVER_contracts_write_set_insert_assignable(__CPROVER_contracts_write_set_ptr_t set, __CPROVER_size_t idx, void *ptr, __CPROVER_size_t size)
Inserts a snapshot of the range starting at ptr of size size at index idx in set->contract_assigns.
void __CPROVER_contracts_write_set_insert_object_upto(__CPROVER_contracts_write_set_ptr_t set, __CPROVER_size_t idx, void *ptr, __CPROVER_size_t size)
Inserts a snapshot of the range of bytes starting at ptr of size bytes in set->contact_assigns at ind...
void __CPROVER_contracts_write_set_insert_object_from(__CPROVER_contracts_write_set_ptr_t set, __CPROVER_size_t idx, void *ptr)
Inserts a snapshot of the range of bytes starting at ptr and extending to the end of the object in se...

The function is then instrumented as described in GOTO Function Instrumentation which adds a second write set parameter, that allows to check that the function has no unintended side effects.

void foo(
<parameter-list>,
// write set to populate with new targets
// write set against which to check itself for unwanted side effects
__CPROVER_contracts_write_set_ptr_t __write_set_to_check);

Generating Havoc Functions from Assigns Clause Functions

Contract replacement requires havocing the set of targets described in an assigns clause. Since there can exist dependencies and aliasing between the different targets, in order to havoc them in parallel, we first need to snapshot the target locations into a write set data structure, and generate a function that can havoc these snapshotted locations in a second step.

GOTO functions which specify assignable locations have the following type signature:

void foo(parameter-list);

They can be generated from an assigns clause or written by the user.

To convert them to havoc functions, the first step is to inline their body and remove all function calls, the result of which must be loop-free.

Then, a second pass adds the write set to be havoced as new parameter to the function:

void contract::foo::havoc(
// write set containing snapshots of the targets to havoc

In the inlined body, calls to built in functions __CPROVER_assignable, __CPROVER_object_upto, __CPROVER_object_from, __CPROVER_object_whole are numbered from 0 to N in increasing order from their position in the instruction sequence:

CALL __CPROVER_object_upto(ptr, size); // index 0
CALL __CPROVER_object_whole(ptr); // index 1
CALL __CPROVER_object_from(ptr); // index 2
...
CALL __CPROVER_assignable(ptr, size, is_ptr_to_ptr); // index N

They are then rewritten into calls to the corresponding instrumentation hook provided by the cprover_contracts.c library:

Targets specified with __CPROVER_object_whole:

CALL __CPROVER_object_whole(ptr); // at index i

Get rewritten to:

CALL __CPROVER_contracts_havoc_object_whole(write_set, i);

Targets specified with __CPROVER_object_from or __CPROVER_object_upto:

CALL __CPROVER_object_from(ptr); // at index i

Get rewritten to:

CALL __CPROVER_contracts_havoc_slice(write_set, i);

Last, targets specified with __CPROVER_assignable

CALL __CPROVER_assignable(ptr, size, is_ptr_to_ptr); // at index i

Get rewritten to a typed nondeterministic assignment:

ASSIGN *target = nondet_type_of_target();
void * __CPROVER_contracts_write_set_havoc_get_assignable_target(__CPROVER_contracts_write_set_ptr_t set, __CPROVER_size_t idx)
Returns the start address of the conditional address range found at index idx in set->contract_assign...
@ ASSIGN
Definition: goto_program.h:46

The generated function has the intended side effects by construction and nothing else.

Rewriting Frees Clause Functions

User-defined functions which list freeable pointers have the following signature:

void foo(<parameter-list>);

They are first inlined and checked to be loop free before being rewritten into functions which accepts a write set parameter to be filled with freeable targets defined by the function:

void foo(
<parameter-list>,
// write set to populate with new targets

Calls to the built-in function:

void __CPROVER_freeable(void *ptr)

are rewritten into to calls to active instrumentation functions which insert into the write set

__write_set_to_fill, ptr);
void __CPROVER_contracts_write_set_add_freeable(__CPROVER_contracts_write_set_ptr_t set, void *ptr)
Adds the freeable pointer ptr to set->contract_frees.

The function is then instrumented as described in GOTO Function Instrumentation which adds a second write set parameter, that allows to check that the function has no unintended side effects.

void foo(
<parameter-list>,
// write set to populate with new targets
// write set against which to check itself for unwanted side effects
__CPROVER_contracts_write_set_ptr_t __write_set_to_check);

Prev Next
Generating GOTO Functions From Contract Clauses Rewriting User-Defined Memory Predicates