Skip to content

Commit 9a392f4

Browse files
committed
Handle the case of violation in loop guards in loop-invariant synthesis
In current loop-invariant synthesizer, we synthesize two kinds of clauses separately. 1. `in_clause` that should hold when we enter the loop body---when the loop guard holds, that is ```loop_guard -> in_clause```. 2. `pos_clause` that should hold when we leave the loop---when the loop guard doesn't hold, that is ```!loop_guard -> pos_clause```. We synthesize `in_clause` when the violations happen in the loop body. We synthesize `pos_clause` when the violation happen after the loop. This PR handles the case that the violations happen in the loop guard ([loop_contracts_synthesis_06/main.c](https://github.com/diffblue/cbmc/compare/develop...qinheping:goto-synthesizer-violation-location?expand=1#diff-8a05899ddc66b61918f2e6180bfc08f09f895f6a78066c65086a5e7c470a880a) as an example). In the case, the new clause we synthesize should hold for both of the cases that loop guard holds and loop guards doesn't hold. We introduce a new variable `violation_location` to indicate whether the violation happen in the loop, after the loop, or in the loop guard, and add the new synthesized clause as `in_clause`, `pos_clause`, or both, respectively.
1 parent 531e768 commit 9a392f4

File tree

9 files changed

+190
-14
lines changed

9 files changed

