Skip to content

Commit 019952e

Browse files
authored
Merge pull request #7457 from qinheping/goto-synthesizer-violation-location
SYNTHESIZER: Handle the case of violation in loop guards
2 parents a69540e + 43f71f3 commit 019952e

File tree

9 files changed

+206
-30
lines changed

9 files changed

+206
-30
lines changed
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+
}
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.
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.
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+
}
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.
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

+71-14
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,9 +514,9 @@ 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
462-
// merged yet, we preprocess the goto model and run the symex checker on it
463-
// to simulate CBMC API.
517+
// 2. run the CBMC API to verify the instrumented goto model. As the API is
518+
// not merged yet, we preprocess the goto model and run the symex checker
519+
// on it to simulate CBMC API.
464520
// TODO: ^^^ replace the symex checker once the real API is merged.
465521
//
466522
// 3. construct the formatted counterexample from the violated property and
@@ -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

+26-7
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

+28-9
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)