Skip to content

Commit 5750c68

Browse files
authored
Merge pull request #6447 from remi-delmas-3000/fix-local-statics-detection
Fix local static variables detection in assigns clauses
2 parents faaf50e + 5fb8ee5 commit 5750c68

File tree

15 files changed

+278
-99
lines changed

15 files changed

+278
-99
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#include <assert.h>
2+
#include <stdlib.h>
3+
4+
static int x;
5+
static int xx;
6+
7+
void foo()
8+
{
9+
int *y = &x;
10+
int *yy = &xx;
11+
12+
static int x;
13+
// must pass (modifies local static)
14+
x++;
15+
16+
// must pass (modifies assignable global static )
17+
(*y)++;
18+
19+
// must fail (modifies non-assignable global static)
20+
(*yy)++;
21+
}
22+
23+
void bar() __CPROVER_assigns(x)
24+
{
25+
foo();
26+
}
27+
28+
int main()
29+
{
30+
bar();
31+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
CORE
2+
main.c
3+
--enforce-contract bar
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^\[foo.\d+\] line \d+ Check that y is assignable: SUCCESS$
7+
^\[foo.\d+\] line \d+ Check that foo\$\$1\$\$x is assignable: SUCCESS$
8+
^\[foo.\d+\] line \d+ Check that \*y is assignable: SUCCESS$
9+
^\[foo.\d+\] line \d+ Check that \*yy is assignable: FAILURE$
10+
^VERIFICATION FAILED$
11+
--
12+
--
13+
Checks whether static local and global variables are correctly tracked
14+
in assigns clause verification, accross subfunction calls.

regression/contracts/assigns_enforce_statics/main.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
#include <assert.h>
2-
#include <stdlib.h>
3-
4-
static int x;
1+
static int x = 0;
52

63
void foo() __CPROVER_assigns(x)
74
{
85
int *y = &x;
96

10-
static int x;
7+
static int x = 0;
8+
9+
// should pass (assigns local x)
1110
x++;
1211

12+
// should fail (assigns global x)
1313
(*y)++;
1414
}
1515

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,12 @@
11
CORE
22
main.c
33
--enforce-contract f1 _ --unwind 20 --unwinding-assertions
4-
^EXIT=10$
4+
^file main\.c line 13 function f2: recursion is ignored on call to 'f2'$
5+
^Invariant check failed$
6+
^Condition: decorated\.get_recursive_function_warnings_count\(\) == 0$
7+
^Reason: Recursive functions found during inlining$
8+
^EXIT=(127|134|137)$
59
^SIGNAL=0$
6-
^file main.c line \d+ function f2: recursion is ignored on call to \'f2\'$
7-
^\[postcondition.\d+\] file main.c line \d+ Check ensures clause: FAILURE$
8-
^VERIFICATION FAILED$
910
--
1011
--
11-
Verification:
12-
function | pre-cond | post-cond
13-
---------|----------|----------
14-
f1 | assumed | asserted
15-
16-
Test should fail:
17-
The postcondition of f2 is incorrect, considering the recursion particularity.
18-
19-
Recursion:
20-
The base case for the recursive call to f2 provides different behavior than the common case (given the pre-conditions).
12+
Test should fail because a recursive function is found during inlining.
Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,12 @@
11
CORE
22
main.c
33
--enforce-contract f2 _ --unwind 20 --unwinding-assertions
4-
^EXIT=10$
4+
^file main\.c line 13 function f2: recursion is ignored on call to 'f2'$
5+
^Invariant check failed$
6+
^Condition: decorated\.get_recursive_function_warnings_count\(\) == 0$
7+
^Reason: Recursive functions found during inlining$
8+
^EXIT=(127|134|137)$
59
^SIGNAL=0$
6-
^file main.c line \d+ function f2: recursion is ignored on call to \'f2\'$
7-
^\[postcondition.\d+\] file main.c line \d+ Check ensures clause: FAILURE$
8-
^\[f2.\d+\] line \d+ Check that loc is assignable: SUCCESS$
9-
^VERIFICATION FAILED$
1010
--
1111
--
12-
Verification:
13-
function | pre-cond | post-cond
14-
---------|----------|----------
15-
f2 | assumed | asserted
16-
17-
Test should fail:
18-
The postcondition of f2 is incorrect, considering the recursion particularity.
19-
20-
Recursion:
21-
The base case for the recursive call to f2 provides different behavior than the common case (given the pre-conditions).
12+
Test should fail because a recursive function is found during inlining.
Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,12 @@
11
CORE
22
main.c
33
--enforce-contract f1 _ --unwind 20 --unwinding-assertions
4-
^EXIT=10$
4+
^file main\.c line 19 function f2_in: recursion is ignored on call to 'f2_out'$
5+
^Invariant check failed$
6+
^Condition: decorated\.get_recursive_function_warnings_count\(\) == 0$
7+
^Reason: Recursive functions found during inlining$
8+
^EXIT=(127|134|137)$
59
^SIGNAL=0$
6-
^file main.c line \d+ function f2\_in: recursion is ignored on call to \'f2\_out\'$
7-
^\[postcondition.\d+\] file main.c line \d+ Check ensures clause: FAILURE$
8-
^VERIFICATION FAILED$
910
--
1011
--
11-
Verification:
12-
function | pre-cond | post-cond
13-
---------|----------|----------
14-
f1 | assumed | asserted
15-
16-
Test should fail:
17-
The postcondition of f2_out is incorrect, considering the recursion particularity.
18-
19-
Recursion:
20-
The base case for the recursive call to f2_out provides different behavior
21-
than the general case (given the pre-conditions).
12+
Test should fail because a recursive function is found during inlining.
Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,12 @@
11
CORE
22
main.c
33
--enforce-contract f2_in _ --unwind 20 --unwinding-assertions
4-
^EXIT=10$
4+
^file main\.c line 13 function f2_out: recursion is ignored on call to 'f2_in'$
5+
^Invariant check failed$
6+
^Condition: decorated\.get_recursive_function_warnings_count\(\) == 0$
7+
^Reason: Recursive functions found during inlining$
8+
^EXIT=(127|134|137)$
59
^SIGNAL=0$
6-
^file main.c line \d+ function f2\_out: recursion is ignored on call to \'f2\_in\'$
7-
^\[postcondition.\d+\] file main.c line \d+ Check ensures clause: FAILURE$
8-
^\[f2\_out.\d+\] line \d+ Check that loc2 is assignable: SUCCESS$
9-
^VERIFICATION FAILED$
1010
--
1111
--
12-
Verification:
13-
function | pre-cond | post-cond
14-
---------|----------|----------
15-
f2_in | assumed | asserted
16-
17-
Test should fail:
18-
The postcondition of f2_out is incorrect, considering the recursion particularity.
19-
20-
Recursion:
21-
The base case for the recursive call to f2_out provides different behavior
22-
than the general case (given the pre-conditions).
12+
Test should fail because a recursive function is found during inlining.
Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,12 @@
11
CORE
22
main.c
33
--enforce-contract f2_out _ --unwind 20 --unwinding-assertions
4-
^EXIT=10$
4+
^file main\.c line 19 function f2_in: recursion is ignored on call to 'f2_out'$
5+
^Invariant check failed$
6+
^Condition: decorated\.get_recursive_function_warnings_count\(\) == 0$
7+
^Reason: Recursive functions found during inlining$
8+
^EXIT=(127|134|137)$
59
^SIGNAL=0$
6-
^file main.c line \d+ function f2\_in: recursion is ignored on call to \'f2\_out\'$
7-
^\[postcondition.\d+\] file main.c line \d+ Check ensures clause: FAILURE$
8-
^\[f2\_out.\d+\] line \d+ Check that loc2 is assignable: SUCCESS$
9-
^VERIFICATION FAILED$
1010
--
1111
--
12-
Verification:
13-
function | pre-cond | post-cond
14-
---------|----------|----------
15-
f2_out | assumed | asserted
16-
17-
Test should fail:
18-
The postcondition of f2 is incorrect, considering the recursion particularity.
19-
20-
Recursion:
21-
The base case for the recursive call to f2_out provides different behavior
22-
than the general case (given the pre-conditions).
12+
Test should fail because a recursive function is found during inlining.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#include <stdlib.h>
2+
3+
int sum_rec(int i, int acc)
4+
{
5+
if(i >= 0)
6+
return sum_rec(i - 1, acc + i);
7+
return acc;
8+
}
9+
10+
int sum(int i) __CPROVER_requires(0 <= i && i <= 50)
11+
__CPROVER_ensures(__CPROVER_return_value >= 0) __CPROVER_assigns()
12+
{
13+
int j = i;
14+
int res = sum_rec(j, 0);
15+
return res;
16+
}
17+
18+
int main()
19+
{
20+
int result = sum(10);
21+
__CPROVER_assert(result == 55, "result == 55");
22+
return 0;
23+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CORE
2+
main.c
3+
--enforce-contract sum _ --trace
4+
^file main\.c line 6 function sum_rec: recursion is ignored on call to 'sum_rec'$
5+
^Invariant check failed$
6+
^Condition: decorated\.get_recursive_function_warnings_count\(\) == 0$
7+
^Reason: Recursive functions found during inlining$
8+
^EXIT=(127|134|137)$
9+
^SIGNAL=0$
10+
--
11+
--
12+
Test should fail because a recursive function is found during inlining.

src/analyses/call_graph.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,11 @@ static void forall_callsites(
5858
if(i_it->is_function_call())
5959
{
6060
const exprt &function_expr = i_it->call_function();
61-
if(function_expr.id()==ID_symbol)
62-
{
63-
const irep_idt &callee=to_symbol_expr(function_expr).get_identifier();
64-
call_task(i_it, callee);
65-
}
61+
PRECONDITION_WITH_DIAGNOSTICS(
62+
function_expr.id() == ID_symbol,
63+
"call graph computation requires function pointer removal");
64+
const irep_idt &callee = to_symbol_expr(function_expr).get_identifier();
65+
call_task(i_it, callee);
6666
}
6767
}
6868
}

src/goto-instrument/contracts/assigns.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ Date: July 2021
1313

1414
#include "assigns.h"
1515

16+
#include <analyses/call_graph.h>
17+
1618
#include <langapi/language_util.h>
1719

1820
#include <util/arith_tools.h>
@@ -196,3 +198,31 @@ void havoc_assigns_targetst::append_havoc_code_for_expr(
196198

197199
havoc_utilst::append_havoc_code_for_expr(location, expr, dest);
198200
}
201+
202+
void assigns_clauset::add_static_locals_to_write_set(
203+
const goto_functionst &functions,
204+
const irep_idt &root_function)
205+
{
206+
auto call_graph =
207+
call_grapht::create_from_root_function(functions, root_function, true)
208+
.get_directed_graph();
209+
210+
for(const auto &sym_pair : symbol_table)
211+
{
212+
const auto &sym = sym_pair.second;
213+
if(sym.is_static_lifetime)
214+
{
215+
auto fname = sym.location.get_function();
216+
if(
217+
!fname.empty() && (fname == root_function ||
218+
call_graph.get_node_index(fname).has_value()))
219+
{
220+
// We found a symbol with
221+
// - a static lifetime
222+
// - non empty location function
223+
// - this function appears in the call graph of the function
224+
add_to_write_set(sym.symbol_expr());
225+
}
226+
}
227+
}
228+
}

src/goto-instrument/contracts/assigns.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,21 @@ class assigns_clauset
8787
return write_set;
8888
}
8989

90+
/// \brief Finds symbols declared with a static lifetime in the given
91+
/// `root_function` or one of the functions it calls,
92+
/// and adds them to the write set of this assigns clause.
93+
///
94+
/// @param functions all functions of the goto_model
95+
/// @param root_function the root function under which to search statics
96+
///
97+
/// A symbol is considered a static local symbol iff:
98+
/// - it has a static lifetime annotation
99+
/// - its source location has a non-empty function attribute
100+
/// - this function attribute is found in the call graph of the root_function
101+
void add_static_locals_to_write_set(
102+
const goto_functionst &functions,
103+
const irep_idt &root_function);
104+
90105
const messaget &log;
91106
const namespacet &ns;
92107
const irep_idt &function_name;

0 commit comments

Comments
 (0)