Skip to content

Function --enforce-contract causes global var precondition assertion to fail #8429

Open
@BeamRaceMuppet

Description

@BeamRaceMuppet

CBMC version: 6.1.1

Instrumenting a function with --enforce-contract that has in its contract a precondition that a global variable be set to a particular value prior to entry causes assertions that the value is correctly set prior to call to fail. Minimal reproducer:

#include <stdbool.h>

bool is_initialized = false;

// Avoid double-initialization by requiring is_initialized==false
void initialize(void)
__CPROVER_requires(is_initialized == false)
__CPROVER_ensures(is_initialized == true)
__CPROVER_assigns(is_initialized)
{
    is_initialized = true;
}

int main(void)
{
    __CPROVER_assert(is_initialized == false, "Precondition for initialize()");
    initialize();
    __CPROVER_assert(is_initialized == true, "Postcondition for initialize()");

    return 0;
}

CBMC is run like this:

goto-cc initialize.c -o initialize.goto && goto-instrument --enforce-contract initialize  initialize.goto initialize.inst.goto && cbmc initialize.inst.goto

Expected output: VERIFICATION SUCCESSFUL.

Observed output:

** Results:
initialize.c function initialize
[initialize.postcondition.1] line 8 Check ensures clause: SUCCESS
[initialize.assigns.1] line 9 Check that is_initialized is valid: SUCCESS
[initialize.assigns.2] line 11 Check that is_initialized is assignable: SUCCESS

initialize.c function main
[main.assertion.1] line 16 assertion is_initialized == false: FAILURE
[main.assertion.2] line 18 Postcondition for initialize(): SUCCESS

** 1 of 5 failed (2 iterations)
VERIFICATION FAILED

Note that simply running

    cbmc initialize.c

Results in success:

** Results:
initialize.c function main
[main.assertion.1] line 16 assertion is_initialized == false: SUCCESS
[main.assertion.2] line 18 Postcondition for initialize(): SUCCESS

Also, using --replace-call-with-contract instead of --enforce-contract like this:

    goto-cc initialize.c -o initialize.goto && goto-instrument --replace-call-with-contract initialize  initialize.goto initialize.inst.goto && cbmc --trace initialize.inst.goto

Also results in success:

initialize.c function main
[main.assertion.1] line 16 assertion is_initialized == false: SUCCESS
[main.assertion.2] line 18 Postcondition for initialize(): SUCCESS

But using both --replace-call-with-contract and --enforce-contract like this:

goto-cc initialize.c -o initialize.goto && goto-instrument --enforce-contract initialize --replace-call-with-contract initialize initialize.goto initialize.inst.goto && cbmc initialize.inst.goto

Still results in failure:

[initialize.precondition.1] line 17 Check requires clause of initialize in main: FAILURE

initialize.c function main
[main.assertion.1] line 16 assertion is_initialized == false: FAILURE
[main.assertion.2] line 18 Postcondition for initialize(): SUCCESS

If we add the line "is_initialized = false" right before the assertion it is false, like this:

int main(void)
{
    is_initialized = false;
    __CPROVER_assert(is_initialized == false, "Precondition for initialize()");
    [...]

Then using --enforce-contract will finally prove the precondition assertion:

initialize.c function main
[main.assertion.1] line 17 Precondition for initialize(): SUCCESS

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions