@@ -53,6 +53,44 @@ static bool contains_symbol_prefix(const exprt &expr, const std::string &prefix)
53
53
return false ;
54
54
}
55
55
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
+ // ptr == NULL
83
+ else if (
84
+ can_cast_expr<constant_exprt>(rhs_pointer) &&
85
+ is_null_pointer (*expr_try_dynamic_cast<constant_exprt>(rhs_pointer)))
86
+ {
87
+ return lhs_pointer;
88
+ }
89
+
90
+ // Not a equation with NULL on one side.
91
+ UNREACHABLE;
92
+ }
93
+
56
94
optionst cegis_verifiert::get_options ()
57
95
{
58
96
optionst options;
@@ -94,6 +132,45 @@ optionst cegis_verifiert::get_options()
94
132
return options;
95
133
}
96
134
135
+ cext::violation_typet
136
+ cegis_verifiert::extract_violation_type (const std::string &description)
137
+ {
138
+ cext::violation_typet violation_type = cext::violation_typet::cex_other;
139
+
140
+ // The violation is a pointer OOB check.
141
+ if ((description.find (
142
+ " dereference failure: pointer outside object bounds in" ) !=
143
+ std::string::npos))
144
+ {
145
+ violation_type = cext::violation_typet::cex_out_of_boundary;
146
+ }
147
+
148
+ // The violation is a null pointer check.
149
+ if (description.find (" pointer NULL" ) != std::string::npos)
150
+ {
151
+ violation_type = cext::violation_typet::cex_null_pointer;
152
+ }
153
+
154
+ // The violation is a loop-invariant-preservation check.
155
+ if (description.find (" preserved" ) != std::string::npos)
156
+ {
157
+ violation_type = cext::violation_typet::cex_not_preserved;
158
+ }
159
+
160
+ // The violation is a loop-invariant-preservation check.
161
+ if (description.find (" invariant before entry" ) != std::string::npos)
162
+ {
163
+ violation_type = cext::violation_typet::cex_not_hold_upon_entry;
164
+ }
165
+
166
+ // The violation is an assignable check.
167
+ if (description.find (" assignable" ) != std::string::npos)
168
+ {
169
+ violation_type = cext::violation_typet::cex_assignable;
170
+ }
171
+ return violation_type;
172
+ }
173
+
97
174
std::list<loop_idt>
98
175
cegis_verifiert::get_cause_loop_id_for_assigns (const goto_tracet &goto_trace)
99
176
{
@@ -586,143 +663,118 @@ optionalt<cext> cegis_verifiert::verify()
586
663
}
587
664
588
665
properties = checker->get_properties ();
589
- // Find the violation and construct counterexample from its trace.
590
- for (const auto &property_it : properties)
666
+ bool target_violation_found = false ;
667
+ auto target_violation_info = properties.begin ()->second ;
668
+
669
+ // Find target violation---the violation we want to fix next.
670
+ // A target violation is an assignable violation or the first violation that
671
+ // is not assignable violation.
672
+ for (const auto &property : properties)
591
673
{
592
- if (property_it .second .status != property_statust::FAIL)
674
+ if (property .second .status != property_statust::FAIL)
593
675
continue ;
594
676
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))
677
+ // assignable violation found
678
+ if (property.second .description .find (" assignable" ) != std::string::npos)
608
679
{
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;
680
+ target_violation = property.first ;
681
+ target_violation_info = property.second ;
682
+ break ;
630
683
}
631
684
632
- // The violation is an assignable check.
633
- if (property_it.second .description .find (" assignable" ) != std::string::npos)
685
+ // Store the violation that we want to fix with synthesized
686
+ // assigns/invariant.
687
+ if (!target_violation_found)
634
688
{
635
- violation_type = cext::violation_typet::cex_assignable;
689
+ target_violation = property.first ;
690
+ target_violation_info = property.second ;
691
+ target_violation_found = true ;
636
692
}
693
+ }
637
694
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.
695
+ // Decide the violation type from the description of violation
696
+ cext::violation_typet violation_type =
697
+ extract_violation_type (target_violation_info.description );
649
698
650
- log .debug () << " Start to compute cause loop ids." << messaget::eom;
699
+ // Compute the cause loop---the loop for which we synthesize loop contracts,
700
+ // and the counterexample.
651
701
652
- const auto &trace = checker->get_traces ()[property_it.first ];
702
+ // If the violation is an assignable check, we synthesize assigns targets.
703
+ // In the case, cause loops are all loops the violation is in. We keep
704
+ // adding the new assigns target to the most-inner loop that does not
705
+ // contain the new target until the assignable violation is resolved.
653
706
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
- }
707
+ // For other cases, we synthesize loop invariant clauses. We synthesize
708
+ // invariants for one loop at a time. So we return only the first cause loop
709
+ // although there can be multiple ones.
664
710
665
- // We construct the full counterexample only for violations other than
666
- // assignable checks.
711
+ log .debug () << " Start to compute cause loop ids." << messaget::eom;
667
712
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 );
713
+ const auto &trace = checker->get_traces ()[target_violation];
714
+ // Doing assigns-synthesis or invariant-synthesis
715
+ if (violation_type == cext::violation_typet::cex_assignable)
716
+ {
717
+ cext result (violation_type);
718
+ result.cause_loop_ids = get_cause_loop_id_for_assigns (trace);
719
+ result.checked_pointer = static_cast <const exprt &>(
720
+ target_violation_info.pc ->condition ().find (ID_checked_assigns));
721
+ restore_functions ();
722
+ return result;
723
+ }
672
724
673
- if (cause_loop_ids.empty ())
674
- {
675
- log .debug () << " No cause loop found!" << messaget::eom;
676
- restore_functions ();
725
+ // We construct the full counterexample only for violations other than
726
+ // assignable checks.
677
727
678
- return cext (violation_type);
679
- }
728
+ // Although there can be multiple cause loop ids. We only synthesize
729
+ // loop invariants for the first cause loop.
730
+ const std::list<loop_idt> cause_loop_ids =
731
+ get_cause_loop_id (trace, target_violation_info.pc );
680
732
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 ;
733
+ if (cause_loop_ids. empty ())
734
+ {
735
+ log . debug () << " No cause loop found! " << messaget::eom;
736
+ restore_functions () ;
685
737
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
- }
738
+ return cext (violation_type);
739
+ }
697
740
698
- restore_functions ();
741
+ log .debug () << " Found cause loop with function id: "
742
+ << cause_loop_ids.front ().function_id
743
+ << " , and loop number: " << cause_loop_ids.front ().loop_number
744
+ << messaget::eom;
699
745
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
- }
746
+ auto violation_location = cext::violation_locationt::in_loop;
747
+ // We always strengthen in_clause if the violation is
748
+ // invariant-not-preserved.
749
+ if (violation_type != cext::violation_typet::cex_not_preserved)
750
+ {
751
+ // Get the location of the violation
752
+ violation_location = get_violation_location (
753
+ cause_loop_ids.front (),
754
+ goto_model.get_goto_function (cause_loop_ids.front ().function_id ),
755
+ target_violation_info.pc ->location_number );
756
+ }
723
757
724
- return return_cex;
758
+ restore_functions ();
759
+
760
+ auto return_cex = build_cex (
761
+ trace,
762
+ get_loop_head (
763
+ cause_loop_ids.front ().loop_number ,
764
+ goto_model.goto_functions
765
+ .function_map [cause_loop_ids.front ().function_id ])
766
+ ->source_location ());
767
+ return_cex.violated_predicate = target_violation_info.pc ->condition ();
768
+ return_cex.cause_loop_ids = cause_loop_ids;
769
+ return_cex.violation_location = violation_location;
770
+ return_cex.violation_type = violation_type;
771
+
772
+ // The pointer checked in the null-pointer-check violation.
773
+ if (violation_type == cext::violation_typet::cex_null_pointer)
774
+ {
775
+ return_cex.checked_pointer = get_checked_pointer_from_null_pointer_check (
776
+ target_violation_info.pc ->condition ());
725
777
}
726
778
727
- UNREACHABLE ;
779
+ return return_cex ;
728
780
}
0 commit comments