@@ -669,21 +669,75 @@ void code_contractst::instrument_call_statement(
669
669
auto snapshot_instructions = car->generate_snapshot_instructions ();
670
670
insert_before_swap_and_advance (
671
671
body, instruction_it, snapshot_instructions);
672
- // since CAR was inserted *after* the malloc,
672
+ // since CAR was inserted *after* the malloc call ,
673
673
// move the instruction pointer backward,
674
674
// because the caller increments it in a `for` loop
675
675
instruction_it--;
676
676
}
677
- return ;
678
677
}
679
678
else if (callee_name == " free" )
680
679
{
681
- add_containment_check (
682
- body,
683
- assigns,
684
- instruction_it,
685
- pointer_object (instruction_it->call_arguments ().front ()));
686
- return ;
680
+ const auto &pointer = instruction_it->call_arguments ().front ();
681
+ const auto object = pointer_object (pointer);
682
+ add_containment_check (body, assigns, instruction_it, object);
683
+
684
+ // Since the argument to free would be an "alias" (but not identical)
685
+ // to an existing CAR's source_expr, structural equality wouldn't work.
686
+ // Moreover, multiple CARs in the writeset might be aliased to the
687
+ // same underlying object.
688
+ // So, we first find the corresponding CAR using `same_object` checks.
689
+ goto_programt invalidation_instructions;
690
+ std::unordered_set<exprt, irep_hash> temps;
691
+
692
+ for (const auto &car : assigns.get_write_set ())
693
+ {
694
+ const auto object_validity_var_addr =
695
+ new_tmp_symbol (
696
+ pointer_type (bool_typet{}),
697
+ instruction_it->source_location ,
698
+ symbol_table.lookup_ref (function).mode ,
699
+ symbol_table)
700
+ .symbol_expr ();
701
+ temps.insert (object_validity_var_addr);
702
+
703
+ invalidation_instructions.add (goto_programt::make_decl (
704
+ object_validity_var_addr, instruction_it->source_location ));
705
+ // if the CAR was defined on the same_object as the one being `free`d,
706
+ // record its validity variable's address, otherwise record NULL
707
+ invalidation_instructions.add (goto_programt::make_assignment (
708
+ object_validity_var_addr,
709
+ if_exprt{
710
+ and_exprt{car.validity_condition_var ,
711
+ same_object (pointer, car.lower_bound_address_var )},
712
+ address_of_exprt{car.validity_condition_var },
713
+ null_pointer_exprt{to_pointer_type (object_validity_var_addr.type ())}},
714
+ instruction_it->source_location ));
715
+ }
716
+
717
+ insert_before_swap_and_advance (
718
+ body, instruction_it, invalidation_instructions);
719
+
720
+ // move past the call and then insert the invalidation instructions
721
+ instruction_it++;
722
+ // invalidate all recorded CAR validity variables
723
+ for (const auto &car_validity_addr : temps)
724
+ {
725
+ goto_programt skip_program;
726
+ const auto skip_target = skip_program.add (
727
+ goto_programt::make_skip (instruction_it->source_location ));
728
+ invalidation_instructions.add (goto_programt::make_goto (
729
+ skip_target,
730
+ null_pointer (car_validity_addr),
731
+ instruction_it->source_location ));
732
+ invalidation_instructions.add (goto_programt::make_assignment (
733
+ dereference_exprt{car_validity_addr},
734
+ false_exprt{},
735
+ instruction_it->source_location ));
736
+ invalidation_instructions.destructive_append (skip_program);
737
+ }
738
+ insert_before_swap_and_advance (
739
+ body, instruction_it, invalidation_instructions);
740
+ instruction_it--;
687
741
}
688
742
}
689
743
@@ -823,9 +877,9 @@ void code_contractst::check_frame_conditions(
823
877
auto snapshot_instructions = car->generate_snapshot_instructions ();
824
878
insert_before_swap_and_advance (
825
879
body, instruction_it, snapshot_instructions);
826
- // since CAR was inserted *after* the DECL,
880
+ // since CAR was inserted *after* the DECL instruction ,
827
881
// move the instruction pointer backward,
828
- // because the caller increments it in a `for` loop
882
+ // because the `for` loop takes care of the increment
829
883
instruction_it--;
830
884
}
831
885
else if (instruction_it->is_assign ())
@@ -838,7 +892,28 @@ void code_contractst::check_frame_conditions(
838
892
}
839
893
else if (instruction_it->is_dead ())
840
894
{
841
- assigns.remove_from_write_set (instruction_it->dead_symbol ());
895
+ const auto &symbol = instruction_it->dead_symbol ();
896
+ // CAR equality and hash are defined on source_expr alone,
897
+ // therefore this temporary CAR should be "found"
898
+ const auto &symbol_car = assigns.get_write_set ().find (
899
+ assigns_clauset::conditional_address_ranget{assigns, symbol});
900
+ if (symbol_car != assigns.get_write_set ().end ())
901
+ {
902
+ instruction_it++;
903
+ auto invalidation_assignment = goto_programt::make_assignment (
904
+ symbol_car->validity_condition_var ,
905
+ false_exprt{},
906
+ instruction_it->source_location );
907
+ // note that instruction_it is not advanced by this call,
908
+ // so no need to move it backwards
909
+ body.insert_before_swap (instruction_it, invalidation_assignment);
910
+ }
911
+ else
912
+ {
913
+ throw incorrect_goto_program_exceptiont (
914
+ " Found a `DEAD` variable without corresponding `DECL`!" ,
915
+ instruction_it->source_location );
916
+ }
842
917
}
843
918
else if (
844
919
instruction_it->is_other () &&
0 commit comments