Description
Hi there,
I followed the cprover-manual/contracts-functions.md and tried to use function contracts in my project. However, the function contracts don't seem to work correctly in some situations. Could you help me look at the following issue?
The foo
function contract doesn't work when using it in the loop body
I have written a function foo
that Iteratively checks whether the two arrays have the same elements. If yes, it returns 0, otherwise, it returns -1. And I try to use its function contract in the main
function. Unfortunately, I got an unexpected result that all wrong assertions are Judged as SUCCESS (When this function contract is not placed in the loop, the result is correct. But when it is put into a meaningless loop, the result is not correct).
//main.c
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <assert.h>
#define SIZE 10
int foo(uint32_t *map, uint32_t *key, uint32_t len)
/* Precondition */
__CPROVER_requires(len <= SIZE)
__CPROVER_requires(__CPROVER_is_fresh(map, sizeof(uint32_t)*len))
__CPROVER_requires(__CPROVER_is_fresh(key, sizeof(uint32_t)*len))
/* Postconditions */
__CPROVER_ensures(__CPROVER_return_value == 0 || __CPROVER_return_value == -1)
__CPROVER_ensures(__CPROVER_return_value == -1 ==> __CPROVER_forall {int i; (0 <= i && i < len) ==> map[i] != key[i]})
__CPROVER_ensures(__CPROVER_return_value == 0 ==> __CPROVER_exists {int i; (0 <= i && i < len) && map[i] == key[i]})
{
int ret = -1;
for (int i = 0; i < len; i++)
__CPROVER_assigns(i)
__CPROVER_loop_invariant(0 <= i && i <= len)
__CPROVER_loop_invariant(ret == -1 || ret == 0)
__CPROVER_loop_invariant((ret == -1)==> __CPROVER_forall {int j; (0 <= j && j < len) ==> ( j < i ==> map[j] != key[j])})
__CPROVER_loop_invariant((ret == 0) ==> map[i] == key[i])
__CPROVER_decreases(len - i)
{
if (map[i] == key[i])
{
ret = 0;
break;
}
}
return ret;
}
int main()
{
uint32_t *map = malloc(sizeof(uint32_t)*SIZE);
uint32_t *key = malloc(sizeof(uint32_t)*SIZE);
memset(map, 0, sizeof(uint32_t)*SIZE);
memset(key, 0, sizeof(uint32_t)*SIZE);
key[0] = 1;
map[0] = 2;
int ret;
for (int i = 1; i < SIZE; i++) {
ret = foo(map, key, i);
}
assert(ret == 0);
assert(ret == -1);
assert(0);
}
You can use the following command to see the ensures clause failure.
goto-cc -o main.goto main.c
goto-instrument --replace-call-with-contract foo main.goto main-using-foo-contract.goto
cbmc main-using-foo-contract.goto
Then you will see all assertion success unexpectedly. The assert(0) doesn't fail which also means that the function exit is not reachable. However, if you change the loop body for (int i = 1; i < SIZE; i++) { ret = foo(map, key, i); }
to ret = foo(map, key, SIZE);
, the result become correct.
** Results:
[precondition.1] Check requires clause: FAILURE
/mnt/e/Github-Bitbucket/CBMC_Frama-C_By_examples/CBMCExamples/foo_and_sum_contract/loop_contract.c function main
[main.precondition_instance.1] line 41 memset destination region writeable: SUCCESS
[main.precondition_instance.2] line 42 memset destination region writeable: SUCCESS
[main.assertion.1] line 50 assertion ret == 0: SUCCESS
[main.assertion.2] line 51 assertion ret == -1: SUCCESS
[main.assertion.3] line 52 assertion 0: SUCCESS
<builtin-library-malloc> function malloc
[malloc.assertion.1] line 26 max allocation size exceeded: SUCCESS
[malloc.assertion.2] line 31 max allocation may fail: SUCCESS
** 1 of 8 failed (2 iterations)
VERIFICATION FAILED