Skip to content

Commit d8f5d77

Browse files
author
Remi Delmas
committed
Function contracts: check for the presence of loops before assigns clause instrumentation.
1 parent b847942 commit d8f5d77

File tree

14 files changed

+232
-40
lines changed

14 files changed

+232
-40
lines changed

regression/contracts/assigns_validity_pointer_01/test.desc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ SUCCESS
88
// bar
99
ASSERT \*foo::x > 0
1010
IF ¬\(\*foo::x = 3\) THEN GOTO \d
11-
IF ¬\(.*0.* = NULL\) THEN GOTO \d
1211
ASSIGN .*::tmp_if_expr := \(\*\(.*0.*\) = 5 \? true : false\)
1312
ASSIGN .*::tmp_if_expr\$\d := .*::tmp_if_expr \? true : false
1413
ASSUME .*::tmp_if_expr\$\d

regression/contracts/function_check_02/main.c

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,16 @@ int initialize(int *arr)
1515
)
1616
// clang-format on
1717
{
18-
for(int i = 0; i < 10; i++)
19-
{
20-
arr[i] = i;
21-
}
18+
arr[0] = 0;
19+
arr[1] = 1;
20+
arr[2] = 2;
21+
arr[3] = 3;
22+
arr[4] = 4;
23+
arr[5] = 5;
24+
arr[6] = 6;
25+
arr[7] = 7;
26+
arr[8] = 8;
27+
arr[9] = 9;
2228

2329
return 0;
2430
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
int foo(int *arr)
2+
// clang-format off
3+
__CPROVER_assigns(__CPROVER_POINTER_OBJECT(arr))
4+
// clang-format off
5+
{
6+
for(int i = 0; i < 10; i++)
7+
arr[i] = i;
8+
9+
return 0;
10+
}
11+
12+
int main()
13+
{
14+
int arr[10];
15+
foo(arr);
16+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
CORE
2+
main.c
3+
--enforce-contract foo
4+
^--- begin invariant violation report ---$
5+
^Invariant check failed$
6+
^Condition: is_loop_free\(.*\)
7+
^Reason: Loops remain in function 'foo', assigns clause checking instrumentation cannot be applied\.$
8+
^EXIT=(127|134)$
9+
^SIGNAL=0$
10+
--
11+
--
12+
This test checks that loops that remain in the program when attempting to
13+
instrument assigns clauses are detected and trigger an INVARIANT.

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

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,16 @@ int f1(int *arr)
77
})
88
// clang-format on
99
{
10-
for(int i = 0; i < 10; i++)
11-
{
12-
arr[i] = i;
13-
}
10+
arr[0] = 0;
11+
arr[1] = 1;
12+
arr[2] = 2;
13+
arr[3] = 3;
14+
arr[4] = 4;
15+
arr[5] = 5;
16+
arr[6] = 6;
17+
arr[7] = 7;
18+
arr[8] = 8;
19+
arr[9] = 9;
1420

1521
return 0;
1622
}
@@ -24,10 +30,16 @@ int f2(int *arr)
2430
})
2531
// clang-format on
2632
{
27-
for(int i = 0; i < 10; i++)
28-
{
29-
arr[i] = 0;
30-
}
33+
arr[0] = 0;
34+
arr[1] = 1;
35+
arr[2] = 2;
36+
arr[3] = 3;
37+
arr[4] = 4;
38+
arr[5] = 5;
39+
arr[6] = 6;
40+
arr[7] = 7;
41+
arr[8] = 8;
42+
arr[9] = 9;
3143

3244
return 0;
3345
}

regression/contracts/quantifiers-exists-requires-enforce/main.c

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#include <stdbool.h>
22
#include <stdlib.h>
33

4-
#define MAX_LEN 64
4+
#define MAX_LEN 10
55

