@@ -25,6 +25,9 @@ Date: January 2022
2525
2626#include " utils.h"
2727
28+ // / header for log messages
29+ static const char LOG_HEADER[] = " assigns clause checking: " ;
30+
2831// / Pragma used to mark assignments to static locals that need to be propagated
2932static const char PROPAGATE_STATIC_LOCAL_PRAGMA[] =
3033 " contracts:propagate-static-local" ;
@@ -694,8 +697,9 @@ exprt instrument_spec_assignst::inclusion_check_full(
694697 }
695698
696699 if (allow_null_lhs)
697- return or_exprt{null_pointer (car.target_start_address ()),
698- and_exprt{car.valid_var (), disjunction (disjuncts)}};
700+ return or_exprt{
701+ null_pointer (car.target_start_address ()),
702+ and_exprt{car.valid_var (), disjunction (disjuncts)}};
699703 else
700704 return and_exprt{car.valid_var (), disjunction (disjuncts)};
701705}
@@ -716,6 +720,8 @@ const car_exprt &instrument_spec_assignst::create_car_from_spec_assigns(
716720 }
717721 else
718722 {
723+ log.debug () << LOG_HEADER << " creating CAR for assigns clause target "
724+ << format (condition) << " : " << format (target) << messaget::eom;
719725 from_spec_assigns.insert ({key, create_car_expr (condition, target)});
720726 return from_spec_assigns.find (key)->second ;
721727 }
@@ -734,6 +740,8 @@ const car_exprt &instrument_spec_assignst::create_car_from_stack_alloc(
734740 }
735741 else
736742 {
743+ log.debug () << LOG_HEADER << " creating CAR for stack-allocated target "
744+ << format (target) << messaget::eom;
737745 from_stack_alloc.insert ({target, create_car_expr (true_exprt{}, target)});
738746 return from_stack_alloc.find (target)->second ;
739747 }
@@ -752,6 +760,8 @@ instrument_spec_assignst::create_car_from_heap_alloc(const exprt &target)
752760 }
753761 else
754762 {
763+ log.debug () << LOG_HEADER << " creating CAR for heap-allocated target "
764+ << format (target) << messaget::eom;
755765 from_heap_alloc.insert ({target, create_car_expr (true_exprt{}, target)});
756766 return from_heap_alloc.find (target)->second ;
757767 }
@@ -770,6 +780,8 @@ const car_exprt &instrument_spec_assignst::create_car_from_static_local(
770780 }
771781 else
772782 {
783+ log.debug () << LOG_HEADER << " creating CAR for static local target "
784+ << format (target) << messaget::eom;
773785 from_static_local.insert ({target, create_car_expr (true_exprt{}, target)});
774786 return from_static_local.find (target)->second ;
775787 }
@@ -809,44 +821,107 @@ bool instrument_spec_assignst::must_check_assign(
809821 skip_function_paramst skip_function_params,
810822 const optionalt<cfg_infot> cfg_info_opt)
811823{
812- if (
813- const auto &symbol_expr =
814- expr_try_dynamic_cast <symbol_exprt>(target->assign_lhs ()))
824+ log. debug (). source_location = target-> source_location ();
825+
826+ if (can_cast_expr <symbol_exprt>(target->assign_lhs ()))
815827 {
828+ const auto &symbol_expr = to_symbol_expr (target->assign_lhs ());
816829 if (
817830 skip_function_params == skip_function_paramst::NO &&
818- ns.lookup (symbol_expr-> get_identifier ()).is_parameter )
831+ ns.lookup (symbol_expr. get_identifier ()).is_parameter )
819832 {
833+ log.debug () << LOG_HEADER << " checking assignment to function parameter "
834+ << format (symbol_expr) << messaget::eom;
820835 return true ;
821836 }
822837
823838 if (cfg_info_opt.has_value ())
824- return !cfg_info_opt.value ().is_local (symbol_expr->get_identifier ());
839+ {
840+ if (cfg_info_opt.value ().is_local (symbol_expr.get_identifier ()))
841+ {
842+ log.debug () << LOG_HEADER
843+ << " skipping checking on assignment to local symbol "
844+ << format (symbol_expr) << messaget::eom;
845+ return false ;
846+ }
847+ else
848+ {
849+ log.debug () << LOG_HEADER << " checking assignment to non-local symbol "
850+ << format (symbol_expr) << messaget::eom;
851+ return true ;
852+ }
853+ }
854+ log.debug () << LOG_HEADER << " checking assignment to symbol "
855+ << format (symbol_expr) << messaget::eom;
856+ return true ;
857+ }
858+ else
859+ {
860+ // This is not a mere symbol.
861+ // Since non-dirty locals are not tracked explicitly in the write set,
862+ // we need to skip the check if we can verify that the expression describes
863+ // an access to a non-dirty local symbol or an input parameter,
864+ // otherwise the check will fail.
865+ // In addition, the expression shall not contain address_of or dereference
866+ // operators, regardless of the base symbol/object on which they apply.
867+ // If the expression contains an address_of operation, the assignment gets
868+ // checked. If the base object is a local or a parameter, it will also be
869+ // flagged as dirty and will be tracked explicitly, and the check will pass.
870+ // If the expression contains a dereference operation, the assignment gets
871+ // checked. If the dereferenced address was computed from a local object,
872+ // from a function parameter or returned by a local malloc,
873+ // then the object will be tracked explicitly and the check will pass.
874+ // In all other cases (address of a non-local object, or dereference of
875+ // a non-locally computed address) the location must be given explicitly
876+ // in the assigns clause to be tracked and we must check the assignment.
877+ if (
878+ cfg_info_opt.has_value () &&
879+ cfg_info_opt.value ().is_local_composite_access (target->assign_lhs ()))
880+ {
881+ log.debug ()
882+ << LOG_HEADER
883+ << " skipping check on assignment to local composite member expression "
884+ << format (target->assign_lhs ()) << messaget::eom;
885+ return false ;
886+ }
887+ log.debug () << LOG_HEADER << " checking assignment to expression "
888+ << format (target->assign_lhs ()) << messaget::eom;
889+ return true ;
825890 }
891+ }
826892
827- return true ;
893+ // / Track the symbol iff we have no cfg_infot, or we have a cfg_infot and the
894+ // / symbol is not a local or is a dirty local.
895+ bool instrument_spec_assignst::must_track_decl_or_dead (
896+ const irep_idt &ident,
897+ const optionalt<cfg_infot> &cfg_info_opt) const
898+ {
899+ return !cfg_info_opt.has_value () ||
900+ (cfg_info_opt.has_value () &&
901+ cfg_info_opt.value ().is_not_local_or_dirty_local (ident));
828902}
829903
830- // / Returns true iff a `DECL x` must be added to the local write set.
831- // /
832- // / A variable is called 'dirty' if its address gets taken at some point in
833- // / the program.
834- // /
835- // / Assuming the goto program is obtained from a structured C program that
836- // / passed C compiler checks, non-dirty variables can only be assigned to
837- // / directly by name, cannot escape their lexical scope, and are always safe
838- // / to assign. Hence, we only track dirty variables in the write set.
904+ // / Returns true iff a `DECL x` must be explicitly tracked in the write set.
839905bool instrument_spec_assignst::must_track_decl (
840906 const goto_programt::const_targett &target,
841907 const optionalt<cfg_infot> &cfg_info_opt) const
842908{
843- if (cfg_info_opt.has_value ())
909+ log.debug ().source_location = target->source_location ();
910+ if (must_track_decl_or_dead (
911+ target->decl_symbol ().get_identifier (), cfg_info_opt))
844912 {
845- return cfg_info_opt.value ().is_not_local_or_dirty_local (
846- target->decl_symbol ().get_identifier ());
913+ log.debug () << LOG_HEADER << " explicitly tracking "
914+ << format (target->decl_symbol ()) << " as assignable"
915+ << messaget::eom;
916+ return true ;
917+ }
918+ else
919+ {
920+ log.debug () << LOG_HEADER << " implicitly tracking "
921+ << format (target->decl_symbol ())
922+ << " as assignable (non-dirty local)" << messaget::eom;
923+ return false ;
847924 }
848- // Unless proved non-dirty by the CFG analysis we assume it is dirty.
849- return true ;
850925}
851926
852927// / Returns true iff a `DEAD x` must be processed to upate the local write set.
@@ -855,12 +930,8 @@ bool instrument_spec_assignst::must_track_dead(
855930 const goto_programt::const_targett &target,
856931 const optionalt<cfg_infot> &cfg_info_opt) const
857932{
858- // Unless proved non-dirty by the CFG analysis we assume it is dirty.
859- if (!cfg_info_opt.has_value ())
860- return true ;
861-
862- return cfg_info_opt.value ().is_not_local_or_dirty_local (
863- target->dead_symbol ().get_identifier ());
933+ return must_track_decl_or_dead (
934+ target->dead_symbol ().get_identifier (), cfg_info_opt);
864935}
865936
866937void instrument_spec_assignst::instrument_assign_statement (
0 commit comments