-
Notifications
You must be signed in to change notification settings - Fork 269
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add new
__CPROVER_pointer_equals(p, q)
predicate
Meant to replace `p == q in contract clauses, works as a hook to override equality behaviour in assume contexts to make it constructive. Add TODOs to remember overriding built-in ptr equality with new predicate in requires/ensures clauses expressions and user-defined ptr predicates bodies.
- Loading branch information
Remi Delmas
committed
Jan 21, 2025
1 parent
a8f6aa4
commit e965339
Showing
26 changed files
with
615 additions
and
5 deletions.
There are no files selected for viewing
16 changes: 16 additions & 0 deletions
16
regression/contracts-dfcc/test_pointer_equals_enforce_requires_disjunction_fail/main.c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
void foo(int *x, int *y) | ||
// clang-format off | ||
__CPROVER_requires(__CPROVER_is_fresh(x, sizeof(int))) | ||
__CPROVER_requires(__CPROVER_pointer_equals(y, x) || 1) | ||
__CPROVER_assigns(*y) | ||
// clang-format on | ||
{ | ||
*y = 0; | ||
} | ||
|
||
void main() | ||
{ | ||
int *x; | ||
int *y; | ||
foo(x, y); | ||
} |
20 changes: 20 additions & 0 deletions
20
regression/contracts-dfcc/test_pointer_equals_enforce_requires_disjunction_fail/test.desc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
CORE dfcc-only | ||
main.c | ||
--dfcc main --enforce-contract foo | ||
^\[__CPROVER_contracts_car_set_insert.assertion.\d+\] line \d+ ptr NULL or writable up to size: (UNKNOWN|FAILURE)$ | ||
^\[__CPROVER_contracts_write_set_check_assignment.assertion.\d+\] line \d+ ptr NULL or writable up to size: (UNKNOWN|FAILURE)$ | ||
^\[foo.assigns.\d+\] line \d+ Check that \*y is assignable: (UNKNOWN|FAILURE)$ | ||
^\[foo.pointer_dereference.\d+\] line \d+ dereference failure: pointer NULL in \*y: (UNKNOWN|FAILURE)$ | ||
^\[foo.pointer_dereference.\d+\] line \d+ dereference failure: pointer invalid in \*y: (UNKNOWN|FAILURE)$ | ||
^\[foo.pointer_dereference.\d+\] line \d+ dereference failure: deallocated dynamic object in \*y: (UNKNOWN|FAILURE)$ | ||
^\[foo.pointer_dereference.\d+\] line \d+ dereference failure: dead object in \*y: (UNKNOWN|FAILURE)$ | ||
^\[foo.pointer_dereference.\d+\] line \d+ dereference failure: pointer outside object bounds in \*y: (UNKNOWN|FAILURE)$ | ||
^\[foo.pointer_dereference.\d+\] line \d+ dereference failure: invalid integer address in \*y: (UNKNOWN|FAILURE)$ | ||
^EXIT=10$ | ||
^SIGNAL=0$ | ||
^VERIFICATION FAILED$ | ||
-- | ||
-- | ||
This test checks that when `__CPROVER_pointer_equals` is used in disjunctions, | ||
the program accepts models where `__CPROVER_pointer_equals` evaluates to | ||
false and the target pointer remains invalid. |
34 changes: 34 additions & 0 deletions
34
regression/contracts-dfcc/test_pointer_equals_enforce_requires_disjunction_pass/main.c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
#include <stdlib.h> | ||
void foo(int *a, int *x, int *y) | ||
// clang-format off | ||
__CPROVER_requires(__CPROVER_is_fresh(a, sizeof(int))) | ||
__CPROVER_requires( | ||
(__CPROVER_pointer_equals(x,a) && y == NULL) || | ||
(x == NULL && __CPROVER_pointer_equals(y,a)) | ||
) | ||
__CPROVER_assigns(y == NULL: *x) | ||
__CPROVER_assigns(x == NULL: *y) | ||
// clang-format on | ||
{ | ||
if(y == NULL) | ||
{ | ||
assert(0); | ||
assert(__CPROVER_same_object(a, x)); | ||
*x = 0; | ||
} | ||
else | ||
{ | ||
assert(0); | ||
assert(x == NULL); | ||
assert(__CPROVER_same_object(a, y)); | ||
*y = 0; | ||
} | ||
} | ||
|
||
void main() | ||
{ | ||
int *a; | ||
int *x; | ||
int *y; | ||
foo(a, x, y); | ||
} |
32 changes: 32 additions & 0 deletions
32
regression/contracts-dfcc/test_pointer_equals_enforce_requires_disjunction_pass/test.desc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
CORE dfcc-only | ||
main.c | ||
--dfcc main --enforce-contract foo | ||
^\[foo.assertion.\d+\] line 15 assertion 0: FAILURE$ | ||
^\[foo.assertion.\d+\] line \d+ assertion __CPROVER_POINTER_OBJECT\(\(.*\)a\) == __CPROVER_POINTER_OBJECT\(\(.*\)x\): SUCCESS$ | ||
^\[foo.assigns.\d+\] line \d+ Check that \*x is assignable: SUCCESS$ | ||
^\[foo.pointer_dereference.\d+\] line \d+ dereference failure: pointer NULL in \*x: SUCCESS$ | ||
^\[foo.pointer_dereference.\d+\] line \d+ dereference failure: pointer invalid in \*x: SUCCESS$ | ||
^\[foo.pointer_dereference.\d+\] line \d+ dereference failure: deallocated dynamic object in \*x: SUCCESS$ | ||
^\[foo.pointer_dereference.\d+\] line \d+ dereference failure: dead object in \*x: SUCCESS$ | ||
^\[foo.pointer_dereference.\d+\] line \d+ dereference failure: pointer outside object bounds in \*x: SUCCESS$ | ||
^\[foo.pointer_dereference.\d+\] line \d+ dereference failure: invalid integer address in \*x: SUCCESS$ | ||
^\[foo.assertion.\d+\] line 21 assertion 0: FAILURE$ | ||
^\[foo.assertion.\d+\] line \d+ assertion x == \(\(.*\)NULL\): SUCCESS$ | ||
^\[foo.assertion.\d+\] line \d+ assertion __CPROVER_POINTER_OBJECT\(\(.*\)a\) == __CPROVER_POINTER_OBJECT\(\(.*\)y\): SUCCESS$ | ||
^\[foo.assigns.\d+\] line \d+ Check that \*y is assignable: SUCCESS$ | ||
^\[foo.pointer_dereference.\d+\] line \d+ dereference failure: pointer NULL in \*y: SUCCESS$ | ||
^\[foo.pointer_dereference.\d+\] line \d+ dereference failure: pointer invalid in \*y: SUCCESS$ | ||
^\[foo.pointer_dereference.\d+\] line \d+ dereference failure: deallocated dynamic object in \*y: SUCCESS$ | ||
^\[foo.pointer_dereference.\d+\] line \d+ dereference failure: dead object in \*y: SUCCESS$ | ||
^\[foo.pointer_dereference.\d+\] line \d+ dereference failure: pointer outside object bounds in \*y: SUCCESS$ | ||
^\[foo.pointer_dereference.\d+\] line \d+ dereference failure: invalid integer address in \*y: SUCCESS$ | ||
^EXIT=10$ | ||
^SIGNAL=0$ | ||
^VERIFICATION FAILED$ | ||
-- | ||
-- | ||
Illustrates the behaviour of `__CPROVER_pointer_equals` under disjunctions in assume contexts. | ||
The precondition of `foo` describes a disjunction of cases, either `x` equals `a` and `y` is null, | ||
or `x` is null and `y` equals `a`. The function `foo` branches on `y == NULL`. | ||
The test suceeds if the two `assert(0)` in `foo` are falsifiable, which which shows | ||
that both cases of the disjunction expressed in the requires clause are reachable. |
28 changes: 28 additions & 0 deletions
28
regression/contracts-dfcc/test_pointer_equals_replace_ensures_disjunction_fail/main.c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
|
||
typedef struct | ||
{ | ||
int *a; | ||
int *x; | ||
} ret_t; | ||
|
||
ret_t foo() | ||
// clang-format off | ||
__CPROVER_ensures(__CPROVER_is_fresh(__CPROVER_return_value.a, sizeof(int))) | ||
__CPROVER_ensures( | ||
__CPROVER_pointer_equals( | ||
__CPROVER_return_value.x, | ||
__CPROVER_return_value.a) || 1) | ||
// clang-format on | ||
; | ||
|
||
void bar() | ||
{ | ||
ret_t ret = foo(); | ||
int *x = ret.x; | ||
*x = 0; //expected to fail | ||
} | ||
|
||
void main() | ||
{ | ||
bar(); | ||
} |
17 changes: 17 additions & 0 deletions
17
regression/contracts-dfcc/test_pointer_equals_replace_ensures_disjunction_fail/test.desc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
CORE dfcc-only | ||
main.c | ||
--dfcc main --replace-call-with-contract foo | ||
^\[bar.pointer_dereference.\d+\] line \d+ dereference failure: pointer NULL in \*x: (UNKNOWN|FAILURE)$ | ||
^\[bar.pointer_dereference.\d+\] line \d+ dereference failure: pointer invalid in \*x: (UNKNOWN|FAILURE)$ | ||
^\[bar.pointer_dereference.\d+\] line \d+ dereference failure: deallocated dynamic object in \*x: (UNKNOWN|FAILURE)$ | ||
^\[bar.pointer_dereference.\d+\] line \d+ dereference failure: dead object in \*x: (UNKNOWN|FAILURE)$ | ||
^\[bar.pointer_dereference.\d+\] line \d+ dereference failure: pointer outside object bounds in \*x: (UNKNOWN|FAILURE)$ | ||
^\[bar.pointer_dereference.\d+\] line \d+ dereference failure: invalid integer address in \*x: (UNKNOWN|FAILURE)$ | ||
^EXIT=10$ | ||
^SIGNAL=0$ | ||
^VERIFICATION FAILED$ | ||
-- | ||
-- | ||
This test checks that when `__CPROVER_pointer_equals` is used in disjunctions, | ||
the program accepts models where `__CPROVER_pointer_equals` evaluates to | ||
false and the target pointer remains invalid. |
49 changes: 49 additions & 0 deletions
49
regression/contracts-dfcc/test_pointer_equals_replace_ensures_disjunction_pass/main.c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
#include <stdlib.h> | ||
typedef struct | ||
{ | ||
int *a; | ||
int *x; | ||
int *y; | ||
} ret_t; | ||
|
||
ret_t foo() | ||
// clang-format off | ||
__CPROVER_ensures(__CPROVER_is_fresh(__CPROVER_return_value.a, sizeof(int))) | ||
__CPROVER_ensures(( | ||
__CPROVER_pointer_equals( | ||
__CPROVER_return_value.x, | ||
__CPROVER_return_value.a) && | ||
__CPROVER_return_value.y == NULL | ||
) || ( | ||
__CPROVER_return_value.x == NULL && | ||
__CPROVER_pointer_equals( | ||
__CPROVER_return_value.y, | ||
__CPROVER_return_value.a))) | ||
// clang-format on | ||
; | ||
|
||
void bar() | ||
{ | ||
ret_t ret = foo(); | ||
int *a = ret.a; | ||
int *x = ret.x; | ||
int *y = ret.y; | ||
if(y == NULL) | ||
{ | ||
assert(0); | ||
assert(__CPROVER_same_object(x, a)); | ||
*x = 0; | ||
} | ||
else | ||
{ | ||
assert(0); | ||
assert(x == NULL); | ||
assert(__CPROVER_same_object(y, a)); | ||
*y = 0; | ||
} | ||
} | ||
|
||
void main() | ||
{ | ||
bar(); | ||
} |
32 changes: 32 additions & 0 deletions
32
regression/contracts-dfcc/test_pointer_equals_replace_ensures_disjunction_pass/test.desc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
CORE dfcc-only | ||
main.c | ||
--dfcc main --replace-call-with-contract foo | ||
^\[bar.assertion.\d+\] line 33 assertion 0: FAILURE$ | ||
^\[bar.assertion.\d+\] line \d+ assertion __CPROVER_POINTER_OBJECT\(\(.*\)x\) == __CPROVER_POINTER_OBJECT\(\(.*\)a\): SUCCESS$ | ||
^\[bar.assigns.\d+\] line \d+ Check that \*x is assignable: SUCCESS$ | ||
^\[bar.pointer_dereference.\d+\] line \d+ dereference failure: pointer NULL in \*x: SUCCESS$ | ||
^\[bar.pointer_dereference.\d+\] line \d+ dereference failure: pointer invalid in \*x: SUCCESS$ | ||
^\[bar.pointer_dereference.\d+\] line \d+ dereference failure: deallocated dynamic object in \*x: SUCCESS$ | ||
^\[bar.pointer_dereference.\d+\] line \d+ dereference failure: dead object in \*x: SUCCESS$ | ||
^\[bar.pointer_dereference.\d+\] line \d+ dereference failure: pointer outside object bounds in \*x: SUCCESS$ | ||
^\[bar.pointer_dereference.\d+\] line \d+ dereference failure: invalid integer address in \*x: SUCCESS$ | ||
^\[bar.assertion.\d+\] line 39 assertion 0: FAILURE$ | ||
^\[bar.assertion.\d+\] line \d+ assertion x == \(\(.*\)NULL\): SUCCESS$ | ||
^\[bar.assertion.\d+\] line \d+ assertion __CPROVER_POINTER_OBJECT\(\(.*\)y\) == __CPROVER_POINTER_OBJECT\(\(.*\)a\): SUCCESS$ | ||
^\[bar.assigns.\d+\] line \d+ Check that \*y is assignable: SUCCESS$ | ||
^\[bar.pointer_dereference.\d+\] line \d+ dereference failure: pointer NULL in \*y: SUCCESS$ | ||
^\[bar.pointer_dereference.\d+\] line \d+ dereference failure: pointer invalid in \*y: SUCCESS$ | ||
^\[bar.pointer_dereference.\d+\] line \d+ dereference failure: deallocated dynamic object in \*y: SUCCESS$ | ||
^\[bar.pointer_dereference.\d+\] line \d+ dereference failure: dead object in \*y: SUCCESS$ | ||
^\[bar.pointer_dereference.\d+\] line \d+ dereference failure: pointer outside object bounds in \*y: SUCCESS$ | ||
^\[bar.pointer_dereference.\d+\] line \d+ dereference failure: invalid integer address in \*y: SUCCESS$ | ||
^EXIT=10$ | ||
^SIGNAL=0$ | ||
^VERIFICATION FAILED$ | ||
-- | ||
-- | ||
Illustrates the behaviour of `__CPROVER_pointer_equals` under disjunctions in assume contexts. | ||
The postcondition of `foo` describes a disjunction of cases: either `x` equals `a` and `y` is null, | ||
or `x` is null and `y` equals `a`. The function `bar` branches on `y == NULL`. | ||
The test succeeds if the two `assert(0)` are falsifiable, which which shows that | ||
both cases of the disjunction expressed in the ensures clause of `foo` are reachable. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
25 changes: 25 additions & 0 deletions
25
src/goto-instrument/contracts/doc/developer/contracts-dev-spec-pointer-equals.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# Rewriting Calls to the __CPROVER_pointer_equals Predicate {#contracts-dev-spec-pointer-equals} | ||
|
||
Back to top @ref contracts-dev-spec | ||
|
||
@tableofcontents | ||
|
||
In goto programs encoding pre or post conditions (generated from the contract | ||
clauses) and in all user-defined functions, we simply replace calls to | ||
`__CPROVER_pointer_equals` with calls to the library implementation. | ||
|
||
```c | ||
__CPROVER_contracts_pointer_equals( | ||
void **ptr1, | ||
void *ptr2, | ||
__CPROVER_contracts_write_set_ptr_t write_set); | ||
``` | ||
This function implements the `__CPROVER_pointer_equals` behaviour in all | ||
possible contexts (contract checking vs replacement, requires vs ensures clause | ||
context, as described by the flags carried by the write set parameter). | ||
--- | ||
Prev | Next | ||
:-----|:------ | ||
@ref contracts-dev | @ref contracts-dev-spec-reminder |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.