Skip to content

Commit ec3be0f

Browse files
author
Remi Delmas
committed
CONTRACTS: hotfix for is_fresh performance
In assumption contexts, turn a control flow branch into an assert/assume pair and inline the malloc function in is_fresh, while removing the failure mode logic.
1 parent 4aceefd commit ec3be0f

File tree

6 files changed

+56
-33
lines changed
  • regression/contracts-dfcc
    • assigns-enforce-malloc-zero
    • assigns_replace_havoc_dependent_targets_fail
    • assigns_replace_havoc_dependent_targets_pass
    • function-pointer-contracts-enforce
    • memory-predicates-is-fresh-requires-max-malloc-size
  • src/ansi-c/library

6 files changed

+56
-33
lines changed
Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
CORE
22
main.c
33
--malloc-may-fail --malloc-fail-null --dfcc main --enforce-contract foo
4+
^\[__CPROVER_contracts_is_fresh.assertion.\d+] line \d+ __CPROVER_is_fresh requires size < __CPROVER_max_malloc_size: FAILURE$
45
^\[foo.assigns.\d+\] line \d+ Check that a\[\(signed long (long )?int\)i\] is assignable: SUCCESS$
56
^\[foo.assigns.\d+\] line \d+ Check that \*out is assignable: SUCCESS$
6-
^EXIT=0$
7+
^EXIT=10$
78
^SIGNAL=0$
8-
^VERIFICATION SUCCESSFUL$
9+
^VERIFICATION FAILED$
910
--
10-
This test checks assuming is_fresh(ptr, size) with a non-deterministic size
11-
does not result in spurious falsifications when checking assigns clauses.
12-
An implicit strict upper bound of __CPROVER_max_malloc_size is imposed on the size by
13-
__CPROVER_is_fresh which guarantees the absence of pointer overflow when computing
14-
the address `ptr + size`.
11+
This test checks that assuming is_fresh(ptr, size) with a non-deterministic size
12+
checks that size < __CPROVER_max_malloc_size and then assumes it.
13+
This guarantees that the address `ptr + size` can always be computed and
14+
represented without offset bits overflowing into the object bits in the pointer
15+
model used by CBMC.

