Skip to content

Commit bbb7bf9

Browse files
committed
Path exploration pushes both successors onto queue
When doing path exploration and encountering a conditional goto instruction, both successors of the goto (the next instruction, and the target) are pushed onto the path exploration workqueue. This allows the symbolic execution caller to have complete control over which path to execute next. Prior to this commit, symex would push one successor onto the workqueue and continue executing the path of the other successor until the path was terminated. This commit introduces the possibility that symbolic execution pauses without reaching the end of a path, and needs to be resumed by the caller. Thus, new safety checker results are introduced (SUSPENDED and UNKNOWN); the former signals to the caller of symex that symex has not reached the end of a branch, and therefore no safety check has been performed. The (currently only) path exploration strategy still pops paths in the order that they were pushed. This behaviour means that the paths that it pops will always alternate between the next instruction of a goto, and the target of that same goto. The test suite is updated to reflect this strategy.
1 parent 27ca92f commit bbb7bf9

File tree

15 files changed

+273
-100
lines changed

15 files changed

+273
-100
lines changed

regression/cbmc-path-fifo/Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
default: tests.log
22

33
test: gen-desc
4-
@../test.pl -j 12 -p -c "../../../src/cbmc/cbmc --paths --verbosity 10"
4+
@../test.pl -p -c "../../../src/cbmc/cbmc --paths fifo --verbosity 10"
55

66
tests.log: ../test.pl gen-desc
7-
@../test.pl -j 12 -p -c "../../../src/cbmc/cbmc --paths --verbosity 10"
7+
@../test.pl -p -c "../../../src/cbmc/cbmc --paths fifo --verbosity 10"
88

99
gen-desc:
1010
@../../scripts/make_descs.py

regression/cbmc-path-fifo/if/test.in

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,21 @@ SIGNAL=0
77
--
88
^warning: ignoring
99
--
10+
This tests that the next path is resumed before the jump path.
11+
1012
Test that the following happens in order:
1113
execute line 4
12-
save line 7
14+
save next 5
15+
save jump 7
16+
any lines
17+
execute line 4
18+
resume next 5
1319
execute line 5
1420
any lines
1521
path is successful
1622
any lines
1723
execute line 4
18-
resume line 7
24+
resume jump 7
1925
execute line 7
2026
any lines
2127
path is successful
22-
end

