Skip to content

Commit

Permalink
Add tests for pointer predicates under disjunctions in assume contexts
Browse files Browse the repository at this point in the history
  • Loading branch information
Remi Delmas committed Jan 21, 2025
1 parent e979772 commit a8f6aa4
Show file tree
Hide file tree
Showing 16 changed files with 429 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
void foo(int *x)
// clang-format off
__CPROVER_requires(__CPROVER_is_fresh(x, sizeof(int)) || 1)
__CPROVER_assigns(*x)
// clang-format on
{
*x = 0;
}

void main()
{
int *x;
foo(x);
}
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: (FAILURE|UNKNOWN)$
^\[__CPROVER_contracts_write_set_check_assignment.assertion.\d+\] line \d+ ptr NULL or writable up to size: (FAILURE|UNKNOWN)$
^\[foo.assigns.\d+\] line \d+ Check that \*x is assignable: (FAILURE|UNKNOWN)$
^\[foo.pointer_dereference.\d+\] line \d+ dereference failure: pointer NULL in \*x: (FAILURE|UNKNOWN)
^\[foo.pointer_dereference.\d+\] line \d+ dereference failure: pointer invalid in \*x: (FAILURE|UNKNOWN)$
^\[foo.pointer_dereference.\d+\] line \d+ dereference failure: deallocated dynamic object in \*x: (FAILURE|UNKNOWN)
^\[foo.pointer_dereference.\d+\] line \d+ dereference failure: dead object in \*x: (FAILURE|UNKNOWN)
^\[foo.pointer_dereference.\d+\] line \d+ dereference failure: pointer outside object bounds in \*x: (FAILURE|UNKNOWN)
^\[foo.pointer_dereference.\d+\] line \d+ dereference failure: invalid integer address in \*x: (FAILURE|UNKNOWN)$
^EXIT=10$
^SIGNAL=0$
^VERIFICATION FAILED$
--
--
This test checks that when `__CPROVER_is_fresh` is used in disjunctions,
the program accepts models where `__CPROVER_is_fresh` evaluates to false
and no object gets allocated, and pointers remain invalid.
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#include <stdlib.h>
void foo(int *x, int *y)
// clang-format off
__CPROVER_requires(
(__CPROVER_is_fresh(x, sizeof(*x)) && y == NULL) ||
(x == NULL && __CPROVER_is_fresh(y, sizeof(*y)))
)
__CPROVER_assigns(y == NULL: *x)
__CPROVER_assigns(x == NULL: *y)
// clang-format on
{
if(y == NULL)
{
assert(0);
*x = 0;
}
else
{
assert(0);
assert(x == NULL);
*y = 0;
}
}

