Skip to content

Commit dbbfd16

Browse files
committed
Synthesize loop assigns before loop invariants.
Loop invariants synthesis is dependent on loop-assigns clauses. So we don't target non assignable-violations until all assignable-violations are fixed.
1 parent 942e797 commit dbbfd16

File tree

5 files changed

+198
-118
lines changed

5 files changed

+198
-118
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
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+
const char *i = malloc(len);
10+
const char *end = i + len;
11+
char s = 0;
12+
13+
while(i != end)
14+
{
15+
s = *i++;
16+
}
17+
}
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.pointer\_dereference.\d+\] .* SUCCESS$
7+
^\[main.\d+\] .* Check loop invariant before entry: SUCCESS$
8+
^\[main.assigns.\d+\] .* Check that i is assignable: SUCCESS$
9+
^VERIFICATION SUCCESSFUL$
10+
--
11+
--
12+
Check whether assigns clauses are synthesize before invariant clauses.

src/goto-synthesizer/cegis_verifier.cpp

Lines changed: 161 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,37 @@ static bool contains_symbol_prefix(const exprt &expr, const std::string &prefix)
5353
return false;
5454
}
5555

56+
static const exprt &
57+
get_checked_pointer_from_null_pointer_check(const exprt &violation)
58+
{
59+
// A NULL-pointer check is the negation of an equation between the checked
60+
// pointer and a NULL pointer.
61+
// ! (POINTER_OBJECT(NULL) == POINTER_OBJECT(ptr))
62+
const equal_exprt &equal_expr = to_equal_expr(to_not_expr(violation).op());
63+
64+
const exprt &lhs_pointer_object = equal_expr.lhs();
65+
const exprt &rhs_pointer_object = equal_expr.rhs();
66+
INVARIANT(
67+
lhs_pointer_object.id() == ID_pointer_object &&
68+
rhs_pointer_object.id() == ID_pointer_object,
69+
"Null pointer check is an equation between two pointer objects of "
70+
"pointer-type operands.");
71+
72+
const exprt &lhs_pointer = lhs_pointer_object.operands()[0];
73+
const exprt &rhs_pointer = rhs_pointer_object.operands()[0];
74+
75+
// NULL == ptr
76+
if(
77+
can_cast_expr<constant_exprt>(lhs_pointer) &&
78+
is_null_pointer(*expr_try_dynamic_cast<constant_exprt>(lhs_pointer)))
79+
{
80+
return rhs_pointer;
81+
}
82+
83+
// Not a equation with NULL on one side.
84+
UNREACHABLE;
85+
}
86+
5687
optionst cegis_verifiert::get_options()
5788
{
5889
optionst options;
@@ -94,6 +125,45 @@ optionst cegis_verifiert::get_options()
94125
return options;
95126
}
96127

128+
cext::violation_typet
129+
cegis_verifiert::extract_violation_type(const std::string &description)
130+
{
131+
cext::violation_typet violation_type = cext::violation_typet::cex_other;
132+
133+
// The violation is a pointer OOB check.
134+
if((description.find(
135+
"dereference failure: pointer outside object bounds in") !=
136+
std::string::npos))
137+
{
138+
violation_type = cext::violation_typet::cex_out_of_boundary;
139+
}
140+
141+
// The violation is a null pointer check.
142+
if(description.find("pointer NULL") != std::string::npos)
143+
{
144+
violation_type = cext::violation_typet::cex_null_pointer;
145+
}
146+
147+
// The violation is a loop-invariant-preservation check.
148+
if(description.find("preserved") != std::string::npos)
149+
{
150+
violation_type = cext::violation_typet::cex_not_preserved;
151+
}
152+
153+
// The violation is a loop-invariant-preservation check.
154+
if(description.find("invariant before entry") != std::string::npos)
155+
{
156+
violation_type = cext::violation_typet::cex_not_hold_upon_entry;
157+
}
158+
159+
// The violation is an assignable check.
160+
if(description.find("assignable") != std::string::npos)
161+
{
162+
violation_type = cext::violation_typet::cex_assignable;
163+
}
164+
return violation_type;
165+
}
166+
97167
std::list<loop_idt>
98168
cegis_verifiert::get_cause_loop_id_for_assigns(const goto_tracet &goto_trace)
99169
{
@@ -586,143 +656,118 @@ optionalt<cext> cegis_verifiert::verify()
586656
}
587657

