@@ -53,6 +53,34 @@ 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 pointer_object_exprt &lhs_pointer_object =
65+ to_pointer_object_expr (equal_expr.lhs ());
66+ const pointer_object_exprt &rhs_pointer_object =
67+ to_pointer_object_expr (equal_expr.rhs ());
68+
69+ const exprt &lhs_pointer = lhs_pointer_object.operands ()[0 ];
70+ const exprt &rhs_pointer = rhs_pointer_object.operands ()[0 ];
71+
72+ // NULL == ptr
73+ if (
74+ can_cast_expr<constant_exprt>(lhs_pointer) &&
75+ is_null_pointer (*expr_try_dynamic_cast<constant_exprt>(lhs_pointer)))
76+ {
77+ return rhs_pointer;
78+ }
79+
80+ // Not a equation with NULL on one side.
81+ UNREACHABLE;
82+ }
83+
5684optionst cegis_verifiert::get_options ()
5785{
5886 optionst options;
@@ -94,6 +122,44 @@ optionst cegis_verifiert::get_options()
94122 return options;
95123}
96124
125+ cext::violation_typet
126+ cegis_verifiert::extract_violation_type (const std::string &description)
127+ {
128+ // The violation is a pointer OOB check.
129+ if ((description.find (
130+ " dereference failure: pointer outside object bounds in" ) !=
131+ std::string::npos))
132+ {
133+ return cext::violation_typet::cex_out_of_boundary;
134+ }
135+
136+ // The violation is a null pointer check.
137+ if (description.find (" pointer NULL" ) != std::string::npos)
138+ {
139+ return cext::violation_typet::cex_null_pointer;
140+ }
141+
142+ // The violation is a loop-invariant-preservation check.
143+ if (description.find (" preserved" ) != std::string::npos)
144+ {
145+ return cext::violation_typet::cex_not_preserved;
146+ }
147+
148+ // The violation is a loop-invariant-preservation check.
149+ if (description.find (" invariant before entry" ) != std::string::npos)
150+ {
151+ return cext::violation_typet::cex_not_hold_upon_entry;
152+ }
153+
154+ // The violation is an assignable check.
155+ if (description.find (" assignable" ) != std::string::npos)
156+ {
157+ return cext::violation_typet::cex_assignable;
158+ }
159+
160+ return cext::violation_typet::cex_other;
161+ }
162+
97163std::list<loop_idt>
98164cegis_verifiert::get_cause_loop_id_for_assigns (const goto_tracet &goto_trace)
99165{
@@ -586,143 +652,118 @@ optionalt<cext> cegis_verifiert::verify()
586652 }
587653
588654 properties = checker->get_properties ();
589- // Find the violation and construct counterexample from its trace.
590- for (const auto &property_it : properties)
655+ bool target_violation_found = false ;
656+ auto target_violation_info = properties.begin ()->second ;
657+
658+ // Find target violation---the violation we want to fix next.
659+ // A target violation is an assignable violation or the first violation that
660+ // is not assignable violation.
661+ for (const auto &property : properties)
591662 {
592- if (property_it .second .status != property_statust::FAIL)
663+ if (property .second .status != property_statust::FAIL)
593664 continue ;
594665
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))
608- {
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)
666+ // assignable violation found
667+ if (property.second .description .find (" assignable" ) != std::string::npos)
614668 {
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;
669+ target_violation = property.first ;
670+ target_violation_info = property.second ;
671+ break ;
630672 }
631673
632- // The violation is an assignable check.
633- if (property_it.second .description .find (" assignable" ) != std::string::npos)
674+ // Store the violation that we want to fix with synthesized
675+ // assigns/invariant.
676+ if (!target_violation_found)
634677 {
635- violation_type = cext::violation_typet::cex_assignable;
678+ target_violation = property.first ;
679+ target_violation_info = property.second ;
680+ target_violation_found = true ;
636681 }
682+ }
637683
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.
684+ // Decide the violation type from the description of violation
685+ cext::violation_typet violation_type =
686+ extract_violation_type (target_violation_info.description );
649687
650- log.debug () << " Start to compute cause loop ids." << messaget::eom;
688+ // Compute the cause loop---the loop for which we synthesize loop contracts,
689+ // and the counterexample.
651690
652- const auto &trace = checker->get_traces ()[property_it.first ];
691+ // If the violation is an assignable check, we synthesize assigns targets.
692+ // In the case, cause loops are all loops the violation is in. We keep
693+ // adding the new assigns target to the most-inner loop that does not
694+ // contain the new target until the assignable violation is resolved.
653695
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- }
696+ // For other cases, we synthesize loop invariant clauses. We synthesize
697+ // invariants for one loop at a time. So we return only the first cause loop
698+ // although there can be multiple ones.
664699
665- // We construct the full counterexample only for violations other than
666- // assignable checks.
700+ log.debug () << " Start to compute cause loop ids." << messaget::eom;
667701
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 );
702+ const auto &trace = checker->get_traces ()[target_violation];
703+ // Doing assigns-synthesis or invariant-synthesis
704+ if (violation_type == cext::violation_typet::cex_assignable)
705+ {
706+ cext result (violation_type);
707+ result.cause_loop_ids = get_cause_loop_id_for_assigns (trace);
708+ result.checked_pointer = static_cast <const exprt &>(
709+ target_violation_info.pc ->condition ().find (ID_checked_assigns));
710+ restore_functions ();
711+ return result;
712+ }
672713
673- if (cause_loop_ids.empty ())
674- {
675- log.debug () << " No cause loop found!" << messaget::eom;
676- restore_functions ();
714+ // We construct the full counterexample only for violations other than
715+ // assignable checks.
677716
678- return cext (violation_type);
679- }
717+ // Although there can be multiple cause loop ids. We only synthesize
718+ // loop invariants for the first cause loop.
719+ const std::list<loop_idt> cause_loop_ids =
720+ get_cause_loop_id (trace, target_violation_info.pc );
680721
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 ;
722+ if (cause_loop_ids. empty ())
723+ {
724+ log. debug () << " No cause loop found! " << messaget::eom;
725+ restore_functions () ;
685726
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- }
727+ return cext (violation_type);
728+ }
697729
698- restore_functions ();
730+ log.debug () << " Found cause loop with function id: "
731+ << cause_loop_ids.front ().function_id
732+ << " , and loop number: " << cause_loop_ids.front ().loop_number
733+ << messaget::eom;
699734
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- }
735+ auto violation_location = cext::violation_locationt::in_loop;
736+ // We always strengthen in_clause if the violation is
737+ // invariant-not-preserved.
738+ if (violation_type != cext::violation_typet::cex_not_preserved)
739+ {
740+ // Get the location of the violation
741+ violation_location = get_violation_location (
742+ cause_loop_ids.front (),
743+ goto_model.get_goto_function (cause_loop_ids.front ().function_id ),
744+ target_violation_info.pc ->location_number );
745+ }
723746
724- return return_cex;
747+ restore_functions ();
748+
749+ auto return_cex = build_cex (
750+ trace,
751+ get_loop_head (
752+ cause_loop_ids.front ().loop_number ,
753+ goto_model.goto_functions
754+ .function_map [cause_loop_ids.front ().function_id ])
755+ ->source_location ());
756+ return_cex.violated_predicate = target_violation_info.pc ->condition ();
757+ return_cex.cause_loop_ids = cause_loop_ids;
758+ return_cex.violation_location = violation_location;
759+ return_cex.violation_type = violation_type;
760+
761+ // The pointer checked in the null-pointer-check violation.
762+ if (violation_type == cext::violation_typet::cex_null_pointer)
763+ {
764+ return_cex.checked_pointer = get_checked_pointer_from_null_pointer_check (
765+ target_violation_info.pc ->condition ());
725766 }
726767
727- UNREACHABLE ;
768+ return return_cex ;
728769}
0 commit comments