regression/cbmc-path-fifo/loop-functional-correctness/main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
int main()
22
{
33
int x;
4-
__CPROVER_assume(x > 0);
4+
__CPROVER_assume(x == 1);
55

66
while(x)
77
--x;

regression/cbmc-path-fifo/loop-functional-correctness/test.in

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,50 @@ EXIT=10
77
--
88
^warning: ignoring
99
--
10-
This tests the following:
11-
12-
- Symex saves the path that traverses the loop once, then the path that
13-
traverses the loop twice;
14-
15-
- Symex pops the path that traverses the loop once, then the path that
16-
traverses the loop twice.
10+
Test that the following happens in order:
11+
execute line 6
12+
// Enter loop
13+
save next 7
14+
// Skip loop
15+
save jump 9
16+
any lines
17+
execute line 6
18+
resume next 7
19+
execute line 7
20+
any lines
21+
// Enter loop twice
22+
save next 7
23+
// Enter loop once & bail
24+
save jump 9
1725

18-
The path that traverses the loop twice is the one that would cause an
19-
assertion failure, thus that result must appear last rather than second
20-
last.
26+
// From this point on, we start hitting the end of paths.
2127

22-
Test that the following happens in order:
23-
// Loop 0 times
28+
// The path where we skipped over the loop completely. The path is
29+
// nonsense, because we assume x == 1 and yet do not enter the loop;
30+
// thus the path is infeasible and verification succeeds.
31+
any lines
32+
execute line 6
33+
resume jump 9
34+
execute line 9
35+
any lines
2436
path is successful
37+
38+
// The path where we enter the loop twice and then hit the assertion.
39+
// The path is infeasible because we cannot execute the loop twice if x
40+
// started out as 1.
41+
any lines
42+
execute line 6
43+
resume next 7
44+
execute line 7
2545
any lines
26-
// Loop once
2746
path is successful
47+
48+
// The path where we enter the loop once and then bail out. Here, x has
49+
// been decremented to zero, so the assertion must fail.
50+
any lines
51+
execute line 6
52+
resume jump 9
53+
execute line 9
2854
any lines
29-
// Loop twice
3055
path is unsuccessful
3156
end

regression/cbmc-path-fifo/nested-if/test.in

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,29 +9,47 @@ SIGNAL=0
99
--
1010
Test that the following happens in order:
1111
execute line 4
12-
save line 13
13-
execute line 6
14-
save line 9
15-
execute line 7
12+
save next 6
13+
save jump 13
14+
1615
any lines
17-
path is successful
16+
execute line 4
17+
resume next 6
18+
execute line 6
19+
save next 7
20+
save jump 9
21+
1822
any lines
1923
execute line 4
20-
resume line 13
24+
resume jump 13
2125
execute line 13
22-
save line 16
23-
execute line 14
26+
save next 14
27+
save jump 16
28+
29+
any lines
30+
execute line 6
31+
resume next 7
32+
execute line 7
2433
any lines
2534
path is successful
35+
2636
any lines
2737
execute line 6
28-
resume line 9
38+
resume jump 9
2939
execute line 9
3040
any lines
3141
path is successful
42+
43+
any lines
44+
execute line 13
45+
resume next 14
46+
execute line 14
47+
any lines
48+
path is successful
49+
3250
any lines
3351
execute line 13
34-
resume line 16
52+
resume jump 16
3553
execute line 16
3654
any lines
3755
path is successful

regression/cbmc-path-fifo/simple-loop/test.in

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,43 +8,39 @@ SIGNAL=0
88
^warning: ignoring
99
--
1010
Test that the following happens in order:
11-
execute line 4
12-
//
13-
// Save that the loop condition was never satisfied
14-
//
15-
save line 8
16-
execute line 6
11+
save next 6
12+
save jump 8
1713
any lines
1814
execute line 4
19-
unwind
20-
//
21-
// Save that the loop condition was satisfied once but not twice
22-
//
23-
save line 8
15+
resume next 6
2416
execute line 6
17+
18+
// Now go back to the top of the loop
2519
any lines
26-
//
27-
// Escape the loop
28-
//
29-
execute line 8
20+
save next 6
21+
save jump 8
22+
23+
// Before doing the instructions that assume we've already been through
24+
// the loop once, explore the path corresponding to never having been
25+
// through the loop
26+
any lines
27+
execute line 4
28+
resume jump 8
3029
any lines
3130
path is successful
32-
//
33-
// Now do the first break out of the loop...
34-
//
31+
32+
// Enter loop twice
3533
any lines
3634
execute line 4
37-
resume line 8
38-
execute line 8
35+
resume next 6
36+
execute line 6
3937
any lines
4038
path is successful
41-
//
42-
// ...and the second.
43-
//
39+
40+
// Bail out after having entered loop once
4441
any lines
4542
execute line 4
46-
resume line 8
43+
resume jump 8
4744
execute line 8
4845
any lines
4946
path is successful
50-
end

scripts/make_descs.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -99,16 +99,28 @@ def interpret(command_list, in_file):
9999
% int(m.group("l")))
100100
continue
101101