regression/contracts-dfcc/assigns_replace_havoc_dependent_targets_fail/vect.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ void resize_vec(vect *v, size_t incr)
1111
// clang-format off
1212
__CPROVER_requires(
1313
__CPROVER_is_fresh(v, sizeof(vect)) &&
14-
__CPROVER_is_fresh(v->arr, v->size) &&
15-
0 < v->size && v->size <= __CPROVER_max_malloc_size &&
16-
0 < incr && incr < __CPROVER_max_malloc_size - v->size
14+
0 < v->size && v->size < __CPROVER_max_malloc_size &&
15+
0 < incr && incr < __CPROVER_max_malloc_size - v->size &&
16+
__CPROVER_is_fresh(v->arr, v->size)
1717
)
1818
__CPROVER_assigns(v->size, v->arr, __CPROVER_object_whole(v->arr))
1919
__CPROVER_frees(v->arr)
@@ -34,9 +34,9 @@ void resize_vec_incr10(vect *v)
3434
// clang-format off
3535
__CPROVER_requires(
3636
__CPROVER_is_fresh(v, sizeof(vect)) &&
37-
__CPROVER_is_fresh(v->arr, v->size) &&
38-
0 < v->size && v->size <= __CPROVER_max_malloc_size &&
39-
v->size + 10 < __CPROVER_max_malloc_size
37+
0 < v->size && v->size < __CPROVER_max_malloc_size &&
38+
v->size + 10 < __CPROVER_max_malloc_size &&
39+
__CPROVER_is_fresh(v->arr, v->size)
4040
)
4141
__CPROVER_assigns(v->size)
4242
__CPROVER_ensures(

regression/contracts-dfcc/assigns_replace_havoc_dependent_targets_pass/vect.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ void resize_vec(vect *v, size_t incr)
1111
// clang-format off
1212
__CPROVER_requires(
1313
__CPROVER_is_fresh(v, sizeof(vect)) &&
14+
0 < v->size && v->size < __CPROVER_max_malloc_size &&
1415
__CPROVER_is_fresh(v->arr, v->size) &&
15-
0 < v->size && v->size <= __CPROVER_max_malloc_size &&
1616
0 < incr && incr < __CPROVER_max_malloc_size - v->size
1717
)
1818
__CPROVER_assigns(v->size, v->arr, __CPROVER_object_whole(v->arr))
@@ -34,8 +34,8 @@ void resize_vec_incr10(vect *v)
3434
// clang-format off
3535
__CPROVER_requires(
3636
__CPROVER_is_fresh(v, sizeof(vect)) &&
37+
0 < v->size && v->size < __CPROVER_max_malloc_size &&
3738
__CPROVER_is_fresh(v->arr, v->size) &&
38-
0 < v->size && v->size <= __CPROVER_max_malloc_size &&
3939
v->size + 10 < __CPROVER_max_malloc_size
4040
)
4141
__CPROVER_assigns(*v, __CPROVER_object_whole(v->arr))

regression/contracts-dfcc/function-pointer-contracts-enforce/main.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ typedef void (*arr_fun_t)(char *arr, size_t size);
99
// resets the first element to zero
1010
void arr_fun_contract(char *arr, size_t size)
1111
// clang-format off
12-
__CPROVER_requires(size > 0 && __CPROVER_is_fresh(arr, size))
12+
__CPROVER_requires(0 < size && size < __CPROVER_max_malloc_size &&
13+
__CPROVER_is_fresh(arr, size))
1314
__CPROVER_assigns(arr[0])
1415
__CPROVER_ensures(arr[0] == 0)
1516
// clang-format on
@@ -20,7 +21,8 @@ __CPROVER_ensures(arr[0] == 0)
2021
// to establish post-conditions
2122
int foo(char *arr, size_t size, arr_fun_t arr_fun)
2223
// clang-format off
23-
__CPROVER_requires(arr == NULL || __CPROVER_is_fresh(arr, size))
24+
__CPROVER_requires(arr == NULL ||
25+
size < __CPROVER_max_malloc_size && __CPROVER_is_fresh(arr, size))
2426
__CPROVER_requires(__CPROVER_obeys_contract(arr_fun, arr_fun_contract))
2527
__CPROVER_assigns(arr && size > 0 : arr[0])
2628
__CPROVER_ensures((__CPROVER_return_value == 0) ==> (arr[0] == 0))

regression/contracts-dfcc/memory-predicates-is-fresh-requires-max-malloc-size/main.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
void foo(char *arr, size_t size)
44
// clang-format off
5-
__CPROVER_requires(__CPROVER_is_fresh(arr, size))
5+
__CPROVER_requires(size < __CPROVER_max_malloc_size &&
6+
__CPROVER_is_fresh(arr, size))
67
__CPROVER_assigns(__CPROVER_object_from(arr))
78
// clang-format on
89
{

src/ansi-c/library/cprover_contracts.c

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ extern __CPROVER_size_t __CPROVER_max_malloc_size;
1111
extern const void *__CPROVER_alloca_object;
1212
extern const void *__CPROVER_deallocated;
1313
extern const void *__CPROVER_new_object;
14+
extern const void *__CPROVER_memory_leak;
1415
extern __CPROVER_bool __CPROVER_malloc_is_new_array;
1516
int __builtin_clzll(unsigned long long);
1617
__CPROVER_bool __VERIFIER_nondet___CPROVER_bool();
@@ -1191,17 +1192,24 @@ __CPROVER_HIDE:;
11911192
(write_set->assert_ensures_ctx == 0),
11921193
"only one context flag at a time");
11931194
#endif
1194-
// fail if size is too big
1195-
if(size >= __CPROVER_max_malloc_size)
1196-
return 0;
1195+
__CPROVER_assert(
1196+
size < __CPROVER_max_malloc_size,
1197+
"__CPROVER_is_fresh requires size < __CPROVER_max_malloc_size");
1198+
__CPROVER_assume(size < __CPROVER_max_malloc_size);
11971199

1198-
// pass a null write set pointer to the instrumented malloc
1199-
void *ptr = __CPROVER_contracts_malloc(size, 0);
1200+
void *ptr = __CPROVER_allocate(size, 0);
12001201
*elem = ptr;
1201-
// malloc can also return a NULL pointer if failure modes are active
1202-
if(!ptr)
1203-
return 0;
1204-
// record fresh object in the object set
1202+
1203+
// record the object size for non-determistic bounds checking
1204+
__CPROVER_bool record_malloc = __VERIFIER_nondet___CPROVER_bool();
1205+
__CPROVER_malloc_is_new_array =
1206+
record_malloc ? 0 : __CPROVER_malloc_is_new_array;
1207+
1208+
// detect memory leaks
1209+
__CPROVER_bool record_may_leak = __VERIFIER_nondet___CPROVER_bool();
1210+
__CPROVER_memory_leak = record_may_leak ? ptr : __CPROVER_memory_leak;
1211+
1212+
// record fresh object in the object set
12051213
#ifdef DFCC_DEBUG
12061214
// manually inlined below
12071215
__CPROVER_contracts_obj_set_add(write_set->linked_is_fresh, ptr);
@@ -1226,13 +1234,24 @@ __CPROVER_HIDE:;
12261234
"only one context flag at a time");
12271235
#endif
12281236
// fail if size is too big
1229-
if(size >= __CPROVER_max_malloc_size)
1230-
return 0;
1231-
void *ptr = __CPROVER_contracts_malloc(size, 0);
1237+
__CPROVER_assert(
1238+
size < __CPROVER_max_malloc_size,
1239+
"__CPROVER_is_fresh requires size < __CPROVER_max_malloc_size");
1240+
__CPROVER_assume(size < __CPROVER_max_malloc_size);
1241+
1242+
void *ptr = __CPROVER_allocate(size, 0);
12321243
*elem = ptr;
1233-
if(!ptr)
1234-
return 0;
1235-
// record fresh object in the caller's write set
1244+
1245+
// record the object size for non-determistic bounds checking
1246+
__CPROVER_bool record_malloc = __VERIFIER_nondet___CPROVER_bool();
1247+
__CPROVER_malloc_is_new_array =
1248+
record_malloc ? 0 : __CPROVER_malloc_is_new_array;
1249+
1250+
// detect memory leaks
1251+
__CPROVER_bool record_may_leak = __VERIFIER_nondet___CPROVER_bool();
1252+
__CPROVER_memory_leak = record_may_leak ? ptr : __CPROVER_memory_leak;
1253+
1254+
// record fresh object in the caller's write set
12361255
#ifdef DFCC_DEBUG
12371256
__CPROVER_contracts_obj_set_add(write_set->linked_allocated, ptr);
12381257
#else

0 commit comments

Comments
 (0)