Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CONTRACTS: force success for necessary pointer predicates #8574

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FUTURE dfcc-only smt-backend broken-cprover-smt-backend
CORE dfcc-only smt-backend broken-cprover-smt-backend
main.c
--dfcc main --apply-loop-contracts --enforce-contract foo --malloc-may-fail --malloc-fail-null _ --z3 --slice-formula --no-standard-checks
^EXIT=0$
Expand All @@ -8,11 +8,6 @@ main.c
^warning: ignoring
--

Marked as FUTURE because:
- z3 >v4.12 and up can solve the problem with `--dfcc-simple-invalid-pointer-model`,
but the CI uses older versions;
- bitwuzla >v0.6 can solve the problem but we don't run bitwuzla in CI.

Tests support for quantifiers in loop contracts with the SMT backend.
When quantified loop invariants are used, they are inserted three times
in the transformed program (base case assertion, step case assumption,
Expand Down
26 changes: 17 additions & 9 deletions src/ansi-c/library/cprover_contracts.c
Original file line number Diff line number Diff line change
Expand Up @@ -1166,6 +1166,7 @@ void __CPROVER_contracts_make_invalid_pointer(void **ptr)
///
/// \param ptr1 First argument of the `pointer_equals` predicate
/// \param ptr2 Second argument of the `pointer_equals` predicate
/// \param may_fail Allow predicate to fail in assume mode
/// \param write_set Write set which conveys the invocation context
/// (requires/ensures clause, assert/assume context);
///
Expand All @@ -1179,6 +1180,7 @@ void __CPROVER_contracts_make_invalid_pointer(void **ptr)
__CPROVER_bool __CPROVER_contracts_pointer_equals(
void **ptr1,
void *ptr2,
__CPROVER_bool may_fail,
__CPROVER_contracts_write_set_ptr_t write_set)
{
__CPROVER_HIDE:;
Expand All @@ -1195,7 +1197,8 @@ __CPROVER_HIDE:;
#endif
if(write_set->assume_requires_ctx | write_set->assume_ensures_ctx)
{
if(__VERIFIER_nondet___CPROVER_bool())
// SOUNDNESS: allow predicate to fail
if(may_fail && __VERIFIER_nondet___CPROVER_bool())
{
__CPROVER_contracts_make_invalid_pointer(ptr1);
return 0;
Expand Down Expand Up @@ -1224,8 +1227,9 @@ __CPROVER_HIDE:;
/// The behaviour depends on the boolean flags carried by \p write_set
/// which reflect the invocation context: checking vs. replacing a contract,
/// in a requires or an ensures clause context.
/// \param elem First argument of the `is_fresh` predicate
/// \param size Second argument of the `is_fresh` predicate
/// \param elem Pointer to the target pointer of the check
/// \param size Size to check for
/// \param may_fail Allow predicate to fail in assume mode
/// \param write_set Write set in which seen/allocated objects are recorded;
///
/// \details The behaviour is as follows:
Expand All @@ -1242,6 +1246,7 @@ __CPROVER_HIDE:;
__CPROVER_bool __CPROVER_contracts_is_fresh(
void **elem,
__CPROVER_size_t size,
__CPROVER_bool may_fail,
__CPROVER_contracts_write_set_ptr_t write_set)
{
__CPROVER_HIDE:;
Expand Down Expand Up @@ -1289,7 +1294,7 @@ __CPROVER_HIDE:;
}

// SOUNDNESS: allow predicate to fail
if(__VERIFIER_nondet___CPROVER_bool())
if(may_fail && __VERIFIER_nondet___CPROVER_bool())
{
__CPROVER_contracts_make_invalid_pointer(elem);
return 0;
Expand Down Expand Up @@ -1349,7 +1354,7 @@ __CPROVER_HIDE:;
}

// SOUNDNESS: allow predicate to fail
if(__VERIFIER_nondet___CPROVER_bool())
if(may_fail && __VERIFIER_nondet___CPROVER_bool())
{
__CPROVER_contracts_make_invalid_pointer(elem);
return 0;
Expand Down Expand Up @@ -1436,6 +1441,7 @@ __CPROVER_HIDE:;
/// \param lb Lower bound pointer
/// \param ptr Target pointer of the predicate
/// \param ub Upper bound pointer
/// \param may_fail Allow predicate to fail in assume mode
/// \param write_set Write set in which seen/allocated objects are recorded;
///
/// \details The behaviour is as follows:
Expand All @@ -1449,6 +1455,7 @@ __CPROVER_bool __CPROVER_contracts_pointer_in_range_dfcc(
void *lb,
void **ptr,
void *ub,
__CPROVER_bool may_fail,
__CPROVER_contracts_write_set_ptr_t write_set)
{
__CPROVER_HIDE:;
Expand All @@ -1470,9 +1477,9 @@ __CPROVER_HIDE:;
lb_offset <= ub_offset, "lb and ub pointers must be ordered");
if(write_set->assume_requires_ctx | write_set->assume_ensures_ctx)
{
if(__VERIFIER_nondet___CPROVER_bool())
// SOUNDNESS: allow predicate to fail
if(may_fail && __VERIFIER_nondet___CPROVER_bool())
{
// SOUNDNESS: allow predicate to fail
__CPROVER_contracts_make_invalid_pointer(ptr);
return 0;
}
Expand Down Expand Up @@ -1647,6 +1654,7 @@ __CPROVER_HIDE:;
__CPROVER_bool __CPROVER_contracts_obeys_contract(
void (**function_pointer)(void),
void (*contract)(void),
__CPROVER_bool may_fail,
__CPROVER_contracts_write_set_ptr_t set)
{
__CPROVER_HIDE:;
Expand All @@ -1657,8 +1665,8 @@ __CPROVER_HIDE:;
"__CPROVER_obeys_contract is used only in requires or ensures clauses");
if((set->assume_requires_ctx == 1) | (set->assume_ensures_ctx == 1))
{
// decide if predicate must hold
if(__VERIFIER_nondet___CPROVER_bool())
// SOUDNESS: allow predicate to fail
if(may_fail && __VERIFIER_nondet___CPROVER_bool())
return 0;

// must hold, assign the function pointer to the contract function
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,3 +156,11 @@ bool dfcc_is_cprover_static_symbol(const irep_idt &id)
// going_to variables converted from goto statements
has_prefix(id2string(id), CPROVER_PREFIX "going_to");
}

bool dfcc_is_cprover_pointer_predicate(const irep_idt &id)
{
return id == CPROVER_PREFIX "pointer_equals" ||
id == CPROVER_PREFIX "is_fresh" ||
id == CPROVER_PREFIX "pointer_in_range_dfcc" ||
id == CPROVER_PREFIX "obeys_contract";
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,7 @@ bool dfcc_is_cprover_function_symbol(const irep_idt &id);
/// auto-generated object following a pointer dereference.
bool dfcc_is_cprover_static_symbol(const irep_idt &id);

/// Returns `true` iff the symbol is one of the CPROVER pointer predicates
bool dfcc_is_cprover_pointer_predicate(const irep_idt &id);

#endif
11 changes: 9 additions & 2 deletions src/goto-instrument/contracts/dynamic-frames/dfcc_is_fresh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

#include <util/cprover_prefix.h>
#include <util/pointer_expr.h>
#include <util/prefix.h>
#include <util/suffix.h>
#include <util/symbol.h>

#include "dfcc_cfg_info.h"
Expand Down Expand Up @@ -50,8 +52,7 @@
if(function.id() == ID_symbol)
{
const irep_idt &fun_name = to_symbol_expr(function).get_identifier();

if(fun_name == CPROVER_PREFIX "is_fresh")
if(has_prefix(id2string(fun_name), CPROVER_PREFIX "is_fresh"))
{
// add address on first operand
target->call_arguments()[0] =
Expand All @@ -61,6 +62,12 @@
to_symbol_expr(target->call_function())
.set_identifier(library.dfcc_fun_symbol[dfcc_funt::IS_FRESH].name);

// pass the may_fail flag

Check warning on line 65 in src/goto-instrument/contracts/dynamic-frames/dfcc_is_fresh.cpp

View check run for this annotation

Codecov / codecov/patch

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

Added line #L65 was not covered by tests
if(function.source_location().get_bool("no_fail"))
target->call_arguments().push_back(false_exprt());
else
target->call_arguments().push_back(true_exprt());

// pass the write_set
target->call_arguments().push_back(cfg_info.get_write_set(target));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <util/cprover_prefix.h>
#include <util/pointer_expr.h>
#include <util/prefix.h>
#include <util/suffix.h>
#include <util/symbol.h>

#include <langapi/language_util.h>
Expand Down Expand Up @@ -87,6 +88,12 @@
.set_identifier(
library.dfcc_fun_symbol[dfcc_funt::OBEYS_CONTRACT].name);

// pass the may_fail flag

Check warning on line 91 in src/goto-instrument/contracts/dynamic-frames/dfcc_obeys_contract.cpp

View check run for this annotation

Codecov / codecov/patch

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

Added line #L91 was not covered by tests
if(function.source_location().get_bool("no_fail"))
target->call_arguments().push_back(false_exprt());
else
target->call_arguments().push_back(true_exprt());

Check warning on line 95 in src/goto-instrument/contracts/dynamic-frames/dfcc_obeys_contract.cpp

View check run for this annotation

Codecov / codecov/patch

src/goto-instrument/contracts/dynamic-frames/dfcc_obeys_contract.cpp#L94-L95

Added lines #L94 - L95 were not covered by tests

// pass the write_set
target->call_arguments().push_back(cfg_info.get_write_set(target));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@
#include <util/cprover_prefix.h>
#include <util/expr_iterator.h>
#include <util/pointer_expr.h>
#include <util/prefix.h>
#include <util/replace_expr.h>
#include <util/std_code.h>
#include <util/std_expr.h>
#include <util/suffix.h>
#include <util/symbol.h>

#include "dfcc_cfg_info.h"
Expand Down Expand Up @@ -56,7 +58,7 @@
{
const irep_idt &fun_name = to_symbol_expr(function).get_identifier();

if(fun_name == CPROVER_PREFIX "pointer_equals")
if(has_prefix(id2string(fun_name), CPROVER_PREFIX "pointer_equals"))
{
// add address on first operand
target->call_arguments()[0] =
Expand All @@ -67,6 +69,12 @@
.set_identifier(
library.dfcc_fun_symbol[dfcc_funt::POINTER_EQUALS].name);

// pass the may_fail flag
if(function.source_location().get_bool("no_fail"))
target->call_arguments().push_back(false_exprt());
else

Check warning on line 75 in src/goto-instrument/contracts/dynamic-frames/dfcc_pointer_equals.cpp

View check run for this annotation

Codecov / codecov/patch

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

Added line #L75 was not covered by tests
target->call_arguments().push_back(true_exprt());

Check warning on line 77 in src/goto-instrument/contracts/dynamic-frames/dfcc_pointer_equals.cpp

View check run for this annotation

Codecov / codecov/patch

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

Added line #L77 was not covered by tests
// pass the write_set
target->call_arguments().push_back(cfg_info.get_write_set(target));
}
Expand Down Expand Up @@ -106,7 +114,8 @@
code_typet::parametert(pointer_type(void_type()))},
bool_typet());

symbol_exprt pointer_equals("ID_pointer_equals", pointer_equals_type);
symbol_exprt pointer_equals(
CPROVER_PREFIX "pointer_equals", pointer_equals_type);

Check warning on line 118 in src/goto-instrument/contracts/dynamic-frames/dfcc_pointer_equals.cpp

View check run for this annotation

Codecov / codecov/patch

src/goto-instrument/contracts/dynamic-frames/dfcc_pointer_equals.cpp#L117-L118

Added lines #L117 - L118 were not covered by tests

for(exprt *expr_ptr : equality_exprs_to_transform)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@

#include <util/cprover_prefix.h>
#include <util/pointer_expr.h>
#include <util/prefix.h>
#include <util/replace_expr.h>
#include <util/std_code.h>
#include <util/suffix.h>
#include <util/symbol.h>

#include "dfcc_cfg_info.h"
Expand Down Expand Up @@ -53,7 +55,8 @@
{
const irep_idt &fun_name = to_symbol_expr(function).get_identifier();

if(fun_name == CPROVER_PREFIX "pointer_in_range_dfcc")
if(has_prefix(
id2string(fun_name), CPROVER_PREFIX "pointer_in_range_dfcc"))

Check warning on line 59 in src/goto-instrument/contracts/dynamic-frames/dfcc_pointer_in_range.cpp

View check run for this annotation

Codecov / codecov/patch

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

Added line #L59 was not covered by tests
{
// add address on second operand
target->call_arguments()[1] =
Expand All @@ -64,6 +67,13 @@
.set_identifier(
library.dfcc_fun_symbol[dfcc_funt::POINTER_IN_RANGE_DFCC].name);

// pass the may_fail flag
// pass the may_fail flag
if(function.source_location().get_bool("no_fail"))
target->call_arguments().push_back(false_exprt());
else

Check warning on line 74 in src/goto-instrument/contracts/dynamic-frames/dfcc_pointer_in_range.cpp

View check run for this annotation

Codecov / codecov/patch

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

Added line #L74 was not covered by tests
target->call_arguments().push_back(true_exprt());

// pass the write_set
target->call_arguments().push_back(cfg_info.get_write_set(target));
}
Expand Down
Loading
Loading