66
// clang-format off
77
bool f1(int *arr, int len)
@@ -18,11 +18,27 @@ bool f1(int *arr, int len)
1818
// clang-format on
1919
{
2020
bool found_four = false;
21-
for(int i = 0; i <= MAX_LEN; i++)
22-
{
23-
if(i < len)
24-
found_four |= (arr[i] == 4);
25-
}
21+
if(0 < len)
22+
found_four |= (arr[0] == 4);
23+
if(1 < len)
24+
found_four |= (arr[1] == 4);
25+
if(2 < len)
26+
found_four |= (arr[2] == 4);
27+
if(3 < len)
28+
found_four |= (arr[3] == 4);
29+
if(4 < len)
30+
found_four |= (arr[4] == 4);
31+
if(5 < len)
32+
found_four |= (arr[5] == 4);
33+
if(6 < len)
34+
found_four |= (arr[6] == 4);
35+
if(7 < len)
36+
found_four |= (arr[7] == 4);
37+
if(8 < len)
38+
found_four |= (arr[8] == 4);
39+
40+
if(9 < len)
41+
found_four |= (arr[9] == 4);
2642

2743
// clang-format off
2844
return (len > 0 ==> found_four);

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

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#include <stdlib.h>
22

3-
#define MAX_LEN 16
3+
#define MAX_LEN 10
44

55
// clang-format off
66
int f1(int *arr, int len)
@@ -12,11 +12,27 @@ int f1(int *arr, int len)
1212
})
1313
// clang-format on
1414
{
15-
for(int i = 0; i < MAX_LEN; i++)
16-
{
17-
if(i < len)
18-
arr[i] = 0;
19-
}
15+
if(0 < len)
16+
arr[0] = 0;
17+
if(1 < len)
18+
arr[1] = 0;
19+
if(2 < len)
20+
arr[2] = 0;
21+
if(3 < len)
22+
arr[3] = 0;
23+
if(4 < len)
24+
arr[4] = 0;
25+
if(5 < len)
26+
arr[5] = 0;
27+
if(6 < len)
28+
arr[6] = 0;
29+
if(7 < len)
30+
arr[7] = 0;
31+
if(8 < len)
32+
arr[8] = 0;
33+
if(9 < len)
34+
arr[9] = 0;
35+
2036
return 0;
2137
}
2238

regression/contracts/quantifiers-forall-ensures-enforce/test.desc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@ main.c
44
^EXIT=0$
55
^SIGNAL=0$
66
^\[postcondition.\d+\] file main.c line \d+ Check ensures clause: SUCCESS$
7-
^\[f1.\d+\] line \d+ Check that arr\[\(.*\)i\] is assignable: SUCCESS$
7+
^\[f1.\d+\] line \d+ Check that arr\[\(.*\)\d\] is assignable: SUCCESS$
88
^VERIFICATION SUCCESSFUL$
99
--
1010
^warning: ignoring
11+
^\[f1.\d+\] line \d+ Check that arr\[\(.*\)\d\] is assignable: FAILURE$
1112
--
1213
The purpose of this test is to ensure that we can safely use __CPROVER_forall
1314
within positive contexts (enforced ENSURES clauses).

regression/contracts/quantifiers-forall-requires-enforce/main.c

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,16 @@ bool f1(int *arr)
1212
// clang-format on
1313
{
1414
bool is_identity = true;
15-
for(int i = 0; i < 10; ++i)
16-
is_identity &= (arr[i] == i);
15+
is_identity &= (arr[0] == 0);
16+
is_identity &= (arr[1] == 1);
17+
is_identity &= (arr[2] == 2);
18+
is_identity &= (arr[3] == 3);
19+
is_identity &= (arr[4] == 4);
20+
is_identity &= (arr[5] == 5);
21+
is_identity &= (arr[6] == 6);
22+
is_identity &= (arr[7] == 7);
23+
is_identity &= (arr[8] == 8);
24+
is_identity &= (arr[9] == 9);
1725
return is_identity;
1826
}
1927

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

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,16 @@ int f1(int *arr)
1111
})
1212
// clang-format on
1313
{
14-
for(int i = 0; i < 10; i++)
15-
{
16-
arr[i] = i;
17-
}
14+
arr[0] = 0;
15+
arr[1] = 1;
16+
arr[2] = 2;
17+
arr[3] = 3;
18+
arr[4] = 4;
19+
arr[5] = 5;
20+
arr[6] = 6;
21+
arr[7] = 7;
22+
arr[8] = 8;
23+
arr[9] = 9;
1824

1925
return 0;
2026
}

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

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,16 @@ __CPROVER_assigns(__CPROVER_POINTER_OBJECT(arr))
1010
)
1111
// clang-format on
1212
{
13-
for(int i = 0; i < 10; i++)
14-
{
15-
arr[i] = i;
16-
}
13+
arr[0] = 0;
14+
arr[1] = 1;
15+
arr[2] = 2;
16+
arr[3] = 3;
17+
arr[4] = 4;
18+
arr[5] = 5;
19+
arr[6] = 6;
20+
arr[7] = 7;
21+
arr[8] = 8;
22+
arr[9] = 9;
1723

1824
return 0;
1925
}

src/goto-instrument/contracts/contracts.cpp

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1128,7 +1128,9 @@ bool code_contractst::check_frame_conditions_function(const irep_idt &function)
11281128
return true;
11291129
}
11301130

1131-
if(check_for_looped_mallocs(function_obj->second.body))
1131+
auto &goto_program = function_obj->second.body;
1132+
1133+
if(check_for_looped_mallocs(goto_program))
11321134
{
11331135
return true;
11341136
}
@@ -1149,14 +1151,17 @@ bool code_contractst::check_frame_conditions_function(const irep_idt &function)
11491151
for(const auto &param : to_code_type(target.type).parameters())
11501152
assigns.add_to_write_set(ns.lookup(param.get_identifier()).symbol_expr());
11511153

