Skip to content

The function contract doesn't work when using loop #6483

Closed
@wcventure

Description

@wcventure

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

Metadata

Metadata

Labels

Code ContractsFunction and loop contractsaws-highsoundnessSoundness bug? Review and add "aws" if it is, or remove "soundness" if it isn't.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions