Skip to content

Commit fde3f24

Browse files
authored
Merge pull request #5097 from smowton/smowton/feature/symex-truncate-on-assume-false
Symex: distinguish reachability from the state guard
2 parents 4259189 + 9dcc43f commit fde3f24

File tree

31 files changed

+400
-48
lines changed

31 files changed

+400
-48
lines changed

jbmc/regression/jbmc/NondetEnumOpaqueReturn/test_indirect_enum_initalization.desc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
CORE
22
indirect/IndirectVirtualCall.class
33
--function indirect.IndirectVirtualCall.main
4-
function java::indirect\.TestEnum\.<clinit>:\(\)V$
54
^EXIT=10$
65
^SIGNAL=0$
76
--
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CORE paths-lifo-expected-failure
2+
test.c
3+
--show-vcc
4+
^Generated 1 VCC\(s\), 1 remaining after simplification$
5+
^\{1\} false$
6+
^EXIT=0$
7+
^SIGNAL=0$
8+
--
9+
--
10+
Note: disabled for paths-lifo mode, which never merges state guards
11+
This checks that despite being unreachable due to an assume(false), the state guard
12+
is returned to TRUE by the time the assertion is executed.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
int main(int argc, char **argv)
2+
{
3+
if(argc == 1)
4+
{
5+
do
6+
{
7+
__CPROVER_assume(0);
8+
} while(argc < 2);
9+
}
10+
11+
assert(0);
12+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
test.c
3+
4+
^VERIFICATION FAILED$
5+
^EXIT=10$
6+
^SIGNAL=0$
7+
--
8+
--
9+
This tests that a __CPROVER_assume(0) prompts symex to assume that a conditional
10+
backward branch is not taken.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
CORE paths-lifo-expected-failure
2+
test.c
3+
--show-vcc
4+
^Generated 1 VCC\(s\), 1 remaining after simplification$
5+
^\{1\} goto_symex::\\guard#1$
6+
^EXIT=0$
7+
^SIGNAL=0$
8+
--
9+
--
10+
Note: disabled for paths-lifo mode, which never merges state guards
11+
This checks that the guard from a loop containing an assume(false), and with
12+
an unconditional backedge, is discarded as expected, resulting in a non-trivial
13+
guard at the assertion.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
int main(int argc, char **argv)
2+
{
3+
if(argc == 1)
4+
{
5+
while(argc < 2)
6+
{
7+
__CPROVER_assume(0);
8+
}
9+
}
10+
11+
assert(0);
12+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CORE
2+
test.c
3+
4+
^VERIFICATION FAILED$
5+
^EXIT=10$
6+
^SIGNAL=0$
7+
--
8+
--
9+
This tests that a __CPROVER_assume(0) in an infinite loop terminates the loop
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
CORE paths-lifo-expected-failure
2+
test.c
3+
--show-vcc
4+
^Generated 1 VCC\(s\), 1 remaining after simplification$
5+
^\{1\} false$
6+
^EXIT=0$
7+
^SIGNAL=0$
8+
--
9+
--
10+
Note: disabled for paths-lifo mode, which never merges state guards
11+
This checks that an assume(false) prior to a loop correctly results
12+
in symex stepping around that loop, resulting in the state guard
13+
returning to true.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
int main(int argc, char **argv)
2+
{
3+
if(argc == 1)
4+
{
5+
__CPROVER_assume(0);
6+
while(1)
7+
{
8+
}
9+
}
10+
11+
assert(0);
12+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
test.c
3+
4+
^VERIFICATION FAILED$
5+
^EXIT=10$
6+
^SIGNAL=0$
7+
--
8+
--
9+
This tests that with a __CPROVER_assume(0) before an infinite loop,
10+
symex terminates without needing a `--unwind` setting.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
CORE paths-lifo-expected-failure
2+
test.c
3+
--show-vcc
4+
^Generated 1 VCC\(s\), 1 remaining after simplification$
5+
^\{1\} false$
6+
^EXIT=0$
7+
^SIGNAL=0$
8+
--
9+
--
10+
Note: disabled for paths-lifo mode, which never merges state guards
11+
This checks that an assume(false) prior to a do-while loop correctly results
12+
in symex stepping into a single iteration of that loop, and then out,
13+
resulting in the state guard returning to true.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
int main(int argc, char **argv)
2+
{
3+
if(argc == 1)
4+
{
5+
__CPROVER_assume(0);
6+
do
7+
{
8+
} while(1);
9+
}
10+
11+
assert(0);
12+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
test.c
3+
4+
^VERIFICATION FAILED$
5+
^EXIT=10$
6+
^SIGNAL=0$
7+
--
8+
--
9+
This tests that with a __CPROVER_assume(0) before an infinite loop,
10+
symex terminates without needing a `--unwind` setting.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CORE paths-lifo-expected-failure
2+
test.c
3+
--show-vcc
4+
^Generated 1 VCC\(s\), 1 remaining after simplification$
5+
^\{1\} false$
6+
^EXIT=0$
7+
^SIGNAL=0$
8+
--
9+
--
10+
Note: disabled for paths-lifo mode, which never merges state guards
11+
This checks that an assume(false) within a branch correctly results
12+
in symex returning the state guard to true when control flow converges.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
int main(int argc, char **argv)
2+
{
3+
if(argc == 1)
4+
{
5+
__CPROVER_assume(0);
6+
}
7+
else
8+
{
9+
}
10+
11+
assert(0);
12+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
test.c
3+
4+
^VERIFICATION FAILED$
5+
^EXIT=10$
6+
^SIGNAL=0$
7+
--
8+
--
9+
This tests that with a __CPROVER_assume(0) within conditional control flow,
10+
symex still executes the assertion as expected.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
CORE paths-lifo-expected-failure
2+
test.c
3+
--show-vcc
4+
^Generated 1 VCC\(s\), 1 remaining after simplification$
5+
^\{1\} false$
6+
^EXIT=0$
7+
^SIGNAL=0$
8+
--
9+
--
10+
Note: disabled for paths-lifo mode, which never merges state guards
11+
This checks that an assume(false) prior to a branch correctly results
12+
in symex picking some arbitrary path through the unreachable CFG, and so
13+
it returns the state guard to true when control flow converges.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
int main(int argc, char **argv)
2+
{
3+
if(argc > 1)
4+
{
5+
__CPROVER_assume(0);
6+
7+
if(argc == 2)
8+
{
9+
++argc;
10+
}
11+
else
12+
{
13+
--argc;
14+
}
15+
}
16+
17+
assert(0);
18+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
test.c
3+
4+
^VERIFICATION FAILED$
5+
^EXIT=10$
6+
^SIGNAL=0$
7+
--
8+
--
9+
This tests that with a __CPROVER_assume(0) prior to conditional control flow,
10+
symex executes the final assertion as expected.

src/analyses/guard_bdd.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,13 @@ class guard_bddt
6565
return guard_bddt(manager, bdd.bdd_not());
6666
}
6767

68+
/// Returns true if `operator|=` with \p other_guard may result in a simpler
69+
/// expression. For `bdd_exprt` we always simplify maximally.
70+
bool disjunction_may_simplify(const guard_bddt &other_guard)
71+
{
72+
return true;
73+
}
74+
6875
private:
6976
bdd_exprt &manager;
7077
bddt bdd;

src/analyses/guard_expr.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ Author: Daniel Kroening, [email protected]
1313

1414
#include <ostream>
1515

16-
#include <util/expr_util.h>
1716
#include <util/invariant.h>
1817
#include <util/simplify_utils.h>
1918
#include <util/std_expr.h>
@@ -91,6 +90,17 @@ guard_exprt &operator-=(guard_exprt &g1, const guard_exprt &g2)
9190
return g1;
9291
}
9392

93+
bool guard_exprt::disjunction_may_simplify(const guard_exprt &other_guard)
94+
{
95+
if(is_true() || is_false() || other_guard.is_true() || other_guard.is_false())
96+
return true;
97+
if(expr.id() == ID_and && other_guard.expr.id() == ID_and)
98+
return true;
99+
if(other_guard.expr == boolean_negate(expr))
100+
return true;
101+
return false;
102+
}
103+
94104
guard_exprt &operator|=(guard_exprt &g1, const guard_exprt &g2)
95105
{
96106
if(g2.is_false() || g1.is_true())

src/analyses/guard_expr.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Author: Daniel Kroening, [email protected]
1414

1515
#include <iosfwd>
1616

17+
#include <util/expr_util.h>
1718
#include <util/std_expr.h>
1819

1920
/// This is unused by this implementation of guards, but can be used by other
@@ -72,6 +73,11 @@ class guard_exprt
7273
friend guard_exprt &operator-=(guard_exprt &g1, const guard_exprt &g2);
7374
friend guard_exprt &operator|=(guard_exprt &g1, const guard_exprt &g2);
7475

76+
/// Returns true if `operator|=` with \p other_guard may result in a simpler
77+
/// expression. For `guard_exprt` in practice this means they're both
78+
/// conjunctions, since for other things we just OR them together.
79+
bool disjunction_may_simplify(const guard_exprt &other_guard);
80+
7581
private:
7682
exprt expr;
7783
};

src/goto-symex/goto_state.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ class goto_statet
4949
// of the condition of the if).
5050
guardt guard;
5151

52+
/// Is this code reachable? If not we can take shortcuts such as not entering
53+
/// function calls, but we still conduct guard arithmetic as usual.
54+
bool reachable;
55+
5256
// Map L1 names to (L2) constants. Values will be evicted from this map
5357
// when they become non-constant. This is used to propagate values that have
5458
// been worked out to only have one possible value.
@@ -73,7 +77,7 @@ class goto_statet
7377
explicit goto_statet(const class goto_symex_statet &s);
7478

7579
explicit goto_statet(guard_managert &guard_manager)
76-
: guard(true_exprt(), guard_manager)
80+
: guard(true_exprt(), guard_manager), reachable(true)
7781
{
7882
}
7983

src/goto-symex/goto_symex.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,9 @@ class goto_symext
332332
/// Symbolically execute a GOTO instruction
333333
/// \param state: Symbolic execution state for current instruction
334334
virtual void symex_goto(statet &state);
335+
/// Symbolically execute a GOTO instruction in the context of unreachable code
336+
/// \param state: Symbolic execution state for current instruction
337+
void symex_unreachable_goto(statet &state);
335338
/// Symbolically execute a START_THREAD instruction
336339
/// \param state: Symbolic execution state for current instruction
337340
virtual void symex_start_thread(statet &state);

src/goto-symex/goto_symex_state.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@ inline goto_statet::goto_statet(const class goto_symex_statet &s)
277277
level2(s.level2),
278278
value_set(s.value_set),
279279
guard(s.guard),
280+
reachable(s.reachable),
280281
propagation(s.propagation),
281282
atomic_section_id(s.atomic_section_id)
282283
{

src/goto-symex/symex_atomic_section.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Author: Daniel Kroening, [email protected]
1515

1616
void goto_symext::symex_atomic_begin(statet &state)
1717
{
18-
if(state.guard.is_false())
18+
if(!state.reachable)
1919
return;
2020

2121
if(state.atomic_section_id != 0)
@@ -35,7 +35,7 @@ void goto_symext::symex_atomic_begin(statet &state)
3535

3636
void goto_symext::symex_atomic_end(statet &state)
3737
{
38-
if(state.guard.is_false())
38+
if(!state.reachable)
3939
return;
4040

4141
if(state.atomic_section_id == 0)

src/goto-symex/symex_function_call.cpp

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -260,9 +260,6 @@ void goto_symext::symex_function_call_code(
260260

261261
// Rule out this path:
262262
symex_assume_l2(state, false_exprt());
263-
// Disable processing instructions until we next encounter one reachable
264-
// without passing this instruction:
265-
state.guard.add(false_exprt());
266263
}
267264

268265
symex_transition(state);
@@ -356,18 +353,15 @@ static void pop_frame(
356353
// accumulate assumptions (in symex_assume_l2) and must be left alone.
357354
// If however it is single-threaded then we should restore the guard, as the
358355
// guard coming out of the function may be more complex (e.g. if the callee
359-
// was { if(x) __CPROVER_assume(false); } then the guard may still be `!x`),
356+
// was { if(x) while(true) { } } then the guard may still be `!x`),
360357
// but at this point all control-flow paths have either converged or been
361358
// proven unviable, so we can stop specifying the callee's constraints when
362359
// we generate an assumption or VCC.
363360

364-
// If the guard is false, *this* path is unviable and we shouldn't discard
365-
// that knowledge. If we're doing path exploration then we do
366-
// tail-duplication, and we actually *are* in a more-restricted context
367-
// than we were when the function began.
368-
if(
369-
state.threads.size() == 1 && !state.guard.is_false() &&
370-
!doing_path_exploration)
361+
// If we're doing path exploration then we do tail-duplication, and we
362+
// actually *are* in a more-restricted context than we were when the
363+
// function began.
364+
if(state.threads.size() == 1 && !doing_path_exploration)
371365
{
372366
state.guard = frame.guard_at_function_start;
373367
}

0 commit comments

Comments
 (0)