@@ -37,7 +37,6 @@ Date: February 2016
37
37
#include < util/pointer_predicates.h>
38
38
#include < util/replace_symbol.h>
39
39
40
- #include " assigns.h"
41
40
#include " memory_predicates.h"
42
41
#include " utils.h"
43
42
@@ -669,21 +668,98 @@ void code_contractst::instrument_call_statement(
669
668
auto snapshot_instructions = car->generate_snapshot_instructions ();
670
669
insert_before_swap_and_advance (
671
670
body, instruction_it, snapshot_instructions);
672
- // since CAR was inserted *after* the malloc,
671
+ // since CAR was inserted *after* the malloc call ,
673
672
// move the instruction pointer backward,
674
673
// because the caller increments it in a `for` loop
675
674
instruction_it--;
676
675
}
677
- return ;
678
676
}
679
677
else if (callee_name == " free" )
680
678
{
681
- add_inclusion_check (
679
+ const auto free_car = add_inclusion_check (
682
680
body,
683
681
assigns,
684
682
instruction_it,
685
683
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--;
687
763
}
688
764
}
689
765
@@ -823,9 +899,9 @@ void code_contractst::check_frame_conditions(
823
899
auto snapshot_instructions = car->generate_snapshot_instructions ();
824
900
insert_before_swap_and_advance (
825
901
body, instruction_it, snapshot_instructions);
826
- // since CAR was inserted *after* the DECL,
902
+ // since CAR was inserted *after* the DECL instruction ,
827
903
// 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
829
905
instruction_it--;
830
906
}
831
907
else if (instruction_it->is_assign ())
@@ -838,7 +914,28 @@ void code_contractst::check_frame_conditions(
838
914
}
839
915
else if (instruction_it->is_dead ())
840
916
{
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
+ }
842
939
}
843
940
else if (
844
941
instruction_it->is_other () &&
@@ -851,7 +948,8 @@ void code_contractst::check_frame_conditions(
851
948
}
852
949
}
853
950
854
- void code_contractst::add_inclusion_check (
951
+ const assigns_clauset::conditional_address_ranget
952
+ code_contractst::add_inclusion_check (
855
953
goto_programt &program,
856
954
const assigns_clauset &assigns,
857
955
goto_programt::instructionst::iterator &instruction_it,
@@ -868,6 +966,8 @@ void code_contractst::add_inclusion_check(
868
966
assertion.instructions .back ().source_location .set_comment (
869
967
" Check that " + from_expr (ns, expr.id (), expr) + " is assignable" );
870
968
insert_before_swap_and_advance (program, instruction_it, assertion);
969
+
970
+ return car;
871
971
}
872
972
873
973
bool code_contractst::enforce_contract (const irep_idt &function)
0 commit comments