Skip to content

Commit 8a4fe6b

Browse files
committed
Concurrency: ensure apply_condition's value is used
goto_statet::apply_condition generates an entry (with fresh L2 index) in the propagation map with generating an assignment. We did not previously use the propagation map when handling atomic sections, and thus had a missing link between values, causing spurious verification failures. The regression test is based on SV-COMP's pthread-ext/23_lu-fig2.fixed.c. There is further room for improvement, as shown in the KNOWNBUG-regression test. This will be addressed in a separate commit.
1 parent 21d3ee8 commit 8a4fe6b

File tree

4 files changed

+106
-11
lines changed

4 files changed

+106
-11
lines changed
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#include <assert.h>
2+
3+
int start_main = 0;
4+
int mStartLock = 0;
5+
int __COUNT__ = 0;
6+
7+
void __VERIFIER_atomic_acquire()
8+
{
9+
__CPROVER_assume(mStartLock == 0);
10+
mStartLock = 1;
11+
}
12+
13+
void __VERIFIER_atomic_release()
14+
{
15+
__CPROVER_assume(mStartLock == 1);
16+
mStartLock = 0;
17+
}
18+
19+
void __VERIFIER_atomic_thr1()
20+
{
21+
if(__COUNT__ == 0)
22+
{
23+
__COUNT__ = __COUNT__ + 1;
24+
}
25+
else
26+
{
27+
assert(0);
28+
}
29+
}
30+
31+
void thr1()
32+
{
33+
__VERIFIER_atomic_acquire();
34+
start_main = 1;
35+
__VERIFIER_atomic_thr1();
36+
__VERIFIER_atomic_release();
37+
}
38+
39+
void __VERIFIER_atomic_thr2()
40+
{
41+
if(__COUNT__ == 1)
42+
{
43+
__COUNT__ = __COUNT__ + 1;
44+
}
45+
else
46+
{
47+
assert(0);
48+
}
49+
}
50+
51+
void thr2()
52+
{
53+
__CPROVER_assume(start_main != 0);
54+
__VERIFIER_atomic_acquire();
55+
__VERIFIER_atomic_release();
56+
__VERIFIER_atomic_thr2();
57+
}
58+
59+
int main()
60+
{
61+
__CPROVER_ASYNC_1:
62+
thr1();
63+
64+
thr2();
65+
66+
return 0;
67+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
KNOWNBUG
2+
main.c
3+
--program-only
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
--
7+
^warning: ignoring
8+
__COUNT__#\d+ == (1 \+ __COUNT__#\d+|__COUNT__#\d+ \+ 1)
9+
--
10+
__COUNT__ is guaranteed to have been read within the atomic section, there
11+
should not be a need to produce a conditional assignment, and therefore the
12+
value of __COUNT__ can be constant propagated.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
main.c
3+
4+
^VERIFICATION SUCCESSFUL$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
8+
^warning: ignoring

src/goto-symex/goto_symex_state.cpp

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -396,9 +396,9 @@ bool goto_symex_statet::l2_thread_read_encoding(
396396
guardt g = guard_in_list;
397397
g-=guard;
398398
if(g.is_true())
399-
// there has already been a write to l1_identifier within
400-
// this atomic section under the same guard, or a guard
401-
// that implies the current one
399+
// There has already been a write to l1_identifier within this atomic
400+
// section under the same guard, or a guard implied by the current
401+
// one.
402402
return false;
403403

404404
write_guard |= guard_in_list;
@@ -418,9 +418,8 @@ bool goto_symex_statet::l2_thread_read_encoding(
418418
guardt g = a_s_read_guard; // copy
419419
g-=guard;
420420
if(g.is_true())
421-
// there has already been a read l1_identifier within
422-
// this atomic section under the same guard, or a guard
423-
// that implies the current one
421+
// There has already been a read of l1_identifier within this atomic
422+
// section under the same guard, or a guard implied by the current one.
424423
return false;
425424

426425
read_guard |= a_s_read_guard;
@@ -430,20 +429,29 @@ bool goto_symex_statet::l2_thread_read_encoding(
430429
if(!no_write.op().is_false())
431430
cond |= guardt{no_write.op(), guard_manager};
432431

433-
const renamedt<ssa_exprt, L2> l2_true_case = set_indices<L2>(ssa_l1, ns);
432+
// It is safe to perform constant propagation in case we have read or
433+
// written this object within the atomic section. We must actually do this,
434+
// because goto_state::apply_condition may have placed the latest value in
435+
// the propagation map without recording an assignment.
436+
auto p_it = propagation.find(ssa_l1.get_identifier());
437+
const exprt l2_true_case =
438+
p_it.has_value() ? *p_it : set_indices<L2>(ssa_l1, ns).get();
434439

435-
if(a_s_read.second.empty())
436-
{
440+
if(!cond.is_true())
437441
level2.increase_generation(l1_identifier, ssa_l1, fresh_l2_name_provider);
442+
443+
if(a_s_read.second.empty())
438444
a_s_read.first = level2.latest_index(l1_identifier);
439-
}
445+
440446
const renamedt<ssa_exprt, L2> l2_false_case = set_indices<L2>(ssa_l1, ns);
441447

442448
exprt tmp;
443449
if(cond.is_false())
444450
tmp = l2_false_case.get();
451+
else if(cond.is_true())
452+
tmp = l2_true_case;
445453
else
446-
tmp = if_exprt{cond.as_expr(), l2_true_case.get(), l2_false_case.get()};
454+
tmp = if_exprt{cond.as_expr(), l2_true_case, l2_false_case.get()};
447455

448456
record_events.push(false);
449457
ssa_exprt ssa_l2 = assignment(std::move(ssa_l1), tmp, ns, true, true).get();

0 commit comments

Comments
 (0)