102-
m = re.match(r"^save line (?P<l>\d+)$", line)
102+
m = re.match(r"^save jump (?P<l>\d+)$", line)
103103
if m:
104-
ret += ("Saving 'file[^\\n]+line %d function \\w+'\\n"
104+
ret += ("Saving jump target 'file[^\\n]+line %d function \\w+'\\n"
105105
% int(m.group("l")))
106106
continue
107107

108-
m = re.match(r"^resume line (?P<l>\d+)$", line)
108+
m = re.match(r"^save next (?P<l>\d+)$", line)
109109
if m:
110-
ret += ("Resuming from 'file[^\\n]+line %d function \\w+'\\n"
111-
% int(m.group("l")))
110+
ret += ("Saving next instruction 'file[^\\n]+line %d function "
111+
"\\w+'\\n" % int(m.group("l")))
112+
continue
113+
114+
m = re.match(r"^resume next (?P<l>\d+)$", line)
115+
if m:
116+
ret += ("Resuming from next instruction 'file[^\\n]+line %d "
117+
"function \\w+'\\n" % int(m.group("l")))
118+
continue
119+
120+
m = re.match(r"^resume jump (?P<l>\d+)$", line)
121+
if m:
122+
ret += ("Resuming from jump target 'file[^\\n]+line %d function "
123+
"\\w+'\\n" % int(m.group("l")))
112124
continue
113125

114126
m = re.match(r"^unwind$", line)

src/cbmc/bmc.cpp

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,8 @@ safety_checkert::resultt bmct::execute(const goto_functionst &goto_functions)
362362
try
363363
{
364364
perform_symbolic_execution(goto_functions);
365+
if(symex.states_pushed)
366+
return safety_checkert::resultt::SUSPENDED;
365367

366368
// add a partial ordering, if required
367369
if(equation.has_threads())
@@ -603,7 +605,7 @@ int bmct::do_language_agnostic_bmc(
603605
std::function<void(bmct &, const goto_modelt &)> frontend_configure_bmc)
604606
{
605607
message_handlert &mh = message.get_message_handler();
606-
safety_checkert::resultt result;
608+
safety_checkert::resultt final_result = safety_checkert::resultt::UNKNOWN;
607609
std::unique_ptr<path_storaget> worklist = path_storaget::make(
608610
path_storaget::disciplinet::FIFO);
609611
try
@@ -618,7 +620,10 @@ int bmct::do_language_agnostic_bmc(
618620
bmct bmc(opts, goto_model.symbol_table, mh, pc, *worklist);
619621
bmc.set_ui(ui);
620622
frontend_configure_bmc(bmc, goto_model);
621-
result = bmc.run(goto_model.goto_functions);
623+
const safety_checkert::resultt tmp_result =
624+
bmc.run(goto_model.goto_functions);
625+
if(tmp_result != safety_checkert::resultt::SUSPENDED)
626+
final_result = tmp_result;
622627
}
623628
INVARIANT(
624629
opts.get_bool_option("paths") || worklist->empty(),
@@ -627,8 +632,8 @@ int bmct::do_language_agnostic_bmc(
627632
std::to_string(worklist->size()) + " unexplored branches.");
628633

629634
// When model checking, the bmc.run() above will already have explored
630-
// the entire program, and result contains the verification result. The
631-
// worklist (containing paths that have not yet been explored) is thus
635+
// the entire program, and final_result contains the verification result.
636+
// The worklist (containing paths that have not yet been explored) is thus
632637
// empty, and we don't enter this loop.
633638
//
634639
// When doing path exploration, there will be some saved paths left to
@@ -663,7 +668,10 @@ int bmct::do_language_agnostic_bmc(
663668
resume.state,
664669
*worklist);
665670
frontend_configure_bmc(pe, goto_model);
666-
result &= pe.run(goto_model.goto_functions);
671+
const safety_checkert::resultt tmp_result =
672+
pe.run(goto_model.goto_functions);
673+
if(tmp_result != safety_checkert::resultt::SUSPENDED)
674+
final_result &= tmp_result;
667675
worklist->pop();
668676
}
669677
}
@@ -683,14 +691,18 @@ int bmct::do_language_agnostic_bmc(
683691
throw std::current_exception();
684692
}
685693

686-
switch(result)
694+
switch(final_result)
687695
{
688696
case safety_checkert::resultt::SAFE:
689697
return CPROVER_EXIT_VERIFICATION_SAFE;
690698
case safety_checkert::resultt::UNSAFE:
691699
return CPROVER_EXIT_VERIFICATION_UNSAFE;
692700
case safety_checkert::resultt::ERROR:
693701
return CPROVER_EXIT_INTERNAL_ERROR;
702+
case safety_checkert::resultt::UNKNOWN:
703+
return CPROVER_EXIT_INTERNAL_ERROR;
704+
case safety_checkert::resultt::SUSPENDED:
705+
UNREACHABLE;
694706
}
695707
UNREACHABLE;
696708
}

0 commit comments

Comments
 (0)