Skip to content

Commit b80403d

Browse files
committed
check safety of free(...)
1 parent b3ec47e commit b80403d

File tree

11 files changed

+200
-9
lines changed

11 files changed

+200
-9
lines changed
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
KNOWNBUG
1+
CORE
22
double_free1.c
3-
4-
^EXIT=0$
3+
--safety
4+
^EXIT=10$
55
^SIGNAL=0$
6-
^program is safe / function main is safe / dereference line 6: SUCCESS$
7-
^program is safe / function main is safe / dereference line 8: REFUTED$
6+
^\[main\.free\.1\] line \d+ free argument must be valid dynamic object: SUCCESS$
7+
^\[main\.free\.2\] line \d+ free argument must be valid dynamic object: REFUTED$
88
--
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#include <stdlib.h>
2+
3+
int main()
4+
{
5+
free(0); // safe
6+
free((char *)0 + 1); // unsafe
7+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
free_null1.c
3+
--safety
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^\[main\.free\.1\] line \d+ free argument must be valid dynamic object: SUCCESS$
7+
^\[main\.free\.2\] line \d+ free argument must be valid dynamic object: REFUTED$
8+
--

src/cprover/axioms.cpp

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,6 @@ void axiomst::live_object_fc()
108108
{
109109
if(a_it->state() != b_it->state())
110110
continue;
111-
auto a_op = a_it->address();
112111
auto operands_equal = same_object(a_it->address(), b_it->address());
113112
auto implication =
114113
implies_exprt(operands_equal, equal_exprt(*a_it, *b_it));
@@ -118,6 +117,27 @@ void axiomst::live_object_fc()
118117
}
119118
}
120119

120+
void axiomst::is_dynamic_object_fc()
121+
{
122+
// quadratic
123+
for(auto a_it = is_dynamic_object_exprs.begin();
124+
a_it != is_dynamic_object_exprs.end();
125+
a_it++)
126+
{
127+
for(auto b_it = std::next(a_it); b_it != is_dynamic_object_exprs.end();
128+
b_it++)
129+
{
130+
if(a_it->state() != b_it->state())
131+
continue;
132+
auto operands_equal = same_object(a_it->address(), b_it->address());
133+
auto implication =
134+
implies_exprt(operands_equal, equal_exprt(*a_it, *b_it));
135+
std::cout << "IS_DYNAMIC_OBJECT: " << format(implication) << '\n';
136+
dest << replace(implication);
137+
}
138+
}
139+
}
140+
121141
void axiomst::object_size()
122142
{
123143
for(const auto &src : object_size_exprs)
@@ -182,6 +202,7 @@ exprt axiomst::replace(exprt src)
182202

183203
if(
184204
src.id() == ID_evaluate || src.id() == ID_state_is_cstring ||
205+
src.id() == ID_state_is_dynamic_object ||
185206
src.id() == ID_state_object_size || src.id() == ID_state_live_object ||
186207
src.id() == ID_state_r_ok || src.id() == ID_state_w_ok ||
187208
src.id() == ID_state_rw_ok || src.id() == ID_allocate)
@@ -275,6 +296,11 @@ void axiomst::node(const exprt &src)
275296
dest << instance;
276297
}
277298
}
299+
else if(src.id() == ID_state_is_dynamic_object)
300+
{
301+
const auto &is_dynamic_object_expr = to_state_is_dynamic_object_expr(src);
302+
is_dynamic_object_exprs.insert(is_dynamic_object_expr);
303+
}
278304
else if(src.id() == ID_allocate)
279305
{
280306
const auto &allocate_expr = to_allocate_expr(src);
@@ -299,10 +325,19 @@ void axiomst::node(const exprt &src)
299325

300326
// pointer_offset(allocate(ς, s)) = 0
301327
auto pointer_offset_expr = pointer_offset(allocate_expr);
328+
//pointer_offset_exprs.insert(pointer_offset_expr);
302329
auto instance3 =
303330
replace(equal_exprt(pointer_offset_expr, from_integer(0, pointer_offset_expr.type())));
304331
std::cout << "ALLOCATE3: " << format(instance3) << "\n";
305332
dest << instance3;
333+
334+
// is_dynamic_object(ς, allocate(ς, s))
335+
auto is_dynamic_object_expr =
336+
state_is_dynamic_object_exprt(allocate_expr.state(), allocate_expr);
337+
is_dynamic_object_exprs.insert(is_dynamic_object_expr);
338+
auto instance4 = replace(is_dynamic_object_expr);
339+
std::cout << "ALLOCATE4: " << format(instance4) << "\n";
340+
dest << instance4;
306341
}
307342
else if(src.id() == ID_deallocate_state)
308343
{
@@ -407,4 +442,5 @@ void axiomst::emit()
407442
ok_fc();
408443
live_object_fc();
409444
object_size_fc();
445+
is_dynamic_object_fc();
410446
}

