Skip to content

Commit ef6005d

Browse files
committed
Symex: restore guards on function exit
Executing a function may have a cumulative effect on the state guard. For example, if the callee contained ASSUME statements that rendered one or more control-flow options unviable then the guard might still embody that restriction (i.e. for if(x) ASSUME(false) the guard might still be `!x`). However, on function return we know that all control-flow paths have converged or been shown to be unviable, so we can restore the simpler guard as it was when we entered the callee function. Exceptions: (a) if the guard is false it would be correct but inefficient to restore it; keep it false until we find a convergeance with another viable path (b) if we're doing path-sensitive symex then we do tail duplication, and there are no control-flow convergeances. Keep the guard as-was. (c) if we're executing a multi-threaded program then symex_assume_l2 uses the guard to accumulate assumptions, which we must not discard. In truth this optimisation is applicable whenever a block postdominates another, but function structure gives us a cheap way to establish postdominance without analysis (in the absence of setjmp/longjmp at least)
1 parent 719e9a9 commit ef6005d

File tree

18 files changed

+216
-10
lines changed

18 files changed

+216
-10
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
void g()
2+
{
3+
g();
4+
}
5+
6+
void f(int y)
7+
{
8+
if(y == 5)
9+
{
10+
g();
11+
y = 10;
12+
}
13+
}
14+
15+
int main(int argc, char **argv)
16+
{
17+
f(argc);
18+
assert(argc == 1);
19+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
CORE paths-lifo-expected-failure
2+
test.c
3+
--show-vcc --unwind 10
4+
^\{1\} main::argc!0@1#1 = 1$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
8+
^\{-[0-9]+\} f::y!0@1#[0-9]+ = 10$
9+
--
10+
This checks that the assertion, argc == 1, is unguarded (i.e. is not of the form
11+
'guard => condition'). Such a guard is redundant, but could be added before goto-symex
12+
made sure to restore guards to their state at function entry.
13+
We also check that no VCC is created for the unreachable code 'y = 10;'
14+
--paths mode is excluded as it currently always accrues large guards as it proceeds through execution
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CORE
2+
test.c
3+
--unwind 10
4+
^\[main\.assertion\.[0-9]+\] line [0-9]+ assertion argc == 1: FAILURE$
5+
^VERIFICATION FAILED$
6+
^EXIT=10$
7+
^SIGNAL=0$
8+
--
9+
--
10+
The main test is in test.desc; this just checks that the test is executable and
11+
the assertion fails as expected.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
void f(int y)
2+
{
3+
if(y == 5)
4+
{
5+
for(int i = 0; i < 20; ++i)
6+
{
7+
}
8+
y = 10;
9+
}
10+
}
11+
12+
int main(int argc, char **argv)
13+
{
14+
f(argc);
15+
assert(argc == 1);
16+
}
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 --unwind 10
4+
^\{1\} main::argc!0@1#1 = 1$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
8+
--
9+
This checks that the assertion, argc == 1, is unguarded (i.e. is not of the form
10+
'guard => condition'). Such a guard is redundant, but could be added before goto-symex
11+
made sure to restore guards to their state at function entry.
12+
--paths mode is excluded as it currently always accrues large guards as it proceeds through execution
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CORE
2+
test.c
3+
--unwind 10
4+
^\[main\.assertion\.[0-9]+\] line [0-9]+ assertion argc == 1: FAILURE$
5+
^VERIFICATION FAILED$
6+
^EXIT=10$
7+
^SIGNAL=0$
8+
--
9+
--
10+
The main test is in test.desc; this just checks that the test is executable and
11+
the assertion fails as expected.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
FUTURE
2+
test.c
3+
--show-vcc --unwind 10
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
--
7+
^\{-[0-9]+\} f::y!0@1#[0-9]+ = 10$
8+
--
9+
We also check that no VCC is created for the unreachable code 'y = 10;'
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
void g(int x)
2+
{
3+
return g(x + 1);
4+
}
5+
6+
void f(int y)
7+
{
8+
if(y == 5)
9+
{
10+
g(1);
11+
y = 10;
12+
}
13+
}
14+
15+
int main(int argc, char **argv)
16+
{
17+
f(argc);
18+
assert(argc == 1);
19+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
CORE paths-lifo-expected-failure
2+
test.c
3+
--show-vcc --depth 1000
4+
^\{1\} main::argc!0@1#1 = 1$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
8+
^\{-[0-9]+\} f::y!0@1#[0-9]+ = 10$
9+
--
10+
This checks that the assertion, argc == 1, is unguarded (i.e. is not of the form
11+
'guard => condition'). Such a guard is redundant, but could be added before goto-symex
12+
made sure to restore guards to their state at function entry.
13+
We also check that no VCC is created for the unreachable code 'y = 10;'
14+
--paths mode is excluded as it currently always accrues large guards as it proceeds through execution
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CORE
2+
test.c
3+
--depth 1000
4+
^\[main\.assertion\.[0-9]+\] line [0-9]+ assertion argc == 1: FAILURE$
5+
^VERIFICATION FAILED$
6+
^EXIT=10$
7+
^SIGNAL=0$
8+
--
9+
--
10+
The main test is in test.desc; this just checks that the test is executable and
11+
the assertion fails as expected.

0 commit comments

Comments
 (0)