Skip to content

Commit b1d99cb

Browse files
authored
Merge pull request #3462 from peterschrammel/memory-leak-abort
Check for memory leaks on exit and abort
2 parents f1aaf5b + b4b815f commit b1d99cb

File tree

9 files changed

+93
-19
lines changed

9 files changed

+93
-19
lines changed

doc/man/cbmc.1

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,10 @@ enable pointer checks
178178
\fB\-\-memory\-leak\-check\fR
179179
enable memory leak checks
180180
.TP
181+
\fB\-\-memory\-cleanup\-check\fR
182+
Enable memory cleanup checks: assert that all dynamically allocated memory is
183+
explicitly freed before terminating the program.
184+
.TP
181185
\fB\-\-div\-by\-zero\-check\fR
182186
enable division by zero checks
183187
.TP

doc/man/goto-analyzer.1

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,10 @@ enable pointer checks
506506
\fB\-\-memory\-leak\-check\fR
507507
enable memory leak checks
508508
.TP
509+
\fB\-\-memory\-cleanup\-check\fR
510+
Enable memory cleanup checks: assert that all dynamically allocated memory is
511+
explicitly freed before terminating the program.
512+
.TP
509513
\fB\-\-div\-by\-zero\-check\fR
510514
enable division by zero checks
511515
.TP

doc/man/goto-diff.1

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ enable pointer checks
4747
\fB\-\-memory\-leak\-check\fR
4848
enable memory leak checks
4949
.TP
50+
\fB\-\-memory\-cleanup\-check\fR
51+
Enable memory cleanup checks: assert that all dynamically allocated memory is
52+
explicitly freed before terminating the program.
53+
.TP
5054
\fB\-\-div\-by\-zero\-check\fR
5155
enable division by zero checks
5256
.TP

doc/man/goto-instrument.1

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,10 @@ enable pointer checks
187187
\fB\-\-memory\-leak\-check\fR
188188
enable memory leak checks
189189
.TP
190+
\fB\-\-memory\-cleanup\-check\fR
191+
Enable memory cleanup checks: assert that all dynamically allocated memory is
192+
explicitly freed before terminating the program.
193+
.TP
190194
\fB\-\-div\-by\-zero\-check\fR
191195
enable division by zero checks
192196
.TP
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#include <stdlib.h>
2+
3+
extern void __VERIFIER_error();
4+
5+
int main()
6+
{
7+
int *p = malloc(sizeof(int));
8+
if(*p == 0)
9+
abort();
10+
if(*p == 1)
11+
exit(1);
12+
if(*p == 2)
13+
_Exit(1);
14+
if(*p == 3)
15+
__VERIFIER_error();
16+
free(p);
17+
return 0;
18+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
CORE
2+
main.c
3+
--memory-leak-check --memory-cleanup-check --no-assertions
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^VERIFICATION FAILED$
7+
_Exit\.memory-leak\.1.*FAILURE
8+
__CPROVER__start\.memory-leak\.1.*SUCCESS
9+
abort\.memory-leak\.1.*FAILURE
10+
exit\.memory-leak\.1.*FAILURE
11+
main\.memory-leak\.1.*FAILURE
12+
--
13+
main\.assertion\.1.*FAILURE
14+
^warning: ignoring

src/ansi-c/goto_check_c.cpp

Lines changed: 41 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ class goto_check_ct
5555
enable_bounds_check = _options.get_bool_option("bounds-check");
5656
enable_pointer_check = _options.get_bool_option("pointer-check");
5757
enable_memory_leak_check = _options.get_bool_option("memory-leak-check");
58+
enable_memory_cleanup_check =
59+
_options.get_bool_option("memory-cleanup-check");
5860
enable_div_by_zero_check = _options.get_bool_option("div-by-zero-check");
5961
enable_enum_range_check = _options.get_bool_option("enum-range-check");
6062
enable_signed_overflow_check =
@@ -181,6 +183,7 @@ class goto_check_ct
181183
void undefined_shift_check(const shift_exprt &, const guardt &);
182184
void pointer_rel_check(const binary_exprt &, const guardt &);
183185
void pointer_overflow_check(const exprt &, const guardt &);
186+
void memory_leak_check(const irep_idt &function_id);
184187

185188
/// Generates VCCs for the validity of the given dereferencing operation.
186189
/// \param expr the expression to be checked
@@ -256,6 +259,7 @@ class goto_check_ct
256259
bool enable_bounds_check;
257260
bool enable_pointer_check;
258261
bool enable_memory_leak_check;
262+
bool enable_memory_cleanup_check;
259263
bool enable_div_by_zero_check;
260264
bool enable_enum_range_check;
261265
bool enable_signed_overflow_check;
@@ -275,6 +279,7 @@ class goto_check_ct
275279
{"bounds-check", &enable_bounds_check},
276280
{"pointer-check", &enable_pointer_check},
277281
{"memory-leak-check", &enable_memory_leak_check},
282+
{"memory-cleanup-check", &enable_memory_cleanup_check},
278283
{"div-by-zero-check", &enable_div_by_zero_check},
279284
{"enum-range-check", &enable_enum_range_check},
280285
{"signed-overflow-check", &enable_signed_overflow_check},
@@ -2045,6 +2050,28 @@ optionalt<exprt> goto_check_ct::expand_pointer_checks(exprt expr)
20452050
return {};
20462051
}
20472052