void main()
{
int *x;
int *y;
foo(x, y);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
CORE dfcc-only
main.c
--dfcc main --enforce-contract foo
^\[foo.assertion.\d+\] line 14 assertion 0: FAILURE$
^\[foo.assigns.\d+\] line 15 Check that \*x is assignable: SUCCESS$
^\[foo.pointer_dereference.\d+\] line 15 dereference failure: pointer NULL in \*x: SUCCESS$
^\[foo.pointer_dereference.\d+\] line 15 dereference failure: pointer invalid in \*x: SUCCESS$
^\[foo.pointer_dereference.\d+\] line 15 dereference failure: deallocated dynamic object in \*x: SUCCESS$
^\[foo.pointer_dereference.\d+\] line 15 dereference failure: dead object in \*x: SUCCESS$
^\[foo.pointer_dereference.\d+\] line 15 dereference failure: pointer outside object bounds in \*x: SUCCESS$
^\[foo.pointer_dereference.\d+\] line 15 dereference failure: invalid integer address in \*x: SUCCESS$
^\[foo.assertion.\d+\] line 19 assertion 0: FAILURE$
^\[foo.assertion.\d+\] line 20 assertion x == \(\(.*\)NULL\): SUCCESS$
^\[foo.assigns.\d+\] line 21 Check that \*y is assignable: SUCCESS$
^\[foo.pointer_dereference.\d+\] line 21 dereference failure: pointer NULL in \*y: SUCCESS$
^\[foo.pointer_dereference.\d+\] line 21 dereference failure: pointer invalid in \*y: SUCCESS$
^\[foo.pointer_dereference.\d+\] line 21 dereference failure: deallocated dynamic object in \*y: SUCCESS$
^\[foo.pointer_dereference.\d+\] line 21 dereference failure: dead object in \*y: SUCCESS$
^\[foo.pointer_dereference.\d+\] line 21 dereference failure: pointer outside object bounds in \*y: SUCCESS$
^\[foo.pointer_dereference.\d+\] line 21 dereference failure: invalid integer address in \*y: SUCCESS$
^EXIT=10$
^SIGNAL=0$
^VERIFICATION FAILED$
--
--
Illustrates the behaviour of `__CPROVER_is_fresh` under disjunctions in assume contexts.
The precondition of `foo` describes a disjunction of cases, either `x` is fresh and `y` is null,
or `x` is null and `y` is fresh. 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.
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
int *foo()
// clang-format off
__CPROVER_ensures(
__CPROVER_is_fresh(__CPROVER_return_value, sizeof(int)) || 1);
// clang-format on

void bar()
{
int *x = foo();
*x = 0; //expected to fail
}
void main()
{
bar();
}
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 10 dereference failure: pointer NULL in \*x: (UNKNOWN|FAILURE)$
^\[bar.pointer_dereference.\d+\] line 10 dereference failure: pointer invalid in \*x: (UNKNOWN|FAILURE)$
^\[bar.pointer_dereference.\d+\] line 10 dereference failure: deallocated dynamic object in \*x: (UNKNOWN|FAILURE)$
^\[bar.pointer_dereference.\d+\] line 10 dereference failure: dead object in \*x: (UNKNOWN|FAILURE)$
^\[bar.pointer_dereference.\d+\] line 10 dereference failure: pointer outside object bounds in \*x: (UNKNOWN|FAILURE)$
^\[bar.pointer_dereference.\d+\] line 10 dereference failure: invalid integer address in \*x: (UNKNOWN|FAILURE)$
^EXIT=10$
^SIGNAL=0$
^VERIFICATION FAILED$
--
--
This test checks that when `__CPROVER_is_fresh` is used in disjunctions,
the program accepts models where `__CPROVER_is_fresh` evaluates to false
and no object gets allocated, and pointers remain invalid.
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#include <stdlib.h>
typedef struct
{
int *x;
int *y;
} ret_t;

ret_t foo()
// clang-format off
__CPROVER_ensures((
__CPROVER_is_fresh(__CPROVER_return_value.x, sizeof(int)) &&
__CPROVER_return_value.y == NULL
) || (
__CPROVER_return_value.x == NULL &&
__CPROVER_is_fresh(__CPROVER_return_value.y, sizeof(int))
))
// clang-format on
;

void bar()
{
ret_t ret = foo();
int *x = ret.x;
int *y = ret.y;
if(y == NULL)
{
assert(0);
*x = 0;
}
else
{
assert(0);
assert(x == NULL);
*y = 0;
}
}

void main()
{
bar();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
CORE dfcc-only
main.c
--dfcc main --replace-call-with-contract foo
^\[bar.assertion.\d+\] line 27 assertion 0: FAILURE$
^\[bar.assigns.\d+\] line 28 Check that \*x is assignable: SUCCESS$
^\[bar.pointer_dereference.\d+\] line 28 dereference failure: pointer NULL in \*x: SUCCESS$
^\[bar.pointer_dereference.\d+\] line 28 dereference failure: pointer invalid in \*x: SUCCESS$
^\[bar.pointer_dereference.\d+\] line 28 dereference failure: deallocated dynamic object in \*x: SUCCESS$
^\[bar.pointer_dereference.\d+\] line 28 dereference failure: dead object in \*x: SUCCESS$
^\[bar.pointer_dereference.\d+\] line 28 dereference failure: pointer outside object bounds in \*x: SUCCESS$
^\[bar.pointer_dereference.\d+\] line 28 dereference failure: invalid integer address in \*x: SUCCESS$
^\[bar.assertion.\d+\] line 32 assertion 0: FAILURE$
^\[bar.assertion.\d+\] line 33 assertion x == \(\(.*\)NULL\): SUCCESS$
^\[bar.assigns.\d+\] line 34 Check that \*y is assignable: SUCCESS$
^\[bar.pointer_dereference.\d+\] line 34 dereference failure: pointer NULL in \*y: SUCCESS$
^\[bar.pointer_dereference.\d+\] line 34 dereference failure: pointer invalid in \*y: SUCCESS$
^\[bar.pointer_dereference.\d+\] line 34 dereference failure: deallocated dynamic object in \*y: SUCCESS$
^\[bar.pointer_dereference.\d+\] line 34 dereference failure: dead object in \*y: SUCCESS$
^\[bar.pointer_dereference.\d+\] line 34 dereference failure: pointer outside object bounds in \*y: SUCCESS$
^\[bar.pointer_dereference.\d+\] line 34 dereference failure: invalid integer address in \*y: SUCCESS$
^EXIT=10$
^SIGNAL=0$
^VERIFICATION FAILED$
--
--
Illustrates the behaviour of `__CPROVER_is_fresh` under disjunctions in assume contexts.
The postcondition of `foo` describes a disjunction of cases: either `x` is fresh and `y` is null,
or `x` is null and `y` is fresh. 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.
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, 8*sizeof(int)))
__CPROVER_requires(__CPROVER_pointer_in_range_dfcc(x, y, x+7) || 1)
__CPROVER_assigns(*y)
// clang-format on
{
*y = 0;
}