+190
-14
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#define SIZE 80
2+
3+
void main()
4+
{
5+
unsigned long len;
6+
__CPROVER_assume(len <= SIZE);
7+
__CPROVER_assume(len >= 8);
8+
char *array = malloc(len);
9+
__CPROVER_assume(array != 0);
10+
11+
array[len - 1] = 0;
12+
unsigned i = 0;
13+
14+
while(array[i] != 0)
15+
{
16+
i++;
17+
}
18+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
CORE
2+
main.c
3+
--pointer-check
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^\[main.*\d+\] .* Check loop invariant before entry: SUCCESS$
7+
^\[main.assigns.\d+\] .* Check that i is assignable: SUCCESS$
8+
^\[main.*\d+\] .* Check that loop invariant is preserved: SUCCESS$
9+
^VERIFICATION SUCCESSFUL$
10+
--
11+
--
12+
This test is a variation of strlen. It checks that we can synthesize
13+
loop invariant for checks happen in the loop guard.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CORE
2+
main.c
3+
--pointer-check _ --dump-loop-contracts
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
loop 1 invariant.*\_\_CPROVER\_POINTER\_OFFSET
7+
assigns i
8+
--
9+
This test case checks if synthesized contracts are correctly dumped.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#define SIZE 80
2+
3+
void main()
4+
{
5+
unsigned long len;
6+
__CPROVER_assume(len <= SIZE);
7+
__CPROVER_assume(len >= 8);
8+
char *array = malloc(len);
9+
__CPROVER_assume(array != 0);
10+
11+
unsigned i = 0;
12+
13+
while(i <= len - 2)
14+
{
15+
i++;
16+
}
17+
unsigned result = array[i];
18+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
CORE
2+
main.c
3+
--pointer-check
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^\[main.\d+\] .* Check loop invariant before entry: SUCCESS$
7+
^\[main.assigns.\d+\] .* Check that i is assignable: SUCCESS$
8+
^\[main.\d+\] .* Check that loop invariant is preserved: SUCCESS$
9+
^\[main.pointer\_dereference.\d+].*SUCCESS$
10+
^VERIFICATION SUCCESSFUL$
11+
--
12+
--
13+
This test checks that we can synthesize loop invariant for violation happen
14+
after the loop body.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CORE
2+
main.c
3+
--pointer-check _ --dump-loop-contracts
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
loop 1 assigns i
7+
loop 1 invariant.*\_\_CPROVER\_POINTER\_OFFSET
8+
--
9+
This test case checks if synthesized contracts are correctly dumped.

src/goto-synthesizer/cegis_verifier.cpp

Lines changed: 66 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,62 @@ std::list<loop_idt> cegis_verifiert::get_cause_loop_id(
211211
return result;
212212
}
213213

214+
cext::violation_locationt cegis_verifiert::get_violation_location(
215+
const loop_idt &loop_id,
216+
const goto_functiont &function,
217+
unsigned location_number_of_target)
218+
{
219+
if(is_instruction_in_transfomed_loop_condition(
220+
loop_id, function, location_number_of_target))
221+
{
222+
return cext::violation_locationt::in_condition;
223+
}
224+
225+
if(is_instruction_in_transfomed_loop(
226+
loop_id, function, location_number_of_target))
227+
{
228+
return cext::violation_locationt::in_loop;
229+
}
230+
231+
return cext::violation_locationt::after_loop;
232+
}
233+
234+
bool cegis_verifiert::is_instruction_in_transfomed_loop_condition(
235+
const loop_idt &loop_id,
236+
const goto_functiont &function,
237+
unsigned location_number_of_target)
238+
{
239+
// The transformed loop condition are instructions from
240+
// loop havocing instructions
241+
// to
242+
// if(!guard) GOTO EXIT
243+
unsigned location_number_of_havocing = 0;
244+
for(goto_programt::const_targett it = function.body.instructions.begin();
245+
it != function.body.instructions.end();
246+
++it)
247+
{
248+
// Record the location number of the begin of a transformed loop.
249+
if(
250+
loop_havoc_set.count(it) &&
251+
original_loop_number_map[it] == loop_id.loop_number)
252+
{
253+
location_number_of_havocing = it->location_number;
254+
}
255+
256+
// Reach the end of the evaluation of the transformed loop condition.
257+
if(location_number_of_havocing != 0 && it->is_goto())
258+
{
259+
if((location_number_of_havocing < location_number_of_target &&
260+
location_number_of_target < it->location_number))
261+
{
262+
return true;
263+
}
264+
location_number_of_havocing = 0;
265+
}
266+
}
267+
return false;
268+
}
269+
214270
bool cegis_verifiert::is_instruction_in_transfomed_loop(
215271
const loop_idt &loop_id,
216272
const goto_functiont &function,
@@ -622,21 +678,22 @@ optionalt<cext> cegis_verifiert::verify()
622678
return cext(violation_type);
623679
}
624680

625-
// Decide whether the violation is in the cause loop.
626-
bool is_violation_in_loop = is_instruction_in_transfomed_loop(
627-
cause_loop_ids.front(),
628-
goto_model.get_goto_function(cause_loop_ids.front().function_id),
629-
property_it.second.pc->location_number);
630-
631681
log.debug() << "Found cause loop with function id: "
632682
<< cause_loop_ids.front().function_id
633683
<< ", and loop number: " << cause_loop_ids.front().loop_number
634684
<< messaget::eom;
635685

686+
auto violation_location = cext::violation_locationt::in_loop;
636687
// We always strengthen in_clause if the violation is
637688
// invariant-not-preserved.
638-
if(violation_type == cext::violation_typet::cex_not_preserved)
639-
is_violation_in_loop = true;
689+
if(violation_type != cext::violation_typet::cex_not_preserved)
690+
{
691+
// Get the location of the violation
692+
violation_location = get_violation_location(
693+
cause_loop_ids.front(),
694+
goto_model.get_goto_function(cause_loop_ids.front().function_id),
695+
property_it.second.pc->location_number);
696+
}
640697

641698
restore_functions();
642699

@@ -649,7 +706,7 @@ optionalt<cext> cegis_verifiert::verify()
649706
->source_location());
650707
return_cex.violated_predicate = property_it.second.pc->condition();
651708
return_cex.cause_loop_ids = cause_loop_ids;
652-
return_cex.is_violation_in_loop = is_violation_in_loop;
709+
return_cex.violation_location = violation_location;
653710
return_cex.violation_type = violation_type;
654711

655712
// The pointer checked in the null-pointer-check violation.

src/goto-synthesizer/cegis_verifier.h

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,13 @@ class cext
3535
cex_other
3636
};
3737