src/cprover/axioms.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ class axiomst
6464
std::set<state_is_cstring_exprt> is_cstring_exprs;
6565
void is_cstring_fc();
6666

67+
std::set<state_is_dynamic_object_exprt> is_dynamic_object_exprs;
68+
void is_dynamic_object_fc();
69+
6770
std::set<state_live_object_exprt> live_object_exprs;
6871
void live_object_fc();
6972

src/cprover/c_safety_checks.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,37 @@ void c_safety_checks(
206206
it->apply([&f, &ns, &dest](const exprt &expr) {
207207
c_safety_checks(f, expr, ns, dest);
208208
});
209+
210+
if(it->is_function_call())
211+
{
212+
const auto &function = it->call_function();
213+
if(function.id() == ID_symbol)
214+
{
215+
const auto &identifier = to_symbol_expr(function).get_identifier();
216+
if(identifier == "free")
217+
{
218+
if(
219+
it->call_arguments().size() == 1 &&
220+
it->call_arguments()[0].type().id() == ID_pointer)
221+
{
222+
// Must be dynamically allocated and still alive,
223+
// or, alternatively, NULL.
224+
const auto &pointer = it->call_arguments()[0];
225+
auto condition = or_exprt(
226+
equal_exprt(
227+
pointer, null_pointer_exprt(to_pointer_type(pointer.type()))),
228+
and_exprt(
229+
live_object_exprt(pointer), is_dynamic_object_exprt(pointer)));
230+
auto source_location = it->source_location();
231+
source_location.set_property_class("free");
232+
source_location.set_comment(
233+
"free argument must be valid dynamic object");
234+
dest.add(goto_programt::make_assertion(condition, source_location));
235+
}
236+
}
237+
}
238+
}
239+
209240
std::size_t n = dest.instructions.size();
210241
if(n != 0)
211242
{
@@ -222,6 +253,9 @@ void c_safety_checks(goto_modelt &goto_model)
222253

223254
for(auto &f : goto_model.goto_functions.function_map)
224255
c_safety_checks(f, ns);
256+
257+
// update the numbering
258+
goto_model.goto_functions.compute_location_numbers();
225259
}
226260

227261
void no_assertions(goto_functionst::function_mapt::value_type &f)

src/cprover/format_hooks.cpp

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,19 @@ void format_hooks()
7575
add_format_hook(
7676
ID_state_is_cstring,
7777
[](std::ostream &os, const exprt &expr) -> std::ostream & {
78-
const auto &is_cstring_expr = to_binary_expr(expr);
79-
return os << "is_cstring(" << format(is_cstring_expr.op0()) << ", "
80-
<< format(is_cstring_expr.op1()) << ')';
78+
const auto &is_cstring_expr = to_state_is_cstring_expr(expr);
79+
return os << "is_cstring(" << format(is_cstring_expr.state()) << ", "
80+
<< format(is_cstring_expr.address()) << ')';
81+
});
82+
83+
add_format_hook(
84+
ID_state_is_dynamic_object,
85+
[](std::ostream &os, const exprt &expr) -> std::ostream & {
86+
const auto &is_dynamic_object_expr =
87+
to_state_is_dynamic_object_expr(expr);
88+
return os << "is_dynamic_object("
89+
<< format(is_dynamic_object_expr.state()) << ", "
90+
<< format(is_dynamic_object_expr.address()) << ')';
8191
});
8292

8393
add_format_hook(

src/cprover/simplify_state_expr.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,29 @@ exprt simplify_live_object_expr(binary_exprt src, const namespacet &ns)
211211
return std::move(src);
212212
}
213213

214+
exprt simplify_is_dynamic_object_expr(state_is_dynamic_object_exprt src)
215+
{
216+
const auto &pointer = src.address();
217+
218+
auto object = simplify_object_expression(pointer);
219+
220+
if(object.id() == ID_object_address)
221+
{
222+
// these are not dynamic
223+
return false_exprt();
224+
}
225+
226+
// A store does not affect the result.
227+
// is_dynamic_object(ς[A:=V]), p) --> is_dynamic_object(ς, p)
228+
if(src.state().id() == ID_update_state)
229+
{
230+
src.state() = to_update_state_expr(src.state()).state();
231+
return std::move(src);
232+
}
233+
234+
return std::move(src);
235+
}
236+
214237
exprt simplify_object_size_expr(
215238
state_object_size_exprt src,
216239
const namespacet &ns)
@@ -414,6 +437,11 @@ exprt simplify_state_expr_node(
414437
return simplify_is_cstring_expr(
415438
to_state_is_cstring_expr(src), address_taken, ns);
416439
}
440+
else if(src.id() == ID_state_is_dynamic_object)
441+
{
442+
return simplify_is_dynamic_object_expr(
443+
to_state_is_dynamic_object_expr(src));
444+
}
417445
else if(src.id() == ID_plus)
418446
{
419447
auto &plus_expr = to_plus_expr(src);

src/cprover/state.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Author: Daniel Kroening, [email protected]
1111
#include <util/pointer_expr.h>
1212

1313
const irep_idt ID_state_is_cstring = "state_is_cstring";
14+
const irep_idt ID_state_is_dynamic_object = "state_is_dynamic_object";
1415
const irep_idt ID_state_live_object = "state_live_object";
1516
const irep_idt ID_state_object_size = "state_object_size";
1617
const irep_idt ID_state_r_ok = "state_r_ok";

src/cprover/state.h

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Author: Daniel Kroening, [email protected]
1313
#include <util/pointer_expr.h>
1414

1515
extern const irep_idt ID_state_is_cstring;
16+
extern const irep_idt ID_state_is_dynamic_object;
1617
extern const irep_idt ID_state_live_object;
1718
extern const irep_idt ID_state_object_size;
1819
extern const irep_idt ID_state_r_ok;
@@ -387,6 +388,62 @@ inline state_is_cstring_exprt &to_state_is_cstring_expr(exprt &expr)
387388
return ret;
388389
}
389390

391+
class state_is_dynamic_object_exprt : public binary_predicate_exprt
392+
{
393+
public:
394+
state_is_dynamic_object_exprt(exprt state, exprt address)
395+
: binary_predicate_exprt(
396+
std::move(state),
397+
ID_state_is_dynamic_object,
398+
std::move(address))
399+
{
400+
PRECONDITION(this->state().type().id() == ID_state);
401+
PRECONDITION(this->address().type().id() == ID_pointer);
402+
}
403+
404+
const exprt &state() const
405+
{
406+
return op0();
407+
}
408+
409+
exprt &state()
410+
{
411+
return op0();
412+
}
413+
414+
const exprt &address() const
415+
{
416+
return op1();
417+
}
418+
};
419+
420+
/// \brief Cast an exprt to a \ref state_is_dynamic_object_exprt
421+
///
422+
/// \a expr must be known to be \ref state_is_dynamic_object_exprt.
423+
///
424+
/// \param expr: Source expression
425+
/// \return Object of type \ref state_is_dynamic_object_exprt
426+
inline const state_is_dynamic_object_exprt &
427+
to_state_is_dynamic_object_expr(const exprt &expr)
428+
{
429+
PRECONDITION(expr.id() == ID_state_is_dynamic_object);
430+
const state_is_dynamic_object_exprt &ret =
431+
static_cast<const state_is_dynamic_object_exprt &>(expr);
432+
validate_expr(ret);
433+
return ret;
434+
}
435+
436+
/// \copydoc to_state_is_dynamic_object_expr(const exprt &)
437+
inline state_is_dynamic_object_exprt &
438+
to_state_is_dynamic_object_expr(exprt &expr)
439+
{
440+
PRECONDITION(expr.id() == ID_state_is_dynamic_object);
441+
state_is_dynamic_object_exprt &ret =
442+
static_cast<state_is_dynamic_object_exprt &>(expr);
443+
validate_expr(ret);
444+
return ret;
445+
}
446+
390447
class state_object_size_exprt : public binary_exprt
391448
{
392449
public:

src/cprover/state_encoding.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,13 @@ exprt state_encodingt::evaluate_expr_rec(
235235
evaluate_expr_rec(loc, state, live_object_expr.pointer(), bound_symbols);
236236
return state_live_object_exprt(state, pointer);
237237
}
238+
else if(what.id() == ID_is_dynamic_object)
239+
{
240+
const auto &is_dynamic_object_expr = to_is_dynamic_object_expr(what);
241+
auto pointer = evaluate_expr_rec(
242+
loc, state, is_dynamic_object_expr.address(), bound_symbols);
243+
return state_is_dynamic_object_exprt(state, pointer);
244+
}
238245
else if(what.id() == ID_object_size)
239246
{
240247
const auto &object_size_expr = to_object_size_expr(what);

0 commit comments

Comments
 (0)