2053+
void goto_check_ct::memory_leak_check(const irep_idt &function_id)
2054+
{
2055+
const symbolt &leak = ns.lookup(CPROVER_PREFIX "memory_leak");
2056+
const symbol_exprt leak_expr = leak.symbol_expr();
2057+
2058+
// add self-assignment to get helpful counterexample output
2059+
new_code.add(goto_programt::make_assignment(leak_expr, leak_expr));
2060+
2061+
source_locationt source_location;
2062+
source_location.set_function(function_id);
2063+
2064+
equal_exprt eq(leak_expr, null_pointer_exprt(to_pointer_type(leak.type)));
2065+
2066+
add_guarded_property(
2067+
eq,
2068+
"dynamically allocated memory never freed",
2069+
"memory-leak",
2070+
source_location,
2071+
eq,
2072+
identity);
2073+
}
2074+
20482075
void goto_check_ct::goto_check(
20492076
const irep_idt &function_identifier,
20502077
goto_functiont &goto_function)
@@ -2196,6 +2223,19 @@ void goto_check_ct::goto_check(
21962223
// this has no successor
21972224
assertions.clear();
21982225
}
2226+
else if(i.is_assume())
2227+
{
2228+
// These are further 'exit points' of the program
2229+
const exprt simplified_guard = simplify_expr(i.condition(), ns);
2230+
if(
2231+
enable_memory_cleanup_check && simplified_guard.is_false() &&
2232+
(function_identifier == "abort" || function_identifier == "exit" ||
2233+
function_identifier == "_Exit" ||
2234+
(i.labels.size() == 1 && i.labels.front() == "__VERIFIER_abort")))
2235+
{
2236+
memory_leak_check(function_identifier);
2237+
}
2238+
}
21992239
else if(i.is_dead())
22002240
{
22012241
if(enable_pointer_check || enable_pointer_primitive_check)
@@ -2225,24 +2265,7 @@ void goto_check_ct::goto_check(
22252265
function_identifier == goto_functionst::entry_point() &&
22262266
enable_memory_leak_check)
22272267
{
2228-
const symbolt &leak = ns.lookup(CPROVER_PREFIX "memory_leak");
2229-
const symbol_exprt leak_expr = leak.symbol_expr();
2230-
2231-
// add self-assignment to get helpful counterexample output
2232-
new_code.add(goto_programt::make_assignment(leak_expr, leak_expr));
2233-
2234-
source_locationt source_location;
2235-
source_location.set_function(function_identifier);
2236-
2237-
equal_exprt eq(
2238-
leak_expr, null_pointer_exprt(to_pointer_type(leak.type)));
2239-
add_guarded_property(
2240-
eq,
2241-
"dynamically allocated memory never freed",
2242-
"memory-leak",
2243-
source_location,
2244-
eq,
2245-
identity);
2268+
memory_leak_check(function_identifier);
22462269
}
22472270
}
22482271

src/ansi-c/goto_check_c.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ void goto_check_c(
3838
message_handlert &message_handler);
3939

4040
#define OPT_GOTO_CHECK \
41-
"(bounds-check)(pointer-check)(memory-leak-check)" \
41+
"(bounds-check)(pointer-check)(memory-leak-check)(memory-cleanup-check)" \
4242
"(div-by-zero-check)(enum-range-check)" \
4343
"(signed-overflow-check)(unsigned-overflow-check)" \
4444
"(pointer-overflow-check)(conversion-check)(undefined-shift-check)" \
@@ -54,6 +54,7 @@ void goto_check_c(
5454
" --bounds-check enable array bounds checks\n" \
5555
" --pointer-check enable pointer checks\n" /* NOLINT(whitespace/line_length) */ \
5656
" --memory-leak-check enable memory leak checks\n" \
57+
" --memory-cleanup-check enable memory cleanup checks\n" \
5758
" --div-by-zero-check enable division by zero checks\n" \
5859
" --signed-overflow-check enable signed arithmetic over- and underflow checks\n" /* NOLINT(whitespace/line_length) */ \
5960
" --unsigned-overflow-check enable arithmetic over- and underflow checks\n" /* NOLINT(whitespace/line_length) */ \
@@ -75,6 +76,7 @@ void goto_check_c(
7576
options.set_option("bounds-check", cmdline.isset("bounds-check")); \
7677
options.set_option("pointer-check", cmdline.isset("pointer-check")); \
7778
options.set_option("memory-leak-check", cmdline.isset("memory-leak-check")); \
79+
options.set_option("memory-cleanup-check", cmdline.isset("memory-cleanup-check")); /* NOLINT(whitespace/line_length) */ \
7880
options.set_option("div-by-zero-check", cmdline.isset("div-by-zero-check")); \
7981
options.set_option("enum-range-check", cmdline.isset("enum-range-check")); \
8082
options.set_option("signed-overflow-check", cmdline.isset("signed-overflow-check")); /* NOLINT(whitespace/line_length) */ \

src/goto-programs/builtin_functions.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -831,6 +831,7 @@ void goto_convertt::do_function_call_symbol(
831831
annotated_location = function.source_location();
832832
annotated_location.set("user-provided", true);
833833
dest.add(goto_programt::make_assumption(false_exprt(), annotated_location));
834+
dest.instructions.back().labels.push_back("__VERIFIER_abort");
834835
}
835836
else if(
836837
identifier == "assert" &&

0 commit comments

Comments
 (0)