Description
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