Skip to content

Commit c29b69a

Browse files
committed
Support for empty assigns clause
CBMC currently doesn't enforce assigns clauses if there isn't one. However, whenever enforcing a function contract, the absence of an assigns clause should be interpreted as an empty write set (i.e., no inputs will be modified by this function). CBMC now properly checks for empty assigns clause behavior. Signed-off-by: Felipe R. Monteiro <[email protected]>
1 parent 749074b commit c29b69a

File tree

9 files changed

+104
-64
lines changed

9 files changed

+104
-64
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
CORE
22
main.c
33
--enforce-all-contracts
4-
^EXIT=10$
4+
^EXIT=0$
55
^SIGNAL=0$
6-
^VERIFICATION FAILED$
6+
^\[f1.1\] line \d+ .*: SUCCESS$
7+
^VERIFICATION SUCCESSFUL$
78
--
89
--
9-
This test checks that verification fails when a function with an assigns clause calls a function without an assigns clause.
10+
This test checks that verification succeeds when enforcing a contract
11+
for functions, without assigns clauses, that don't modify anything.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
int global = 0;
2+
3+
int foo(int *x) __CPROVER_assigns(*x)
4+
__CPROVER_ensures(__CPROVER_return_value == *x + 5)
5+
{
6+
int z = 5;
7+
int y = bar(&z);
8+
return *x + 5;
9+
}
10+
11+
int bar(int *a) __CPROVER_ensures(__CPROVER_return_value >= 5)
12+
{
13+
*a = *a + 2;
14+
return *a;
15+
}
16+
17+
int baz() __CPROVER_ensures(__CPROVER_return_value == global)
18+
{
19+
global = global + 1;
20+
return global;
21+
}
22+
23+
int main()
24+
{
25+
int n;
26+
n = foo(&n);
27+
n = baz();
28+
return 0;
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CORE
2+
main.c
3+
--enforce-all-contracts
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^\[bar.1\] line \d+ .*: FAILURE$
7+
^\[baz.1\] line \d+ .*: FAILURE$
8+
^VERIFICATION FAILED$
9+
--
10+
--
11+
Checks whether verification fails when enforcing a contract
12+
for functions, without assigns clauses, that modify an input.

regression/contracts/function_check_02/main.c

+7-1
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,16 @@
44
// A known bug (resolved in PR #2278) causes the use of quantifiers
55
// in ensures to fail.
66

7+
// clang-format off
78
int initialize(int *arr)
9+
__CPROVER_assigns(*arr)
810
__CPROVER_ensures(
9-
__CPROVER_forall {int i; (0 <= i && i < 10) ==> arr[i] == i}
11+
__CPROVER_forall {
12+
int i;
13+
(0 <= i && i < 10) ==> arr[i] == i
14+
}
1015
)
16+
// clang-format on
1117
{
1218
for(int i = 0; i < 10; i++)
1319
{

regression/contracts/quantifiers-exists-ensures-01/main.c

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
// clang-format off
2-
int f1(int *arr) __CPROVER_ensures(__CPROVER_exists {
3-
int i;
4-
(0 <= i && i < 10) ==> arr[i] == i
5-
})
2+
int f1(int *arr)
3+
__CPROVER_assigns(*arr)
4+
__CPROVER_ensures(__CPROVER_exists {
5+
int i;
6+
(0 <= i && i < 10) ==> arr[i] == i
7+
})
68
// clang-format on
79
{
810
for(int i = 0; i < 10; i++)

regression/contracts/quantifiers-forall-ensures-01/main.c

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
// clang-format off
2-
int f1(int *arr) __CPROVER_ensures(__CPROVER_forall {
3-
int i;
4-
(0 <= i && i < 10) ==> arr[i] == 0
5-
})
2+
int f1(int *arr)
3+
__CPROVER_assigns(*arr)
4+
__CPROVER_ensures(__CPROVER_forall {
5+
int i;
6+
(0 <= i && i < 10) ==> arr[i] == 0
7+
})
68
// clang-format on
79
{
810
for(int i = 0; i < 10; i++)

regression/contracts/quantifiers-nested-01/main.c

+10-8
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
// clang-format off
2-
int f1(int *arr) __CPROVER_ensures(__CPROVER_forall {
3-
int i;
4-
__CPROVER_forall
5-
{
6-
int j;
7-
(0 <= i && i < 10 && i <= j && j < 10) ==> arr[i] <= arr[j]
8-
}
9-
})
2+
int f1(int *arr)
3+
__CPROVER_assigns(*arr)
4+
__CPROVER_ensures(__CPROVER_forall {
5+
int i;
6+
__CPROVER_forall
7+
{
8+
int j;
9+
(0 <= i && i < 10 && i <= j && j < 10) ==> arr[i] <= arr[j]
10+
}
11+
})
1012
// clang-format on
1113
{
1214
for(int i = 0; i < 10; i++)

regression/contracts/quantifiers-nested-03/main.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
int f1(int *arr)
1+
int f1(int *arr) __CPROVER_assigns(*arr)
22
__CPROVER_ensures(__CPROVER_return_value == 0 && __CPROVER_exists {
33
int i;
44
(0 <= i && i < 10) && arr[i] == i

src/goto-instrument/code_contracts.cpp

+28-43
Original file line numberDiff line numberDiff line change
@@ -513,50 +513,38 @@ void code_contractst::instrument_call_statement(
513513

514514
exprt called_assigns =
515515
to_code_with_contract_type(called_symbol.type).assigns();
516-
if(called_assigns.is_nil()) // Called function has no assigns clause
517-
{
518-
// Create a false assertion, so the analysis
519-
// will fail if this function is called.
520-
goto_programt failing_assertion;
521-
failing_assertion.add(goto_programt::make_assertion(
522-
false_exprt(), instruction_iterator->source_location));
523-
program.insert_before_swap(instruction_iterator, failing_assertion);
524-
++instruction_iterator;
525-
526-
return;
527-
}
528-
else // Called function has assigns clause
516+
if(!called_assigns.is_nil()) // Called function has assigns clause
517+
{
518+
replace_symbolt replace;
519+
// Replace formal parameters
520+
code_function_callt::argumentst::const_iterator a_it =
521+
call.arguments().begin();
522+
for(code_typet::parameterst::const_iterator p_it =
523+
called_type.parameters().begin();
524+
p_it != called_type.parameters().end() &&
525+
a_it != call.arguments().end();
526+
++p_it, ++a_it)
529527
{
530-
replace_symbolt replace;
531-
// Replace formal parameters
532-
code_function_callt::argumentst::const_iterator a_it =
533-
call.arguments().begin();
534-
for(code_typet::parameterst::const_iterator p_it =
535-
called_type.parameters().begin();
536-
p_it != called_type.parameters().end() &&
537-
a_it != call.arguments().end();
538-
++p_it, ++a_it)
528+
if(!p_it->get_identifier().empty())
539529
{
540-
if(!p_it->get_identifier().empty())
541-
{
542-
symbol_exprt p(p_it->get_identifier(), p_it->type());
543-
replace.insert(p, *a_it);
544-
}
530+
symbol_exprt p(p_it->get_identifier(), p_it->type());
531+
replace.insert(p, *a_it);
545532
}
546-
547-
replace(called_assigns);
548-
549-
// check compatibility of assigns clause with the called function
550-
assigns_clauset called_assigns_clause(
551-
called_assigns, *this, function_id, log);
552-
exprt compatible =
553-
assigns_clause.compatible_expression(called_assigns_clause);
554-
goto_programt alias_assertion;
555-
alias_assertion.add(goto_programt::make_assertion(
556-
compatible, instruction_iterator->source_location));
557-
program.insert_before_swap(instruction_iterator, alias_assertion);
558-
++instruction_iterator;
559533
}
534+
535+
replace(called_assigns);
536+
537+
// check compatibility of assigns clause with the called function
538+
assigns_clauset called_assigns_clause(
539+
called_assigns, *this, function_id, log);
540+
exprt compatible =
541+
assigns_clause.compatible_expression(called_assigns_clause);
542+
goto_programt alias_assertion;
543+
alias_assertion.add(goto_programt::make_assertion(
544+
compatible, instruction_iterator->source_location));
545+
program.insert_before_swap(instruction_iterator, alias_assertion);
546+
++instruction_iterator;
547+
}
560548
}
561549

562550
bool code_contractst::check_for_looped_mallocs(const goto_programt &program)
@@ -634,9 +622,6 @@ bool code_contractst::add_pointer_checks(const std::string &function_name)
634622
const auto &type = to_code_with_contract_type(function_symbol.type);
635623

636624
exprt assigns_expr = type.assigns();
637-
// Return if there are no reference checks to perform.
638-
if(assigns_expr.is_nil())
639-
return false;
640625

641626
assigns_clauset assigns(assigns_expr, *this, function_id, log);
642627

0 commit comments

Comments
 (0)