Skip to content

Commit fad7616

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. 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 fad7616

File tree

9 files changed

+204
-28
lines changed

9 files changed

+204
-28
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#include <stdlib.h>
2+
#define SIZE 80
3+
4+
void main()
5+
{
6+
unsigned long len;
7+
__CPROVER_assume(len <= SIZE);
8+
__CPROVER_assume(len >= 8);
9+
char *array = malloc(len);
10+
__CPROVER_assume(array != 0);
11+
12+
array[len - 1] = 0;
13+
unsigned i = 0;
14+
15+
while(array[i] != 0)
16+
{
17+
i++;
18+
}
19+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
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+
Checks whether CBMC synthesizes loop invariants for checks 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+
Checks if synthesized contracts are dumped correctly.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#include <stdlib.h>
2+
#define SIZE 80
3+
4+
void main()
5+
{
6+
unsigned long len;
7+
__CPROVER_assume(len <= SIZE);
8+
__CPROVER_assume(len >= 8);
9+
char *array = malloc(len);
10+
__CPROVER_assume(array != 0);
11+
12+
unsigned i = 0;
13+
14+
while(i <= len - 2)
15+
{
16+
i++;
17+
}
18+
unsigned result = array[i];
19+
}
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+
^\[main.pointer\_dereference.\d+].*SUCCESS$
10+
^VERIFICATION SUCCESSFUL$
11+
--
12+
--
13+
Checks whether CBMC synthesizes loop invariants for checks located 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+
Checks if synthesized contracts are dumped correctly.

src/goto-synthesizer/cegis_verifier.cpp

Lines changed: 69 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,63 @@ std::list<loop_idt> cegis_verifiert::get_cause_loop_id(
211211
return result;
212212
}
213213

214-
bool cegis_verifiert::is_instruction_in_transfomed_loop(
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_transformed_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_transformed_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_transformed_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 is a set of instructions from
240+
// loop havocing instructions
241+
// to
242+
// if(!guard) GOTO EXIT
243+
unsigned location_number_of_havocing = 0;
244+
for(auto it = function.body.instructions.begin();
245+
it != function.body.instructions.end();
246+
++it)
247+
{
248+
// Record the location number of the beginning 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+
270+
bool cegis_verifiert::is_instruction_in_transformed_loop(
215271
const loop_idt &loop_id,
216272
const goto_functiont &function,
217273
unsigned location_number_of_target)
@@ -458,7 +514,7 @@ optionalt<cext> cegis_verifiert::verify()
458514
//
459515
// 1. annotate and apply the loop contracts stored in `invariant_candidates`.
460516
//
461-
// 2. run the CBMC API to verify the intrumented goto model. As the API is not
517+
// 2. run the CBMC API to verify the instrumented goto model. As the API is not
462518
// merged yet, we preprocess the goto model and run the symex checker on it
463519
// to simulate CBMC API.
464520
// TODO: ^^^ replace the symex checker once the real API is merged.
@@ -530,7 +586,7 @@ optionalt<cext> cegis_verifiert::verify()
530586
}
531587

532588
properties = checker->get_properties();
533-
// Find the violation and construct conterexample from its trace.
589+
// Find the violation and construct counterexample from its trace.
534590
for(const auto &property_it : properties)
535591
{
536592
if(property_it.second.status != property_statust::FAIL)
@@ -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: 26 additions & 7 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,16 +68,15 @@ 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 happens
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.
7076

7177
// __CPROVER_OBJECT_SIZE
7278
std::unordered_map<exprt, mp_integer, irep_hash> object_sizes;
73-
// all the valuation of havoced variables with primitived type.
79+
// all the valuation of havoced variables with primitive type.
7480
std::unordered_map<exprt, mp_integer, irep_hash> havoced_values;
7581
// __CPROVER_POINTER_OFFSET
7682
std::unordered_map<exprt, mp_integer, irep_hash> havoced_pointer_offsets;
@@ -88,7 +94,7 @@ class cext
8894
std::list<loop_idt> cause_loop_ids;
8995
};
9096

91-
/// Verifier that take a goto program as input, and ouptut formatted
97+
/// Verifier that take a goto program as input, and output formatted
9298
/// counterexamples for counterexample-guided-synthesis.
9399
class cegis_verifiert
94100
{
@@ -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

@@ -141,7 +153,14 @@ class cegis_verifiert
141153

142154
/// Decide whether the target instruction is in the body of the transformed
143155
/// loop specified by `loop_id`.
144-
bool is_instruction_in_transfomed_loop(
156+
bool is_instruction_in_transformed_loop(
157+
const loop_idt &loop_id,
158+
const goto_functiont &function,
159+
unsigned location_number_of_target);
160+
161+
/// Decide whether the target instruction is between the loop-havoc and the
162+
/// evaluation of the loop guard.
163+
bool is_instruction_in_transformed_loop_condition(
145164
const loop_idt &loop_id,
146165
const goto_functiont &function,
147166
unsigned location_number_of_target);
@@ -160,7 +179,7 @@ class cegis_verifiert
160179
std::unordered_map<goto_programt::const_targett, unsigned, const_target_hash>
161180
original_loop_number_map;
162181

163-
/// Loop havoc instructions instrumneted during applying loop contracts.
182+
/// Loop havoc instructions instrumented during applying loop contracts.
164183
std::unordered_set<goto_programt::const_targett, const_target_hash>
165184
loop_havoc_set;
166185
};

src/goto-synthesizer/enumerative_loop_contracts_synthesizer.cpp

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ exprt enumerative_loop_contracts_synthesizert::synthesize_same_object_predicate(
217217
{
218218
// The same object predicate says that the checked pointer points to the
219219
// same object as it pointed before entering the loop.
220-
// It works for the array-manuplating loops where only offset of pointer
220+
// It works for the array-manipulating loops where only offset of pointer
221221
// are modified but not the object pointers point to.
222222
return same_object(
223223
checked_pointer, unary_exprt(ID_loop_entry, checked_pointer));
@@ -228,7 +228,7 @@ exprt enumerative_loop_contracts_synthesizert::synthesize_strengthening_clause(
228228
const loop_idt &cause_loop_id,
229229
const irep_idt &violation_id)
230230
{
231-
// Synthesis of strengthning clauses is a enumerate-and-check proecess.
231+
// Synthesis of strengthening clauses is a enumerate-and-check process.
232232
// We first construct the enumerator for the following grammar.
233233
// And then enumerate clause and check that if it can make the invariant
234234
// inductive.
@@ -343,17 +343,17 @@ invariant_mapt enumerative_loop_contracts_synthesizert::synthesize_all()
343343
cegis_verifiert verifier(combined_invariant, assigns_map, goto_model, log);
344344

345345
// Set of symbols the violation may be dependent on.
346-
// We enumerate strenghening clauses built from symbols from the set.
346+
// We enumerate strengthening clauses built from symbols from the set.
347347
std::set<symbol_exprt> dependent_symbols;
348-
// Set of symbols we used to enumerate strenghening clauses.
348+
// Set of symbols we used to enumerate strengthening clauses.
349349
std::vector<exprt> terminal_symbols;
350350

351351
auto return_cex = verifier.verify();
352352

353353
while(return_cex.has_value())
354354
{
355355
exprt new_invariant_clause = true_exprt();
356-
// Synthsize the new_clause
356+
// Synthesize the new_clause
357357
// We use difference strategies for different type of violations.
358358
switch(return_cex->violation_type)
359359
{
@@ -394,7 +394,7 @@ invariant_mapt enumerative_loop_contracts_synthesizert::synthesize_all()
394394
new_invariant_clause != true_exprt(),
395395
"failed to synthesized meaningful clause");
396396

397-
// There could be tmp_post varialbes in the synthesized clause.
397+
// There could be tmp_post variables in the synthesized clause.
398398
// We substitute them with their original variables.
399399
replace_tmp_post(new_invariant_clause, tmp_post_map);
400400

@@ -403,15 +403,34 @@ invariant_mapt enumerative_loop_contracts_synthesizert::synthesize_all()
403403
dependent_symbols = compute_dependent_symbols(
404404
cause_loop_id, new_invariant_clause, return_cex->live_variables);
405405

406-
// add the new cluase to the candidate invariants.
407-
if(return_cex->is_violation_in_loop)
406+
// add the new clause to the candidate invariants.
407+
if(
408+
return_cex->violation_location ==
409+
cext::violation_locationt::in_condition)
410+
{
411+
// When the violation happens 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 happens 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 happens 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)