void main()
{
int *x;
int *y;
foo(x, y);
}
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.
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, 8*sizeof(int)))
__CPROVER_requires(
(__CPROVER_pointer_in_range_dfcc(a, x, a+7) && y == NULL) ||
(x == NULL && __CPROVER_pointer_in_range_dfcc(a, y, a+7))
)
__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);
}
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 16 assertion __CPROVER_POINTER_OBJECT\(\(.*\)a\) == __CPROVER_POINTER_OBJECT\(\(.*\)x\): SUCCESS$
^\[foo.assigns.\d+\] line 17 Check that \*x is assignable: SUCCESS$
^\[foo.pointer_dereference.\d+\] line 17 dereference failure: pointer NULL in \*x: SUCCESS$
^\[foo.pointer_dereference.\d+\] line 17 dereference failure: pointer invalid in \*x: SUCCESS$
^\[foo.pointer_dereference.\d+\] line 17 dereference failure: deallocated dynamic object in \*x: SUCCESS$
^\[foo.pointer_dereference.\d+\] line 17 dereference failure: dead object in \*x: SUCCESS$
^\[foo.pointer_dereference.\d+\] line 17 dereference failure: pointer outside object bounds in \*x: SUCCESS$
^\[foo.pointer_dereference.\d+\] line 17 dereference failure: invalid integer address in \*x: SUCCESS$
^\[foo.assertion.\d+\] line 21 assertion 0: FAILURE$
^\[foo.assertion.\d+\] line 22 assertion x == \(\(.*\)NULL\): SUCCESS$
^\[foo.assertion.\d+\] line 23 assertion __CPROVER_POINTER_OBJECT\(\(.*\)a\) == __CPROVER_POINTER_OBJECT\(\(.*\)y\): SUCCESS$
^\[foo.assigns.\d+\] line 24 Check that \*y is assignable: SUCCESS$
^\[foo.pointer_dereference.\d+\] line 24 dereference failure: pointer NULL in \*y: SUCCESS$
^\[foo.pointer_dereference.\d+\] line 24 dereference failure: pointer invalid in \*y: SUCCESS$
^\[foo.pointer_dereference.\d+\] line 24 dereference failure: deallocated dynamic object in \*y: SUCCESS$
^\[foo.pointer_dereference.\d+\] line 24 dereference failure: dead object in \*y: SUCCESS$
^\[foo.pointer_dereference.\d+\] line 24 dereference failure: pointer outside object bounds in \*y: SUCCESS$
^\[foo.pointer_dereference.\d+\] line 24 dereference failure: invalid integer address in \*y: SUCCESS$
^EXIT=10$
^SIGNAL=0$
^VERIFICATION FAILED$
--
--
Illustrates the behaviour of `__CPROVER_pointer_in_range_dfcc` under disjunctions in assume contexts.
The precondition of `foo` describes a disjunction of cases, either `x` is in range of `a` and `y` is null,
or `x` is null and `y` is in range of `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.
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@

typedef struct
{
int *a;
int *x;
} ret_t;

ret_t foo()
// clang-format off
__CPROVER_ensures(__CPROVER_is_fresh(__CPROVER_return_value.a, 8*sizeof(int)))
__CPROVER_ensures(
__CPROVER_pointer_in_range_dfcc(
__CPROVER_return_value.a,
__CPROVER_return_value.x,
__CPROVER_return_value.a + 7
) || 1)
// clang-format on
;

void bar()
{
ret_t ret = foo();
int *x = ret.x;
*x = 0; //expected to fail
}

void main()
{
bar();
}
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_in_range_dfcc` is used in disjunctions,
the program accepts models where `__CPROVER_pointer_in_range_dfcc` evaluates to
false and the target pointer remains invalid.
Loading

0 comments on commit a8f6aa4

Please sign in to comment.