588658
properties = checker->get_properties();
589-
// Find the violation and construct counterexample from its trace.
590-
for(const auto &property_it : properties)
659+
bool target_violation_found = false;
660+
auto target_violation_info = properties.begin()->second;
661+
662+
// Find target violation---the violation we want to fix next.
663+
// A target violation is an assignable violation or the first violation that
664+
// is not assignable violation.
665+
for(const auto &property : properties)
591666
{
592-
if(property_it.second.status != property_statust::FAIL)
667+
if(property.second.status != property_statust::FAIL)
593668
continue;
594669

595-
// Store the violation that we want to fix with synthesized
596-
// assigns/invariant.
597-
first_violation = property_it.first;
598-
599-
// Type of the violation
600-
cext::violation_typet violation_type = cext::violation_typet::cex_other;
601-
602-
// Decide the violation type from the description of violation
603-
604-
// The violation is a pointer OOB check.
605-
if((property_it.second.description.find(
606-
"dereference failure: pointer outside object bounds in") !=
607-
std::string::npos))
670+
// assignable violation found
671+
if(property.second.description.find("assignable") != std::string::npos)
608672
{
609-
violation_type = cext::violation_typet::cex_out_of_boundary;
610-
}
611-
612-
// The violation is a null pointer check.
613-
if(property_it.second.description.find("pointer NULL") != std::string::npos)
614-
{
615-
violation_type = cext::violation_typet::cex_null_pointer;
616-
}
617-
618-
// The violation is a loop-invariant-preservation check.
619-
if(property_it.second.description.find("preserved") != std::string::npos)
620-
{
621-
violation_type = cext::violation_typet::cex_not_preserved;
622-
}
623-
624-
// The violation is a loop-invariant-preservation check.
625-
if(
626-
property_it.second.description.find("invariant before entry") !=
627-
std::string::npos)
628-
{
629-
violation_type = cext::violation_typet::cex_not_hold_upon_entry;
673+
target_violation = property.first;
674+
target_violation_info = property.second;
675+
break;
630676
}
631677

632-
// The violation is an assignable check.
633-
if(property_it.second.description.find("assignable") != std::string::npos)
678+
// Store the violation that we want to fix with synthesized
679+
// assigns/invariant.
680+
if(!target_violation_found)
634681
{
635-
violation_type = cext::violation_typet::cex_assignable;
682+
target_violation = property.first;
683+
target_violation_info = property.second;
684+
target_violation_found = true;
636685
}
686+
}
637687

638-
// Compute the cause loop---the loop for which we synthesize loop contracts,
639-
// and the counterexample.
640-
641-
// If the violation is an assignable check, we synthesize assigns targets.
642-
// In the case, cause loops are all loops the violation is in. We keep
643-
// adding the new assigns target to the most-inner loop that does not
644-
// contain the new target until the assignable violation is resolved.
645-
646-
// For other cases, we synthesize loop invariant clauses. We synthesize
647-
// invariants for one loop at a time. So we return only the first cause loop
648-
// although there can be multiple ones.
688+
// Decide the violation type from the description of violation
689+
cext::violation_typet violation_type =
690+
extract_violation_type(target_violation_info.description);
649691

650-
log.debug() << "Start to compute cause loop ids." << messaget::eom;
692+
// Compute the cause loop---the loop for which we synthesize loop contracts,
693+
// and the counterexample.
651694

652-
const auto &trace = checker->get_traces()[property_it.first];
695+
// If the violation is an assignable check, we synthesize assigns targets.
696+
// In the case, cause loops are all loops the violation is in. We keep
697+
// adding the new assigns target to the most-inner loop that does not
698+
// contain the new target until the assignable violation is resolved.
653699

654-
// Doing assigns-synthesis or invariant-synthesis
655-
if(violation_type == cext::violation_typet::cex_assignable)
656-
{
657-
cext result(violation_type);
658-
result.cause_loop_ids = get_cause_loop_id_for_assigns(trace);
659-
result.checked_pointer = static_cast<const exprt &>(
660-
property_it.second.pc->condition().find(ID_checked_assigns));
661-
restore_functions();
662-
return result;
663-
}
700+
// For other cases, we synthesize loop invariant clauses. We synthesize
701+
// invariants for one loop at a time. So we return only the first cause loop
702+
// although there can be multiple ones.
664703

665-
// We construct the full counterexample only for violations other than
666-
// assignable checks.
704+
log.debug() << "Start to compute cause loop ids." << messaget::eom;
667705

668-
// Although there can be multiple cause loop ids. We only synthesize
669-
// loop invariants for the first cause loop.
670-
const std::list<loop_idt> cause_loop_ids =
671-
get_cause_loop_id(trace, property_it.second.pc);
706+
const auto &trace = checker->get_traces()[target_violation];
707+
// Doing assigns-synthesis or invariant-synthesis
708+
if(violation_type == cext::violation_typet::cex_assignable)
709+
{
710+
cext result(violation_type);
711+
result.cause_loop_ids = get_cause_loop_id_for_assigns(trace);
712+
result.checked_pointer = static_cast<const exprt &>(
713+
target_violation_info.pc->condition().find(ID_checked_assigns));
714+
restore_functions();
715+
return result;
716+
}
672717

673-
if(cause_loop_ids.empty())
674-
{
675-
log.debug() << "No cause loop found!" << messaget::eom;
676-
restore_functions();
718+
// We construct the full counterexample only for violations other than
719+
// assignable checks.
677720

678-
return cext(violation_type);
679-
}
721+
// Although there can be multiple cause loop ids. We only synthesize
722+
// loop invariants for the first cause loop.
723+
const std::list<loop_idt> cause_loop_ids =
724+
get_cause_loop_id(trace, target_violation_info.pc);
680725