1152-
auto instruction_it = function_obj->second.body.instructions.begin();
1154+
auto instruction_it = goto_program.instructions.begin();
11531155
for(const auto &car : assigns.get_write_set())
11541156
{
11551157
auto snapshot_instructions = car.generate_snapshot_instructions();
11561158
insert_before_swap_and_advance(
1157-
function_obj->second.body, instruction_it, snapshot_instructions);
1159+
goto_program, instruction_it, snapshot_instructions);
11581160
};
11591161

1162+
// restore internal coherence in the programs
1163+
goto_functions.update();
1164+
11601165
// Full inlining of the function body
11611166
// Inlining is performed so that function calls to a same function
11621167
// occurring under different write sets get instrumented specifically
@@ -1168,9 +1173,17 @@ bool code_contractst::check_frame_conditions_function(const irep_idt &function)
11681173
decorated.get_recursive_function_warnings_count() == 0,
11691174
"Recursive functions found during inlining");
11701175

1171-
// restore internal invariants
1176+
// clean up possible fake loops that are due to `IF 0!=0 GOTO i` instructions
1177+
simplify_gotos(goto_program, ns);
1178+
1179+
// restore internal coherence in the programs
11721180
goto_functions.update();
11731181

1182+
INVARIANT(
1183+
is_loop_free(goto_program, ns, log),
1184+
"Loops remain in function '" + id2string(function) +
1185+
"', assigns clause checking instrumentation cannot be applied.");
1186+
11741187
// Insert write set inclusion checks.
11751188
check_frame_conditions(
11761189
function_obj->first,

src/goto-instrument/contracts/utils.cpp

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,13 @@ Date: September 2021
1010

1111
#include "utils.h"
1212

13+
#include <goto-programs/cfg.h>
1314
#include <util/fresh_symbol.h>
15+
#include <util/graph.h>
16+
#include <util/message.h>
1417
#include <util/pointer_expr.h>
1518
#include <util/pointer_predicates.h>
19+
#include <util/simplify_expr.h>
1620

1721
goto_programt::instructiont &
1822
add_pragma_disable_assigns_check(goto_programt::instructiont &instr)
@@ -210,3 +214,66 @@ bool is_auxiliary_decl_dead_or_assign(
210214
return false;
211215
}
212216

217+
void simplify_gotos(goto_programt &goto_program, namespacet &ns)
218+
{
219+
for(auto &instruction : goto_program.instructions)
220+
{
221+
if(instruction.is_goto())
222+
{
223+
const auto &condition = instruction.get_condition();
224+
const auto &simplified = simplify_expr(condition, ns);
225+
if(simplified.is_false())
226+
instruction.turn_into_skip();
227+
}
228+
}
229+
}
230+
231+
bool is_loop_free(
232+
const goto_programt &goto_program,
233+
namespacet &ns,
234+
messaget &log)
235+
{
236+
// create cfg from instruction list
237+
cfg_baset<empty_cfg_nodet> cfg;
238+
cfg(goto_program);
239+
240+
// check that all nodes are there
241+
INVARIANT(
242+
goto_program.instructions.size() == cfg.size(),
243+
"Instruction list vs CFG size mismatch.");
244+
245+
// compute SCCs
246+
using idxt = graph_nodet<empty_cfg_nodet>::node_indext;
247+
std::vector<idxt> node_to_scc(cfg.size(), -1);
248+
auto nof_sccs = cfg.SCCs(node_to_scc);
249+
250+
// compute size of each SCC
251+
std::vector<int> scc_size(nof_sccs, 0);
252+
for(auto scc : node_to_scc)
253+
{
254+
INVARIANT(
255+
0 <= scc && scc < nof_sccs, "Could not determine SCC for instruction");
256+
scc_size[scc]++;
257+
}
258+
259+
// check they are all of size 1
260+
for(size_t scc_id = 0; scc_id < nof_sccs; scc_id++)
261+
{
262+
auto size = scc_size[scc_id];
263+
if(size > 1)
264+
{
265+
log.error() << "Found CFG SCC with size " << size << messaget::eom;
266+
for(const auto &node_id : node_to_scc)
267+
{
268+
if(node_to_scc[node_id] == scc_id)
269+
{
270+
const auto &pc = cfg[node_id].PC;
271+
goto_program.output_instruction(ns, "", log.error(), *pc);
272+
log.error() << messaget::eom;
273+
}
274+
}
275+
return false;
276+
}
277+
}
278+
return true;
279+
}

0 commit comments

Comments
 (0)