Skip to content

Commit d4a1c6d

Browse files
Remi Delmasremi-delmas-3000
Remi Delmas
authored andcommitted
CONTRACTS: is_fresh now checks separation of byte-intervals instead of whole objects.
When assumed, is_fresh still builds distinct objects. When asserted, it allows for either distinct objects, or distinct byte intervals within the same object. A function foo(int *a, int *b) that requires is_fresh(a) and is_fresh(b) is checked under the assumption that a and b are distinct objects, but can still be used in contexts where a and b are distinct slices within the same base object. This is sound because the function is checked under the stronger precondition and hence is proved to not perform any operation that requires that a and b be in the same object, such as pointer differences or comparisons.
1 parent 159af34 commit d4a1c6d

File tree

10 files changed

+214
-23
lines changed

10 files changed

+214
-23
lines changed

regression/contracts-dfcc/memory-predicates-is-fresh-failure-modes/test-fail-none.desc

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
CORE dfcc-only
22
main.c
3-
--no-malloc-may-fail --dfcc main --enforce-contract foo _ --pointer-check --pointer-primitive-check --pointer-overflow-check
4-
^\[__CPROVER_contracts_car_set_insert.assertion.\d+\] line \d+ CAR size is less than __CPROVER_max_malloc_size: FAILURE$
3+
--no-malloc-may-fail --dfcc main --enforce-contract foo
54
^\[foo.assertion.\d+\] line \d+ size is capped: FAILURE$
6-
^\*\* 2 of \d+ failed
75
^EXIT=10$
86
^SIGNAL=0$
97
^VERIFICATION FAILED$
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#include <stdlib.h>
2+
int nondet_int();
3+
4+
void foo(int *a, int **b_out, int **c_out)
5+
// clang-format off
6+
__CPROVER_requires(__CPROVER_is_fresh(a, 2*sizeof(int)))
7+
__CPROVER_requires(__CPROVER_is_fresh(b_out, sizeof(int*)))
8+
__CPROVER_requires(__CPROVER_is_fresh(c_out, sizeof(int*)))
9+
__CPROVER_ensures(__CPROVER_is_fresh(*b_out, sizeof(int)))
10+
__CPROVER_ensures(__CPROVER_is_fresh(*c_out, sizeof(int)))
11+
__CPROVER_assigns(*b_out, *c_out)
12+
__CPROVER_ensures(**b_out == a[0])
13+
__CPROVER_ensures(**c_out == a[1])
14+
// clang-format on
15+
{
16+
if(nondet_int())
17+
{
18+
*b_out = malloc(sizeof(int));
19+
__CPROVER_assume(*b_out != NULL);
20+
*c_out = malloc(sizeof(int));
21+
__CPROVER_assume(*c_out != NULL);
22+
}
23+
else
24+
{
25+
*b_out = malloc(2 * sizeof(int));
26+
__CPROVER_assume(*b_out != NULL);
27+
*c_out = *b_out; // not separated, expect failure
28+
}
29+
**b_out = a[0];
30+
**c_out = a[1];
31+
}
32+
33+
int main()
34+
{
35+
int *a, **b_out, **c_out;
36+
foo(a, b_out, c_out);
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CORE dfcc-only
2+
main.c
3+
--dfcc main --enforce-contract foo
4+
^\[foo.postcondition.\d+\].*Check ensures clause of contract contract::foo for function foo: FAILURE$
5+
^EXIT=10$
6+
^SIGNAL=0$
7+
^VERIFICATION FAILED$
8+
--
9+
--
10+
This test checks that when __CPROVER_is_fresh is asserted in ensures clauses
11+
in contract checking mode, it detects byte interval separation failure
12+
within the same object.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#include <stdlib.h>
2+
int nondet_int();
3+
4+
void foo(int *a, int **b_out, int **c_out)
5+
// clang-format off
6+
__CPROVER_requires(__CPROVER_is_fresh(a, 2*sizeof(int)))
7+
__CPROVER_requires(__CPROVER_is_fresh(b_out, sizeof(int*)))
8+
__CPROVER_requires(__CPROVER_is_fresh(c_out, sizeof(int*)))
9+
__CPROVER_ensures(__CPROVER_is_fresh(*b_out, sizeof(int)))
10+
__CPROVER_ensures(__CPROVER_is_fresh(*c_out, sizeof(int)))
11+
__CPROVER_assigns(*b_out, *c_out)
12+
__CPROVER_ensures(**b_out == a[0])
13+
__CPROVER_ensures(**c_out == a[1])
14+
// clang-format on
15+
{
16+
if(nondet_int())
17+
{
18+
*b_out = malloc(sizeof(int));
19+
__CPROVER_assume(*b_out != NULL);
20+
*c_out = malloc(sizeof(int));
21+
__CPROVER_assume(*c_out != NULL);
22+
}
23+
else
24+
{
25+
*b_out = malloc(2 * sizeof(int));
26+
__CPROVER_assume(*b_out != NULL);
27+
*c_out = *b_out + 1;
28+
}
29+
**b_out = a[0];
30+
**c_out = a[1];
31+
}
32+
33+
int main()
34+
{
35+
int *a, **b_out, **c_out;
36+
foo(a, b_out, c_out);
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CORE dfcc-only
2+
main.c
3+
--dfcc main --enforce-contract foo
4+
^\[foo.postcondition.\d+\].*Check ensures clause of contract contract::foo for function foo: SUCCESS$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
^VERIFICATION SUCCESSFUL$
8+
--
9+
--
10+
This test checks that when __CPROVER_is_fresh is asserted in ensures clauses
11+
in contract checking mode, it allows both objet level separation and byte
12+
interval separation within the same object.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#include <stdlib.h>
2+
3+
void foo(int *a, int *b)
4+
// clang-format off
5+
__CPROVER_requires(__CPROVER_is_fresh(a, 3*sizeof(int)))
6+
__CPROVER_requires(__CPROVER_is_fresh(b, 3*sizeof(int)))
7+
__CPROVER_assigns(__CPROVER_object_upto(a, 3*sizeof(int)))
8+
__CPROVER_ensures(a[0] == b[0])
9+
__CPROVER_ensures(a[1] == b[1])
10+
__CPROVER_ensures(a[2] == b[2])
11+
;
12+
13+
int nondet_int();
14+
15+
void bar()
16+
{
17+
int a[6];
18+
int b[3];
19+
// c is either either a slice of `a` that overlaps a[0..2] or `b`
20+
int *c = nondet_int() ? &a[0] + 2: &b[0];
21+
int old_c0 = c[0];
22+
int old_c1 = c[1];
23+
int old_c2 = c[2];
24+
foo(a, c); // failure of preconditions
25+
__CPROVER_assert(a[0] == c[0], "same value 0");
26+
__CPROVER_assert(a[1] == c[1], "same value 1");
27+
__CPROVER_assert(a[2] == c[2], "same value 2");
28+
__CPROVER_assert(old_c0 == c[0], "unmodified 0");
29+
__CPROVER_assert(old_c1 == c[1], "unmodified 1");
30+
__CPROVER_assert(old_c2 == c[2], "unmodified 2");
31+
}
32+
33+
int main()
34+
{
35+
bar();
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
CORE dfcc-only
2+
main.c
3+
--dfcc main --enforce-contract bar --replace-call-with-contract foo
4+
^\[bar.assertion.\d+\].* unmodified 0: FAILURE$
5+
^\[foo.precondition.\d+\].*Check requires clause of contract contract::foo for function foo: FAILURE$
6+
^EXIT=10$
7+
^SIGNAL=0$
8+
^VERIFICATION FAILED$
9+
--
10+
--
11+
This test checks that when __CPROVER_is_fresh is asserted in requires clauses
12+
in contract replacement mode, it detects byte interval separation failure within
13+
the same object.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#include <stdlib.h>
2+
3+
void foo(int *a, int *b)
4+
// clang-format off
5+
__CPROVER_requires(__CPROVER_is_fresh(a, 3*sizeof(int)))
6+
__CPROVER_requires(__CPROVER_is_fresh(b, 3*sizeof(int)))
7+
__CPROVER_assigns(__CPROVER_object_upto(a, 3*sizeof(int)))
8+
__CPROVER_ensures(a[0] == b[0])
9+
__CPROVER_ensures(a[1] == b[1])
10+
__CPROVER_ensures(a[2] == b[2])
11+
;
12+
13+
int nondet_int();
14+
15+
void bar()
16+
{
17+
int a[6];
18+
int b[3];
19+
// c is either either a slice of `a` disjoint from a[0..2] or `b`
20+
int *c = nondet_int() ? &a[0] + 3: &b[0];
21+
int old_c0 = c[0];
22+
int old_c1 = c[1];
23+
int old_c2 = c[2];
24+
foo(a, c); // success of preconditions
25+
__CPROVER_assert(a[0] == c[0], "same value 0");
26+
__CPROVER_assert(a[1] == c[1], "same value 1");
27+
__CPROVER_assert(a[2] == c[2], "same value 2");
28+
__CPROVER_assert(old_c0 == c[0], "unmodified 0");
29+
__CPROVER_assert(old_c1 == c[1], "unmodified 1");
30+
__CPROVER_assert(old_c2 == c[2], "unmodified 2");
31+
}
32+
33+
int main()
34+
{
35+
bar();
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CORE dfcc-only
2+
main.c
3+
--dfcc main --enforce-contract bar --replace-call-with-contract foo _ --z3 --slice-formula
4+
^\[foo.precondition.\d+\].*Check requires clause of contract contract::foo for function foo: SUCCESS$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
^VERIFICATION SUCCESSFUL$
8+
--
9+
--
10+
This test checks that when __CPROVER_is_fresh in preconditions replacement checks
11+
succeed when separation and size are as expected.

src/ansi-c/library/cprover_contracts.c

+19-20
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,7 @@ typedef __CPROVER_contracts_obj_set_t *__CPROVER_contracts_obj_set_ptr_t;
7272
/// pointer equals, pointer_in_range_dfcc, pointer_is_fresh, obeys_contract.
7373
typedef struct
7474
{
75-
/// \brief Nondet variable ranging over the set of objects allocated
76-
/// by __CPROVER_contracts_is_fresh. Used to check separation constraints
77-
/// in __CPROVER_contracts_is_fresh.
78-
void *fresh_ptr;
79-
/// \brief Nondet variable ranging over the set of locations storing
80-
/// pointers on which predicates were assumed/asserted. Used to ensure
81-
/// that at most one predicate is assumed per pointer.
75+
__CPROVER_contracts_car_t fresh_car;
8276
void **ptr_pred;
8377
} __CPROVER_contracts_ptr_pred_ctx_t;
8478

@@ -419,7 +413,8 @@ void __CPROVER_contracts_ptr_pred_ctx_init(
419413
__CPROVER_contracts_ptr_pred_ctx_ptr_t set)
420414
{
421415
__CPROVER_HIDE:;
422-
set->fresh_ptr = (void *)0;
416+
set->fresh_car = (__CPROVER_contracts_car_t){
417+
.is_writable = 0, .size = 0, .lb = (void *)0, .ub = (void *)0};
423418
set->ptr_pred = (void **)0;
424419
}
425420

@@ -1345,10 +1340,10 @@ __CPROVER_HIDE:;
13451340
__VERIFIER_nondet___CPROVER_bool()
13461341
? elem
13471342
: write_set->linked_ptr_pred_ctx->ptr_pred;
1348-
write_set->linked_ptr_pred_ctx->fresh_ptr =
1343+
write_set->linked_ptr_pred_ctx->fresh_car =
13491344
__VERIFIER_nondet___CPROVER_bool()
1350-
? ptr
1351-
: write_set->linked_ptr_pred_ctx->fresh_ptr;
1345+
? __CPROVER_contracts_car_create(ptr, size)
1346+
: write_set->linked_ptr_pred_ctx->fresh_car;
13521347

13531348
// record the object size for non-determistic bounds checking
13541349
__CPROVER_bool record_malloc = __VERIFIER_nondet___CPROVER_bool();
@@ -1403,10 +1398,10 @@ __CPROVER_HIDE:;
14031398
__VERIFIER_nondet___CPROVER_bool()
14041399
? elem
14051400
: write_set->linked_ptr_pred_ctx->ptr_pred;
1406-
write_set->linked_ptr_pred_ctx->fresh_ptr =
1401+
write_set->linked_ptr_pred_ctx->fresh_car =
14071402
__VERIFIER_nondet___CPROVER_bool()
1408-
? ptr
1409-
: write_set->linked_ptr_pred_ctx->fresh_ptr;
1403+
? __CPROVER_contracts_car_create(ptr, size)
1404+
: write_set->linked_ptr_pred_ctx->fresh_car;
14101405

14111406
// record the object size for non-determistic bounds checking
14121407
__CPROVER_bool record_malloc = __VERIFIER_nondet___CPROVER_bool();
@@ -1440,11 +1435,15 @@ __CPROVER_HIDE:;
14401435
(write_set->assume_ensures_ctx == 0),
14411436
"only one context flag at a time");
14421437
#endif
1438+
// check separation
14431439
void *ptr = *elem;
1440+
__CPROVER_contracts_car_t car = __CPROVER_contracts_car_create(ptr, size);
1441+
__CPROVER_contracts_car_t fresh_car =
1442+
write_set->linked_ptr_pred_ctx->fresh_car;
14441443
if(
1445-
ptr != (void *)0 &&
1446-
!__CPROVER_same_object(write_set->linked_ptr_pred_ctx->fresh_ptr, ptr) &&
1447-
__CPROVER_r_ok(ptr, size))
1444+
ptr != (void *)0 && __CPROVER_r_ok(ptr, size) &&
1445+
(!__CPROVER_same_object(car.lb, fresh_car.lb) ||
1446+
(car.ub <= fresh_car.lb) || (fresh_car.ub <= car.lb)))
14481447
{
14491448
__CPROVER_assert(
14501449
write_set->linked_ptr_pred_ctx->ptr_pred != elem,
@@ -1454,10 +1453,10 @@ __CPROVER_HIDE:;
14541453
__VERIFIER_nondet___CPROVER_bool()
14551454
? elem
14561455
: write_set->linked_ptr_pred_ctx->ptr_pred;
1457-
write_set->linked_ptr_pred_ctx->fresh_ptr =
1456+
write_set->linked_ptr_pred_ctx->fresh_car =
14581457
__VERIFIER_nondet___CPROVER_bool()
1459-
? ptr
1460-
: write_set->linked_ptr_pred_ctx->fresh_ptr;
1458+
? car
1459+
: write_set->linked_ptr_pred_ctx->fresh_car;
14611460
return 1;
14621461
}
14631462
return 0;

0 commit comments

Comments
 (0)