Skip to content

Commit 1059ee7

Browse files
committed
uninitialized check
Uninitialized local or dynamically allocated variables have an indeterminate initial value. Reading an indeterminate value _may_ be undefined behavior, or may yield an unspecific value. This adds a check for uninitialized local variables. The check is not added as a standard check.
1 parent b712143 commit 1059ee7

File tree

2 files changed

+86
-7
lines changed

2 files changed

+86
-7
lines changed

Diff for: src/ansi-c/goto-conversion/goto_check_c.cpp

+79-7
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,15 @@ class goto_check_ct
4646
{
4747
public:
4848
goto_check_ct(
49-
const namespacet &_ns,
49+
symbol_table_baset &_symbol_table,
5050
const optionst &_options,
5151
message_handlert &_message_handler)
52-
: ns(_ns), local_bitvector_analysis(nullptr), log(_message_handler)
52+
: ns(_symbol_table), symbol_table(_symbol_table), local_bitvector_analysis(nullptr), log(_message_handler)
5353
{
5454
enable_bounds_check = _options.get_bool_option("bounds-check");
5555
enable_pointer_check = _options.get_bool_option("pointer-check");
56+
enable_uninitialized_check =
57+
_options.get_bool_option("uninitialized-check");
5658
enable_memory_leak_check = _options.get_bool_option("memory-leak-check");
5759
enable_memory_cleanup_check =
5860
_options.get_bool_option("memory-cleanup-check");
@@ -94,7 +96,8 @@ class goto_check_ct
9496
void collect_allocations(const goto_functionst &goto_functions);
9597

9698
protected:
97-
const namespacet &ns;
99+
const namespacet ns;
100+
symbol_table_baset &symbol_table;
98101
std::unique_ptr<local_bitvector_analysist> local_bitvector_analysis;
99102
goto_programt::const_targett current_target;
100103
messaget log;
@@ -189,6 +192,8 @@ class goto_check_ct
189192
void undefined_shift_check(const shift_exprt &, const guardt &);
190193
void pointer_rel_check(const binary_exprt &, const guardt &);
191194
void pointer_overflow_check(const exprt &, const guardt &);
195+
void
196+
uninitialized_check(const symbol_exprt &, const guardt &, bool is_assigned);
192197
void memory_leak_check(const irep_idt &function_id);
193198

194199
/// Generates VCCs for the validity of the given dereferencing operation.
@@ -265,6 +270,7 @@ class goto_check_ct
265270

266271
bool enable_bounds_check;
267272
bool enable_pointer_check;
273+
bool enable_uninitialized_check;
268274
bool enable_memory_leak_check;
269275
bool enable_memory_cleanup_check;
270276
bool enable_div_by_zero_check;
@@ -286,6 +292,7 @@ class goto_check_ct
286292
std::map<irep_idt, bool *> name_to_flag{
287293
{"bounds-check", &enable_bounds_check},
288294
{"pointer-check", &enable_pointer_check},
295+
{"uninitialized-check", &enable_uninitialized_check},
289296
{"memory-leak-check", &enable_memory_leak_check},
290297
{"memory-cleanup-check", &enable_memory_cleanup_check},
291298
{"div-by-zero-check", &enable_div_by_zero_check},
@@ -1341,6 +1348,66 @@ void goto_check_ct::nan_check(const exprt &expr, const guardt &guard)
13411348
guard);
13421349
}
13431350

1351+
void goto_check_ct::uninitialized_check(
1352+
const symbol_exprt &expr,
1353+
const guardt &guard,
1354+
bool is_assigned)
1355+
{
1356+
if(!enable_uninitialized_check)
1357+
return;
1358+
1359+
// Ignore C function symbols.
1360+
if(expr.type().id() == ID_code)
1361+
return;
1362+
1363+
// Don't check the LHS of an assignment -- these do not
1364+
// have to be initialized
1365+
if(is_assigned)
1366+
return;
1367+
1368+
// Look up the symbol
1369+
auto &symbol = ns.lookup(expr);
1370+
1371+
// Anything with static lifetime is initialized by the runtime,
1372+
// and is hence never uninitialized.
1373+
if(symbol.is_static_lifetime)
1374+
return;
1375+
1376+
// Is the address taken? If so, uninitialized accesses are merely
1377+
// intederminate, but not undefined.
1378+
if(local_bitvector_analysis->dirty(expr))
1379+
return;
1380+
1381+
// Ignore structs/union/arrays for now
1382+
if(
1383+
symbol.type.id() == ID_struct || symbol.type.id() == ID_union ||
1384+
symbol.type.id() == ID_array)
1385+
{
1386+
return;
1387+
}
1388+
1389+
// Look up the bits that track initialization
1390+
auto init_bit_id = id2string(symbol.name) + "$init";
1391+
1392+
if(!symbol_table.has_symbol(init_bit_id))
1393+
{
1394+
auxiliary_symbolt new_init_symbol{init_bit_id, bool_typet{}, symbol.mode};
1395+
new_init_symbol.is_static_lifetime = false;
1396+
symbol_table.add(new_init_symbol);
1397+
}
1398+
1399+
const symbolt &init_symbol = ns.lookup(init_bit_id);
1400+
1401+
add_guarded_property(
1402+
init_symbol.symbol_expr(),
1403+
"reading uninitialized local",
1404+
"uninitialized",
1405+
true, // not fatal
1406+
expr.find_source_location(),
1407+
expr,
1408+
guard);
1409+
}
1410+
13441411
void goto_check_ct::pointer_rel_check(
13451412
const binary_exprt &expr,
13461413
const guardt &guard)
@@ -2061,6 +2128,10 @@ void goto_check_ct::check_rec(
20612128
{
20622129
pointer_validity_check(to_dereference_expr(expr), expr, guard);
20632130
}
2131+
else if(expr.id() == ID_symbol)
2132+
{
2133+
uninitialized_check(to_symbol_expr(expr), guard, is_assigned);
2134+
}
20642135
else if(requires_pointer_primitive_check(expr))
20652136
{
20662137
pointer_primitive_check(expr, guard);
@@ -2469,6 +2540,7 @@ exprt goto_check_ct::is_in_bounds_of_some_explicit_allocation(
24692540
return disjunction(alloc_disjuncts);
24702541
}
24712542

2543+
#if 0
24722544
void goto_check_c(
24732545
const irep_idt &function_identifier,
24742546
goto_functionst::goto_functiont &goto_function,
@@ -2479,14 +2551,15 @@ void goto_check_c(
24792551
goto_check_ct goto_check(ns, options, message_handler);
24802552
goto_check.goto_check(function_identifier, goto_function);
24812553
}
2554+
#endif
24822555

24832556
void goto_check_c(
2484-
const namespacet &ns,
24852557
const optionst &options,
2558+
symbol_table_baset &symbol_table,
24862559
goto_functionst &goto_functions,
24872560
message_handlert &message_handler)
24882561
{
2489-
goto_check_ct goto_check(ns, options, message_handler);
2562+
goto_check_ct goto_check(symbol_table, options, message_handler);
24902563

24912564
goto_check.collect_allocations(goto_functions);
24922565

@@ -2501,8 +2574,7 @@ void goto_check_c(
25012574
goto_modelt &goto_model,
25022575
message_handlert &message_handler)
25032576
{
2504-
const namespacet ns(goto_model.symbol_table);
2505-
goto_check_c(ns, options, goto_model.goto_functions, message_handler);
2577+
goto_check_c(options, goto_model.symbol_table, goto_model.goto_functions, message_handler);
25062578
}
25072579

25082580
void goto_check_ct::add_active_named_check_pragmas(

Diff for: src/ansi-c/goto-conversion/goto_check_c.h

+7
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ class namespacet;
1919
class optionst;
2020
class message_handlert;
2121

22+
#if 0
2223
void goto_check_c(
2324
const namespacet &ns,
2425
const optionst &options,
@@ -31,6 +32,7 @@ void goto_check_c(
3132
const namespacet &ns,
3233
const optionst &options,
3334
message_handlert &message_handler);
35+
#endif
3436

3537
void goto_check_c(
3638
const optionst &options,
@@ -39,6 +41,7 @@ void goto_check_c(
3941

4042
#define OPT_GOTO_CHECK \
4143
"(bounds-check)(pointer-check)(memory-leak-check)(memory-cleanup-check)" \
44+
"(uninitialized-check)" \
4245
"(div-by-zero-check)(float-div-by-zero-check)" \
4346
"(enum-range-check)" \
4447
"(signed-overflow-check)(unsigned-overflow-check)" \
@@ -51,6 +54,7 @@ void goto_check_c(
5154
"(assert-to-assume)" \
5255
"(no-bounds-check)(no-pointer-check)(no-signed-overflow-check)" \
5356
"(no-pointer-primitive-check)(no-undefined-shift-check)" \
57+
"(no-uninitialized-check)" \
5458
"(no-div-by-zero-check)"
5559

5660
// clang-format off
@@ -59,6 +63,8 @@ void goto_check_c(
5963
" {y--bounds-check} \t enable array bounds checks (default on)\n" \
6064
" {y--no-bounds-check} \t disable array bounds checks\n" \
6165
" {y--pointer-check} \t enable pointer checks (default on)\n" \
66+
" {y--uninitialized-check} \t enable checks for uninitialized data (default off)\n" \
67+
" {y--no-uninitialized-check} \t disable checks for uninitialized data\n" \
6268
" {y--no-pointer-check} \t disable pointer checks\n" \
6369
" {y--memory-leak-check} \t enable memory leak checks\n" \
6470
" {y--memory-cleanup-check} \t enable memory cleanup checks\n" \
@@ -126,6 +132,7 @@ void goto_check_c(
126132
options.set_option("error-label", cmdline.get_values("error-label")); \
127133
PARSE_OPTION_OVERRIDE(cmdline, options, "bounds-check"); \
128134
PARSE_OPTION_OVERRIDE(cmdline, options, "pointer-check"); \
135+
PARSE_OPTION_OVERRIDE(cmdline, options, "uninitialized-check"); \
129136
PARSE_OPTION_OVERRIDE(cmdline, options, "div-by-zero-check"); \
130137
PARSE_OPTION_OVERRIDE(cmdline, options, "float-div-by-zero-check"); \
131138
PARSE_OPTION_OVERRIDE(cmdline, options, "signed-overflow-check"); \

0 commit comments

Comments
 (0)