Skip to content

Commit 3d79560

Browse files
authored
Merge pull request #8574 from remi-delmas-3000/contracts-predicates-units-no-fail
CONTRATCS: force success for necessary pointer predicates
2 parents d4757e2 + 3ddd4b6 commit 3d79560

File tree

9 files changed

+171
-20
lines changed

9 files changed

+171
-20
lines changed

regression/contracts-dfcc/quantifiers-loops-fresh-bound-vars-smt/test.desc

+1-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FUTURE dfcc-only smt-backend broken-cprover-smt-backend
1+
CORE dfcc-only smt-backend broken-cprover-smt-backend
22
main.c
33
--dfcc main --apply-loop-contracts --enforce-contract foo --malloc-may-fail --malloc-fail-null _ --z3 --slice-formula --no-standard-checks
44
^EXIT=0$
@@ -8,11 +8,6 @@ main.c
88
^warning: ignoring
99
--
1010

11-
Marked as FUTURE because:
12-
- z3 >v4.12 and up can solve the problem with `--dfcc-simple-invalid-pointer-model`,
13-
but the CI uses older versions;
14-
- bitwuzla >v0.6 can solve the problem but we don't run bitwuzla in CI.
15-
1611
Tests support for quantifiers in loop contracts with the SMT backend.
1712
When quantified loop invariants are used, they are inserted three times
1813
in the transformed program (base case assertion, step case assumption,

src/ansi-c/library/cprover_contracts.c

+17-9
Original file line numberDiff line numberDiff line change
@@ -1166,6 +1166,7 @@ void __CPROVER_contracts_make_invalid_pointer(void **ptr)
11661166
///
11671167
/// \param ptr1 First argument of the `pointer_equals` predicate
11681168
/// \param ptr2 Second argument of the `pointer_equals` predicate
1169+
/// \param may_fail Allow predicate to fail in assume mode
11691170
/// \param write_set Write set which conveys the invocation context
11701171
/// (requires/ensures clause, assert/assume context);
11711172
///
@@ -1179,6 +1180,7 @@ void __CPROVER_contracts_make_invalid_pointer(void **ptr)
11791180
__CPROVER_bool __CPROVER_contracts_pointer_equals(
11801181
void **ptr1,
11811182
void *ptr2,
1183+
__CPROVER_bool may_fail,
11821184
__CPROVER_contracts_write_set_ptr_t write_set)
11831185
{
11841186
__CPROVER_HIDE:;
@@ -1195,7 +1197,8 @@ __CPROVER_HIDE:;
11951197
#endif
11961198
if(write_set->assume_requires_ctx | write_set->assume_ensures_ctx)
11971199
{
1198-
if(__VERIFIER_nondet___CPROVER_bool())
1200+
// SOUNDNESS: allow predicate to fail
1201+
if(may_fail && __VERIFIER_nondet___CPROVER_bool())
11991202
{
12001203
__CPROVER_contracts_make_invalid_pointer(ptr1);
12011204
return 0;
@@ -1224,8 +1227,9 @@ __CPROVER_HIDE:;
12241227
/// The behaviour depends on the boolean flags carried by \p write_set
12251228
/// which reflect the invocation context: checking vs. replacing a contract,
12261229
/// in a requires or an ensures clause context.
1227-
/// \param elem First argument of the `is_fresh` predicate
1228-
/// \param size Second argument of the `is_fresh` predicate
1230+
/// \param elem Pointer to the target pointer of the check
1231+
/// \param size Size to check for
1232+
/// \param may_fail Allow predicate to fail in assume mode
12291233
/// \param write_set Write set in which seen/allocated objects are recorded;
12301234
///
12311235
/// \details The behaviour is as follows:
@@ -1242,6 +1246,7 @@ __CPROVER_HIDE:;
12421246
__CPROVER_bool __CPROVER_contracts_is_fresh(
12431247
void **elem,
12441248
__CPROVER_size_t size,
1249+
__CPROVER_bool may_fail,
12451250
__CPROVER_contracts_write_set_ptr_t write_set)
12461251
{
12471252
__CPROVER_HIDE:;
@@ -1289,7 +1294,7 @@ __CPROVER_HIDE:;
12891294
}
12901295

12911296
// SOUNDNESS: allow predicate to fail
1292-
if(__VERIFIER_nondet___CPROVER_bool())
1297+
if(may_fail && __VERIFIER_nondet___CPROVER_bool())
12931298
{
12941299
__CPROVER_contracts_make_invalid_pointer(elem);
12951300
return 0;
@@ -1349,7 +1354,7 @@ __CPROVER_HIDE:;
13491354
}
13501355

13511356
// SOUNDNESS: allow predicate to fail
1352-
if(__VERIFIER_nondet___CPROVER_bool())
1357+
if(may_fail && __VERIFIER_nondet___CPROVER_bool())
13531358
{
13541359
__CPROVER_contracts_make_invalid_pointer(elem);
13551360
return 0;
@@ -1436,6 +1441,7 @@ __CPROVER_HIDE:;
14361441
/// \param lb Lower bound pointer
14371442
/// \param ptr Target pointer of the predicate
14381443
/// \param ub Upper bound pointer
1444+
/// \param may_fail Allow predicate to fail in assume mode
14391445
/// \param write_set Write set in which seen/allocated objects are recorded;
14401446
///
14411447
/// \details The behaviour is as follows:
@@ -1449,6 +1455,7 @@ __CPROVER_bool __CPROVER_contracts_pointer_in_range_dfcc(
14491455
void *lb,
14501456
void **ptr,
14511457
void *ub,
1458+
__CPROVER_bool may_fail,
14521459
__CPROVER_contracts_write_set_ptr_t write_set)
14531460
{
14541461
__CPROVER_HIDE:;
@@ -1470,9 +1477,9 @@ __CPROVER_HIDE:;
14701477
lb_offset <= ub_offset, "lb and ub pointers must be ordered");
14711478
if(write_set->assume_requires_ctx | write_set->assume_ensures_ctx)
14721479
{
1473-
if(__VERIFIER_nondet___CPROVER_bool())
1480+
// SOUNDNESS: allow predicate to fail
1481+
if(may_fail && __VERIFIER_nondet___CPROVER_bool())
14741482
{
1475-
// SOUNDNESS: allow predicate to fail
14761483
__CPROVER_contracts_make_invalid_pointer(ptr);
14771484
return 0;
14781485
}
@@ -1647,6 +1654,7 @@ __CPROVER_HIDE:;
16471654
__CPROVER_bool __CPROVER_contracts_obeys_contract(
16481655
void (**function_pointer)(void),
16491656
void (*contract)(void),
1657+
__CPROVER_bool may_fail,
16501658
__CPROVER_contracts_write_set_ptr_t set)
16511659
{
16521660
__CPROVER_HIDE:;
@@ -1657,8 +1665,8 @@ __CPROVER_HIDE:;
16571665
"__CPROVER_obeys_contract is used only in requires or ensures clauses");
16581666
if((set->assume_requires_ctx == 1) | (set->assume_ensures_ctx == 1))
16591667
{
1660-
// decide if predicate must hold
1661-
if(__VERIFIER_nondet___CPROVER_bool())
1668+
// SOUDNESS: allow predicate to fail
1669+
if(may_fail && __VERIFIER_nondet___CPROVER_bool())
16621670
return 0;
16631671

16641672
// must hold, assign the function pointer to the contract function

src/goto-instrument/contracts/dynamic-frames/dfcc_is_cprover_symbol.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -156,3 +156,11 @@ bool dfcc_is_cprover_static_symbol(const irep_idt &id)
156156
// going_to variables converted from goto statements
157157
has_prefix(id2string(id), CPROVER_PREFIX "going_to");
158158
}
159+
160+
bool dfcc_is_cprover_pointer_predicate(const irep_idt &id)
161+
{
162+
return id == CPROVER_PREFIX "pointer_equals" ||
163+
id == CPROVER_PREFIX "is_fresh" ||
164+
id == CPROVER_PREFIX "pointer_in_range_dfcc" ||
165+
id == CPROVER_PREFIX "obeys_contract";
166+
}

src/goto-instrument/contracts/dynamic-frames/dfcc_is_cprover_symbol.h

+3
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,7 @@ bool dfcc_is_cprover_function_symbol(const irep_idt &id);
2424
/// auto-generated object following a pointer dereference.
2525
bool dfcc_is_cprover_static_symbol(const irep_idt &id);
2626

27+
/// Returns `true` iff the symbol is one of the CPROVER pointer predicates
28+
bool dfcc_is_cprover_pointer_predicate(const irep_idt &id);
29+
2730
#endif

src/goto-instrument/contracts/dynamic-frames/dfcc_is_fresh.cpp

+9-2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ Date: August 2022
1111

1212
#include <util/cprover_prefix.h>
1313
#include <util/pointer_expr.h>
14+
#include <util/prefix.h>
15+
#include <util/suffix.h>
1416
#include <util/symbol.h>
1517

1618
#include "dfcc_cfg_info.h"
@@ -50,8 +52,7 @@ void dfcc_is_fresht::rewrite_calls(
5052
if(function.id() == ID_symbol)
5153
{
5254
const irep_idt &fun_name = to_symbol_expr(function).get_identifier();
53-
54-
if(fun_name == CPROVER_PREFIX "is_fresh")
55+
if(has_prefix(id2string(fun_name), CPROVER_PREFIX "is_fresh"))
5556
{
5657
// add address on first operand
5758
target->call_arguments()[0] =
@@ -61,6 +62,12 @@ void dfcc_is_fresht::rewrite_calls(
6162
to_symbol_expr(target->call_function())
6263
.set_identifier(library.dfcc_fun_symbol[dfcc_funt::IS_FRESH].name);
6364

65+
// pass the may_fail flag
66+
if(function.source_location().get_bool("no_fail"))
67+
target->call_arguments().push_back(false_exprt());
68+
else
69+
target->call_arguments().push_back(true_exprt());
70+
6471
// pass the write_set
6572
target->call_arguments().push_back(cfg_info.get_write_set(target));
6673
}

src/goto-instrument/contracts/dynamic-frames/dfcc_obeys_contract.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Date: August 2022
1212
#include <util/cprover_prefix.h>
1313
#include <util/pointer_expr.h>
1414
#include <util/prefix.h>
15+
#include <util/suffix.h>
1516
#include <util/symbol.h>
1617

1718
#include <langapi/language_util.h>
@@ -87,6 +88,12 @@ void dfcc_obeys_contractt::rewrite_calls(
8788
.set_identifier(
8889
library.dfcc_fun_symbol[dfcc_funt::OBEYS_CONTRACT].name);
8990

91+
// pass the may_fail flag
92+
if(function.source_location().get_bool("no_fail"))
93+
target->call_arguments().push_back(false_exprt());
94+
else
95+
target->call_arguments().push_back(true_exprt());
96+
9097
// pass the write_set
9198
target->call_arguments().push_back(cfg_info.get_write_set(target));
9299

src/goto-instrument/contracts/dynamic-frames/dfcc_pointer_equals.cpp

+11-2
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@ Date: Jan 2025
1313
#include <util/cprover_prefix.h>
1414
#include <util/expr_iterator.h>
1515
#include <util/pointer_expr.h>
16+
#include <util/prefix.h>
1617
#include <util/replace_expr.h>
1718
#include <util/std_code.h>
1819
#include <util/std_expr.h>
20+
#include <util/suffix.h>
1921
#include <util/symbol.h>
2022

2123
#include "dfcc_cfg_info.h"
@@ -56,7 +58,7 @@ void dfcc_pointer_equalst::rewrite_calls(
5658
{
5759
const irep_idt &fun_name = to_symbol_expr(function).get_identifier();
5860

59-
if(fun_name == CPROVER_PREFIX "pointer_equals")
61+
if(has_prefix(id2string(fun_name), CPROVER_PREFIX "pointer_equals"))
6062
{
6163
// add address on first operand
6264
target->call_arguments()[0] =
@@ -67,6 +69,12 @@ void dfcc_pointer_equalst::rewrite_calls(
6769
.set_identifier(
6870
library.dfcc_fun_symbol[dfcc_funt::POINTER_EQUALS].name);
6971

72+
// pass the may_fail flag
73+
if(function.source_location().get_bool("no_fail"))
74+
target->call_arguments().push_back(false_exprt());
75+
else
76+
target->call_arguments().push_back(true_exprt());
77+
7078
// pass the write_set
7179
target->call_arguments().push_back(cfg_info.get_write_set(target));
7280
}
@@ -106,7 +114,8 @@ class pointer_equality_visitort : public expr_visitort
106114
code_typet::parametert(pointer_type(void_type()))},
107115
bool_typet());
108116

109-
symbol_exprt pointer_equals("ID_pointer_equals", pointer_equals_type);
117+
symbol_exprt pointer_equals(
118+
CPROVER_PREFIX "pointer_equals", pointer_equals_type);
110119

111120
for(exprt *expr_ptr : equality_exprs_to_transform)
112121
{

src/goto-instrument/contracts/dynamic-frames/dfcc_pointer_in_range.cpp

+11-1
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@ Date: August 2022
1111

1212
#include <util/cprover_prefix.h>
1313
#include <util/pointer_expr.h>
14+
#include <util/prefix.h>
1415
#include <util/replace_expr.h>
1516
#include <util/std_code.h>
17+
#include <util/suffix.h>
1618
#include <util/symbol.h>
1719

1820
#include "dfcc_cfg_info.h"
@@ -53,7 +55,8 @@ void dfcc_pointer_in_ranget::rewrite_calls(
5355
{
5456
const irep_idt &fun_name = to_symbol_expr(function).get_identifier();
5557

56-
if(fun_name == CPROVER_PREFIX "pointer_in_range_dfcc")
58+
if(has_prefix(
59+
id2string(fun_name), CPROVER_PREFIX "pointer_in_range_dfcc"))
5760
{
5861
// add address on second operand
5962
target->call_arguments()[1] =
@@ -64,6 +67,13 @@ void dfcc_pointer_in_ranget::rewrite_calls(
6467
.set_identifier(
6568
library.dfcc_fun_symbol[dfcc_funt::POINTER_IN_RANGE_DFCC].name);
6669

70+
// pass the may_fail flag
71+
// pass the may_fail flag
72+
if(function.source_location().get_bool("no_fail"))
73+
target->call_arguments().push_back(false_exprt());
74+
else
75+
target->call_arguments().push_back(true_exprt());
76+
6777
// pass the write_set
6878
target->call_arguments().push_back(cfg_info.get_write_set(target));
6979
}

0 commit comments

Comments
 (0)