@@ -37,7 +37,6 @@ Date: February 2016
3737#include < util/pointer_predicates.h>
3838#include < util/replace_symbol.h>
3939
40- #include " assigns.h"
4140#include " memory_predicates.h"
4241#include " utils.h"
4342
@@ -637,7 +636,7 @@ void code_contractst::instrument_assign_statement(
637636 " The first instruction of instrument_assign_statement should always be"
638637 " an assignment" );
639638
640- add_containment_check (
639+ add_inclusion_check (
641640 program, assigns_clause, instruction_it, instruction_it->assign_lhs ());
642641}
643642
@@ -669,21 +668,98 @@ void code_contractst::instrument_call_statement(
669668 auto snapshot_instructions = car->generate_snapshot_instructions ();
670669 insert_before_swap_and_advance (
671670 body, instruction_it, snapshot_instructions);
672- // since CAR was inserted *after* the malloc,
671+ // since CAR was inserted *after* the malloc call ,
673672 // move the instruction pointer backward,
674673 // because the caller increments it in a `for` loop
675674 instruction_it--;
676675 }
677- return ;
678676 }
679677 else if (callee_name == " free" )
680678 {
681- add_containment_check (
679+ const auto free_car = add_inclusion_check (
682680 body,
683681 assigns,
684682 instruction_it,
685683 pointer_object (instruction_it->call_arguments ().front ()));
686- return ;
684+
685+ // skip all invalidation business if we're freeing invalid memory
686+ goto_programt alias_checking_instructions, skip_program;
687+ alias_checking_instructions.add (goto_programt::make_goto (
688+ skip_program.add (
689+ goto_programt::make_skip (instruction_it->source_location )),
690+ not_exprt{free_car.validity_condition_var },
691+ instruction_it->source_location ));
692+
693+ // Since the argument to free may be an "alias" (but not identical)
694+ // to existing CARs' source_expr, structural equality wouldn't work.
695+ // Moreover, multiple CARs in the writeset might be aliased to the
696+ // same underlying object.
697+ // So, we first find the corresponding CAR using `same_object` checks.
698+ std::unordered_set<exprt, irep_hash> write_set_validity_addrs;
699+ for (const auto &w_car : assigns.get_write_set ())
700+ {
701+ const auto object_validity_var_addr =
702+ new_tmp_symbol (
703+ pointer_type (bool_typet{}),
704+ instruction_it->source_location ,
705+ symbol_table.lookup_ref (function).mode ,
706+ symbol_table)
707+ .symbol_expr ();
708+ write_set_validity_addrs.insert (object_validity_var_addr);
709+
710+ alias_checking_instructions.add (goto_programt::make_decl (
711+ object_validity_var_addr, instruction_it->source_location ));
712+ // if the CAR was defined on the same_object as the one being `free`d,
713+ // record its validity variable's address, otherwise record NULL
714+ alias_checking_instructions.add (goto_programt::make_assignment (
715+ object_validity_var_addr,
716+ if_exprt{
717+ and_exprt{
718+ w_car.validity_condition_var ,
719+ same_object (
720+ free_car.lower_bound_address_var , w_car.lower_bound_address_var )},
721+ address_of_exprt{w_car.validity_condition_var },
722+ null_pointer_exprt{to_pointer_type (object_validity_var_addr.type ())}},
723+ instruction_it->source_location ));
724+ }
725+
726+ alias_checking_instructions.destructive_append (skip_program);
727+ insert_before_swap_and_advance (
728+ body, instruction_it, alias_checking_instructions);
729+
730+ // move past the call and then insert the invalidation instructions
731+ instruction_it++;
732+
733+ // skip all invalidation business if we're freeing invalid memory
734+ goto_programt invalidation_instructions;
735+ skip_program.clear ();
736+ invalidation_instructions.add (goto_programt::make_goto (
737+ skip_program.add (
738+ goto_programt::make_skip (instruction_it->source_location )),
739+ not_exprt{free_car.validity_condition_var },
740+ instruction_it->source_location ));
741+
742+ // invalidate all recorded CAR validity variables
743+ for (const auto &w_car_validity_addr : write_set_validity_addrs)
744+ {
745+ goto_programt w_car_skip_program;
746+ invalidation_instructions.add (goto_programt::make_goto (
747+ w_car_skip_program.add (
748+ goto_programt::make_skip (instruction_it->source_location )),
749+ null_pointer (w_car_validity_addr),
750+ instruction_it->source_location ));
751+ invalidation_instructions.add (goto_programt::make_assignment (
752+ dereference_exprt{w_car_validity_addr},
753+ false_exprt{},
754+ instruction_it->source_location ));
755+ invalidation_instructions.destructive_append (w_car_skip_program);
756+ }
757+
758+ invalidation_instructions.destructive_append (skip_program);
759+ insert_before_swap_and_advance (
760+ body, instruction_it, invalidation_instructions);
761+
762+ instruction_it--;
687763 }
688764}
689765
@@ -823,9 +899,9 @@ void code_contractst::check_frame_conditions(
823899 auto snapshot_instructions = car->generate_snapshot_instructions ();
824900 insert_before_swap_and_advance (
825901 body, instruction_it, snapshot_instructions);
826- // since CAR was inserted *after* the DECL,
902+ // since CAR was inserted *after* the DECL instruction ,
827903 // move the instruction pointer backward,
828- // because the caller increments it in a `for` loop
904+ // because the `for` loop takes care of the increment
829905 instruction_it--;
830906 }
831907 else if (instruction_it->is_assign ())
@@ -838,20 +914,42 @@ void code_contractst::check_frame_conditions(
838914 }
839915 else if (instruction_it->is_dead ())
840916 {
841- assigns.remove_from_write_set (instruction_it->dead_symbol ());
917+ const auto &symbol = instruction_it->dead_symbol ();
918+ // CAR equality and hash are defined on source_expr alone,
919+ // therefore this temporary CAR should be "found"
920+ const auto &symbol_car = assigns.get_write_set ().find (
921+ assigns_clauset::conditional_address_ranget{assigns, symbol});
922+ if (symbol_car != assigns.get_write_set ().end ())
923+ {
924+ instruction_it++;
925+ auto invalidation_assignment = goto_programt::make_assignment (
926+ symbol_car->validity_condition_var ,
927+ false_exprt{},
928+ instruction_it->source_location );
929+ // note that instruction_it is not advanced by this call,
930+ // so no need to move it backwards
931+ body.insert_before_swap (instruction_it, invalidation_assignment);
932+ }
933+ else
934+ {
935+ throw incorrect_goto_program_exceptiont (
936+ " Found a `DEAD` variable without corresponding `DECL`!" ,
937+ instruction_it->source_location );
938+ }
842939 }
843940 else if (
844941 instruction_it->is_other () &&
845942 instruction_it->get_other ().get_statement () == ID_havoc_object)
846943 {
847944 const exprt &havoc_argument = dereference_exprt (
848945 to_typecast_expr (instruction_it->get_other ().operands ().front ()).op ());
849- add_containment_check (body, assigns, instruction_it, havoc_argument);
946+ add_inclusion_check (body, assigns, instruction_it, havoc_argument);
850947 }
851948 }
852949}
853950
854- void code_contractst::add_containment_check (
951+ const assigns_clauset::conditional_address_ranget
952+ code_contractst::add_inclusion_check (
855953 goto_programt &program,
856954 const assigns_clauset &assigns,
857955 goto_programt::instructionst::iterator &instruction_it,
@@ -868,6 +966,8 @@ void code_contractst::add_containment_check(
868966 assertion.instructions .back ().source_location .set_comment (
869967 " Check that " + from_expr (ns, expr.id (), expr) + " is assignable" );
870968 insert_before_swap_and_advance (program, instruction_it, assertion);
969+
970+ return car;
871971}
872972
873973bool code_contractst::enforce_contract (const irep_idt &function)
0 commit comments