38+
enum class violation_locationt
39+
{
40+
in_loop,
41+
after_loop,
42+
in_condition
43+
};
44+
3845
cext(
3946
const std::unordered_map<exprt, mp_integer, irep_hash> &object_sizes,
4047
const std::unordered_map<exprt, mp_integer, irep_hash> &havoced_values,
@@ -61,9 +68,8 @@ class cext
6168
exprt checked_pointer;
6269
exprt violated_predicate;
6370

64-
// true if the violation happens in the cause loop
65-
// false if the violation happens after the cause loop
66-
bool is_violation_in_loop = true;
71+
// Location where the violation happen.
72+
violation_locationt violation_location = violation_locationt::in_loop;
6773

6874
// We collect havoced evaluations of havoced variables and their object sizes
6975
// and pointer offsets.
@@ -131,6 +137,12 @@ class cegis_verifiert
131137
std::list<loop_idt>
132138
get_cause_loop_id_for_assigns(const goto_tracet &goto_trace);
133139

140+
// Compute the location of the violation.
141+
cext::violation_locationt get_violation_location(
142+
const loop_idt &loop_id,
143+
const goto_functiont &function,
144+
unsigned location_number_of_target);
145+
134146
/// Restore transformed functions to original functions.
135147
void restore_functions();
136148

@@ -146,6 +158,13 @@ class cegis_verifiert
146158
const goto_functiont &function,
147159
unsigned location_number_of_target);
148160

161+
/// Decide whether the target instruction is between the loop-havoc and the
162+
/// evaluation of the loop guard.
163+
bool is_instruction_in_transfomed_loop_condition(
164+
const loop_idt &loop_id,
165+
const goto_functiont &function,
166+
unsigned location_number_of_target);
167+
149168
const invariant_mapt &invariant_candidates;
150169
const std::map<loop_idt, std::set<exprt>> &assigns_map;
151170
goto_modelt &goto_model;

src/goto-synthesizer/enumerative_loop_contracts_synthesizer.cpp

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -404,14 +404,33 @@ invariant_mapt enumerative_loop_contracts_synthesizert::synthesize_all()
404404
cause_loop_id, new_invariant_clause, return_cex->live_variables);
405405

406406
// add the new cluase to the candidate invariants.
407-
if(return_cex->is_violation_in_loop)
407+
if(
408+
return_cex->violation_location ==
409+
cext::violation_locationt::in_condition)
410+
{
411+
// When the violation is happen in the loop guard, the new clause
412+
// should hold for the both cases of
413+
// 1. loop guard holds --- loop_guard -> in_invariant
414+
// 2. loop guard doesn't hold --- !loop_guard -> pos_invariant
415+
in_invariant_clause_map[cause_loop_id] = and_exprt(
416+
in_invariant_clause_map[cause_loop_id], new_invariant_clause);
417+
pos_invariant_clause_map[cause_loop_id] = and_exprt(
418+
pos_invariant_clause_map[cause_loop_id], new_invariant_clause);
419+
}
420+
else if(
421+
return_cex->violation_location == cext::violation_locationt::in_loop)
408422
{
423+
// When the violation is happen in the loop body, the new clause
424+
// should hold for the case of
425+
// loop guard holds --- loop_guard -> in_invariant
409426
in_invariant_clause_map[cause_loop_id] = and_exprt(
410427
in_invariant_clause_map[cause_loop_id], new_invariant_clause);
411428
}
412429
else
413430
{
414-
// violation happens post-loop.
431+
// When the violation is happen after the loop body, the new clause
432+
// should hold for the case of
433+
// loop guard doesn't hold --- !loop_guard -> pos_invariant
415434
pos_invariant_clause_map[cause_loop_id] = and_exprt(
416435
pos_invariant_clause_map[cause_loop_id], new_invariant_clause);
417436
}

0 commit comments

Comments
 (0)