CPROVER Manual TOC
Memory Predicates
Syntax
bool __CPROVER_is_fresh(void *p, size_t size);
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).
Parameters
__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.
Return Value
It returns a bool
value, indicating whether the pointer is fresh.
Semantics
To illustrate the semantics for __CPROVER_is_fresh
, consider the following implementation of sum
function.
int *err_signal; // Global variable
void sum(const uint32_t a, const uint32_t b, uint32_t* out)
__CPROVER_requires(__CPROVER_is_fresh(out, sizeof(*out)))
__CPROVER_ensures(__CPROVER_is_fresh(err_signal, sizeof(*err_signal)))
__CPROVER_assigns(*out, err_signal)
{
const uint64_t result = ((uint64_t) a) + ((uint64_t) b);
err_signal = malloc(sizeof(*err_signal));
if (!err_signal) return;
if (result > UINT32_MAX) *err_signal = FAILURE;
*out = (uint32_t) result;
*err_signal = SUCCESS;
}
Enforcement
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.
int *err_signal; // Global variable
int __CPROVER_contracts_original_sum(const uint32_t a, const uint32_t b, uint32_t* out)
{
const uint64_t result = ((uint64_t) a) + ((uint64_t) b);
err_signal = malloc(sizeof(*err_signal));
if (!err_signal) return;
if (result > UINT32_MAX) *err_signal = FAILURE;
*out = (uint32_t) result;
*err_signal = SUCCESS;
}
/* Function Contract Enforcement */
int sum(const uint32_t a, const uint32_t b, uint32_t* out)
{
__CPROVER_assume(__CPROVER_is_fresh(out, sizeof(*out))); // Assumes out is freshly allocated
int return_value_sum = __CPROVER_contracts_original_sum(a, b, out);
__CPROVER_assert(__CPROVER_is_fresh(err_signal, sizeof(*err_signal)), "Check ensures clause");
return return_value_sum;
}
Replacement
In our example, consider that a function foo
may call sum
.
int *err_signal; // Global variable
int foo()
{
uint32_t a;
uint32_t b;
uint32_t out;
sum(a, b, &out);
return *err_signal;
}
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.
int *err_signal; // Global variable
int foo()
{
uint32_t a;
uint32_t b;
uint32_t out;
/* Function Contract Replacement */
/* Precondition */
__CPROVER_assert(__CPROVER_is_fresh(out, sizeof(*out)), "Check requires clause");
/* Writable Set */
*(&out) = nondet_uint32_t();
err_signal = nondet_int_pointer();
/* Postconditions */
__CPROVER_assume(__CPROVER_is_fresh(err_signal, sizeof(*err_signal))); // Assumes out is allocated
return *err_signal;
}
Last modified: 2022-09-29 15:10:20 -0400