Skip to content

Commit baa88bd

Browse files
committed
Fix invalidation for DEAD and free instructions
1 parent 60d683f commit baa88bd

File tree

3 files changed

+140
-11
lines changed

3 files changed

+140
-11
lines changed
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#include <assert.h>
2+
#include <stdlib.h>
3+
4+
int foo(int *x) __CPROVER_assigns(*x)
5+
{
6+
{
7+
int y;
8+
y = 1;
9+
*x = 0;
10+
// y goes DEAD here
11+
}
12+
13+
int a = 1;
14+
15+
if(x == NULL)
16+
{
17+
return 1;
18+
// a goes DEAD here
19+
}
20+
21+
int *z = malloc(100 * sizeof(int));
22+
int *w = NULL;
23+
24+
if(z)
25+
{
26+
w = &(z[10]);
27+
*w = 0;
28+
29+
int *q = &(z[20]);
30+
*q = 1;
31+
// q goes DEAD here
32+
}
33+
34+
free(z);
35+
36+
*x = 1;
37+
x = 0;
38+
}
39+
40+
int main()
41+
{
42+
int z;
43+
foo(&z);
44+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
main.c
3+
--enforce-contract foo _ --malloc-may-fail --malloc-fail-null --pointer-primitive-check
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
--
9+
Checks whether CBMC properly tracks invalidated CARs
10+
on free and DEAD instructions.

src/goto-instrument/contracts/contracts.cpp

Lines changed: 86 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -669,21 +669,75 @@ void code_contractst::instrument_call_statement(
669669
auto snapshot_instructions = car->generate_snapshot_instructions();
670670
insert_before_swap_and_advance(
671671
body, instruction_it, snapshot_instructions);
672-
// since CAR was inserted *after* the malloc,
672+
// since CAR was inserted *after* the malloc call,
673673
// move the instruction pointer backward,
674674
// because the caller increments it in a `for` loop
675675
instruction_it--;
676676
}
677-
return;
678677
}
679678
else if(callee_name == "free")
680679
{
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--;
687741
}
688742
}
689743

@@ -823,9 +877,9 @@ void code_contractst::check_frame_conditions(
823877
auto snapshot_instructions = car->generate_snapshot_instructions();
824878
insert_before_swap_and_advance(
825879
body, instruction_it, snapshot_instructions);
826-
// since CAR was inserted *after* the DECL,
880+
// since CAR was inserted *after* the DECL instruction,
827881
// 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
829883
instruction_it--;
830884
}
831885
else if(instruction_it->is_assign())
@@ -838,7 +892,28 @@ void code_contractst::check_frame_conditions(
838892
}
839893
else if(instruction_it->is_dead())
840894
{
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+
}
842917
}
843918
else if(
844919
instruction_it->is_other() &&

0 commit comments

Comments
 (0)