681-
log.debug() << "Found cause loop with function id: "
682-
<< cause_loop_ids.front().function_id
683-
<< ", and loop number: " << cause_loop_ids.front().loop_number
684-
<< messaget::eom;
726+
if(cause_loop_ids.empty())
727+
{
728+
log.debug() << "No cause loop found!" << messaget::eom;
729+
restore_functions();
685730

686-
auto violation_location = cext::violation_locationt::in_loop;
687-
// We always strengthen in_clause if the violation is
688-
// invariant-not-preserved.
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-
}
731+
return cext(violation_type);
732+
}
697733

698-
restore_functions();
734+
log.debug() << "Found cause loop with function id: "
735+
<< cause_loop_ids.front().function_id
736+
<< ", and loop number: " << cause_loop_ids.front().loop_number
737+
<< messaget::eom;
699738

700-
auto return_cex = build_cex(
701-
trace,
702-
get_loop_head(
703-
cause_loop_ids.front().loop_number,
704-
goto_model.goto_functions
705-
.function_map[cause_loop_ids.front().function_id])
706-
->source_location());
707-
return_cex.violated_predicate = property_it.second.pc->condition();
708-
return_cex.cause_loop_ids = cause_loop_ids;
709-
return_cex.violation_location = violation_location;
710-
return_cex.violation_type = violation_type;
711-
712-
// The pointer checked in the null-pointer-check violation.
713-
if(violation_type == cext::violation_typet::cex_null_pointer)
714-
{
715-
return_cex.checked_pointer = property_it.second.pc->condition()
716-
.operands()[0]
717-
.operands()[1]
718-
.operands()[0];
719-
INVARIANT(
720-
return_cex.checked_pointer.id() == ID_symbol,
721-
"Checking pointer symbol");
722-
}
739+
auto violation_location = cext::violation_locationt::in_loop;
740+
// We always strengthen in_clause if the violation is
741+
// invariant-not-preserved.
742+
if(violation_type != cext::violation_typet::cex_not_preserved)
743+
{
744+
// Get the location of the violation
745+
violation_location = get_violation_location(
746+
cause_loop_ids.front(),
747+
goto_model.get_goto_function(cause_loop_ids.front().function_id),
748+
target_violation_info.pc->location_number);
749+
}
723750

724-
return return_cex;
751+
restore_functions();
752+
753+
auto return_cex = build_cex(
754+
trace,
755+
get_loop_head(
756+
cause_loop_ids.front().loop_number,
757+
goto_model.goto_functions
758+
.function_map[cause_loop_ids.front().function_id])
759+
->source_location());
760+
return_cex.violated_predicate = target_violation_info.pc->condition();
761+
return_cex.cause_loop_ids = cause_loop_ids;
762+
return_cex.violation_location = violation_location;
763+
return_cex.violation_type = violation_type;
764+
765+
// The pointer checked in the null-pointer-check violation.
766+
if(violation_type == cext::violation_typet::cex_null_pointer)
767+
{
768+
return_cex.checked_pointer = get_checked_pointer_from_null_pointer_check(
769+
target_violation_info.pc->condition());
725770
}
726771

727-
UNREACHABLE;
772+
return return_cex;
728773
}

src/goto-synthesizer/cegis_verifier.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ class cegis_verifiert
117117

118118
/// Result counterexample.
119119
propertiest properties;
120-
irep_idt first_violation;
120+
irep_idt target_violation;
121121

122122
protected:
123123
// Get the options same as using CBMC api without any flag, and
@@ -137,6 +137,9 @@ class cegis_verifiert
137137
std::list<loop_idt>
138138
get_cause_loop_id_for_assigns(const goto_tracet &goto_trace);
139139

140+
// Extract the violation type from violation description.
141+
cext::violation_typet extract_violation_type(const std::string &description);
142+
140143
// Compute the location of the violation.
141144
cext::violation_locationt get_violation_location(
142145
const loop_idt &loop_id,

src/goto-synthesizer/enumerative_loop_contracts_synthesizer.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ void enumerative_loop_contracts_synthesizert::init_candidates()
9494

9595
loop_idt new_id(function_p.first, loop_end->loop_number);
9696

97+
log.debug() << "Initialize candidates for the loop at "
98+
<< loop_end->source_location() << messaget::eom;
99+
97100
// we only synthesize invariants and assigns for unannotated loops
98101
if(loop_end->condition().find(ID_C_spec_loop_invariant).is_nil())
99102
{
@@ -372,7 +375,7 @@ invariant_mapt enumerative_loop_contracts_synthesizert::synthesize_all()
372375
new_invariant_clause = synthesize_strengthening_clause(
373376
terminal_symbols,
374377
return_cex->cause_loop_ids.front(),
375-
verifier.first_violation);
378+
verifier.target_violation);
376379
break;
377380

378381
case cext::violation_typet::cex_assignable:

0 commit comments

Comments
 (0)