Skip to content

Commit 1e08a57

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 c4aaafd commit 1e08a57

File tree

2 files changed

+58
-24
lines changed

2 files changed

+58
-24
lines changed

src/ansi-c/goto-conversion/goto_check_c.cpp

Lines changed: 53 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ class goto_check_ct
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");
@@ -157,11 +159,13 @@ class goto_check_ct
157159
/// guard.
158160
/// \param expr: the expression to be checked
159161
/// \param guard: the condition for when the check should be made
160-
void check_rec(const exprt &expr, const guardt &guard);
162+
/// \param is_lhs: the expression is the LHS of an assignment
163+
void check_rec(const exprt &, const guardt &, bool is_lhs);
161164

162165
/// Initiate the recursively analysis of \p expr with its `guard' set to TRUE.
163166
/// \param expr: the expression to be checked
164-
void check(const exprt &expr);
167+
/// \param is_lhs: the expression is the LHS of an assignment
168+
void check(const exprt &expr, bool is_lhs);
165169

166170
struct conditiont
167171
{
@@ -187,6 +191,7 @@ class goto_check_ct
187191
void undefined_shift_check(const shift_exprt &, const guardt &);
188192
void pointer_rel_check(const binary_exprt &, const guardt &);
189193
void pointer_overflow_check(const exprt &, const guardt &);
194+
void uninitialized_check(const symbol_exprt &, const guardt &);
190195
void memory_leak_check(const irep_idt &function_id);
191196

192197
/// Generates VCCs for the validity of the given dereferencing operation.
@@ -263,6 +268,7 @@ class goto_check_ct
263268

264269
bool enable_bounds_check;
265270
bool enable_pointer_check;
271+
bool enable_uninitialized_check;
266272
bool enable_memory_leak_check;
267273
bool enable_memory_cleanup_check;
268274
bool enable_div_by_zero_check;
@@ -284,6 +290,7 @@ class goto_check_ct
284290
std::map<irep_idt, bool *> name_to_flag{
285291
{"bounds-check", &enable_bounds_check},
286292
{"pointer-check", &enable_pointer_check},
293+
{"uninitialized-check", &enable_uninitialized_check},
287294
{"memory-leak-check", &enable_memory_leak_check},
288295
{"memory-cleanup-check", &enable_memory_cleanup_check},
289296
{"div-by-zero-check", &enable_div_by_zero_check},
@@ -1331,6 +1338,23 @@ void goto_check_ct::nan_check(const exprt &expr, const guardt &guard)
13311338
guard);
13321339
}
13331340

1341+
void goto_check_ct::uninitialized_check(
1342+
const symbol_exprt &expr,
1343+
const guardt &guard)
1344+
{
1345+
if(!enable_uninitialized_check)
1346+
return;
1347+
1348+
add_guarded_property(
1349+
false_exprt{},
1350+
"reading uninitialized local",
1351+
"uninitialized",
1352+
true, // not fatal
1353+
expr.find_source_location(),
1354+
expr,
1355+
guard);
1356+
}
1357+
13341358
void goto_check_ct::pointer_rel_check(
13351359
const binary_exprt &expr,
13361360
const guardt &guard)
@@ -1807,13 +1831,13 @@ void goto_check_ct::check_rec_address(const exprt &expr, const guardt &guard)
18071831

18081832
if(expr.id() == ID_dereference)
18091833
{
1810-
check_rec(to_dereference_expr(expr).pointer(), guard);
1834+
check_rec(to_dereference_expr(expr).pointer(), guard, false);
18111835
}
18121836
else if(expr.id() == ID_index)
18131837
{
18141838
const index_exprt &index_expr = to_index_expr(expr);
18151839
check_rec_address(index_expr.array(), guard);
1816-
check_rec(index_expr.index(), guard);
1840+
check_rec(index_expr.index(), guard, false);
18171841
}
18181842
else
18191843
{
@@ -1843,7 +1867,7 @@ void goto_check_ct::check_rec_logical_op(const exprt &expr, const guardt &guard)
18431867
return guard(implication(conjunction(constraints), expr));
18441868
};
18451869

1846-
check_rec(op, new_guard);
1870+
check_rec(op, new_guard, false);
18471871

18481872
constraints.push_back(expr.id() == ID_or ? boolean_negate(op) : op);
18491873
}
@@ -1855,20 +1879,20 @@ void goto_check_ct::check_rec_if(const if_exprt &if_expr, const guardt &guard)
18551879
if_expr.cond().is_boolean(),
18561880
"first argument of if must be boolean, but got " + if_expr.cond().pretty());
18571881

1858-
check_rec(if_expr.cond(), guard);
1882+
check_rec(if_expr.cond(), guard, false);
18591883

18601884
{
18611885
auto new_guard = [&guard, &if_expr](exprt expr) {
18621886
return guard(implication(if_expr.cond(), std::move(expr)));
18631887
};
1864-
check_rec(if_expr.true_case(), new_guard);
1888+
check_rec(if_expr.true_case(), new_guard, false);
18651889
}
18661890

18671891
{
18681892
auto new_guard = [&guard, &if_expr](exprt expr) {
18691893
return guard(implication(not_exprt(if_expr.cond()), std::move(expr)));
18701894
};
1871-
check_rec(if_expr.false_case(), new_guard);
1895+
check_rec(if_expr.false_case(), new_guard, false);
18721896
}
18731897
}
18741898

@@ -1878,7 +1902,7 @@ bool goto_check_ct::check_rec_member(
18781902
{
18791903
const dereference_exprt &deref = to_dereference_expr(member.struct_op());
18801904

1881-
check_rec(deref.pointer(), guard);
1905+
check_rec(deref.pointer(), guard, false);
18821906

18831907
// avoid building the following expressions when pointer_validity_check
18841908
// would return immediately anyway
@@ -1969,7 +1993,7 @@ void goto_check_ct::check_rec_arithmetic_op(
19691993
}
19701994
}
19711995

1972-
void goto_check_ct::check_rec(const exprt &expr, const guardt &guard)
1996+
void goto_check_ct::check_rec(const exprt &expr, const guardt &guard, bool is_lhs)
19731997
{
19741998
if(expr.id() == ID_exists || expr.id() == ID_forall)
19751999
{
@@ -1980,7 +2004,7 @@ void goto_check_ct::check_rec(const exprt &expr, const guardt &guard)
19802004
return guard(forall_exprt(quantifier_expr.symbol(), expr));
19812005
};
19822006

1983-
check_rec(quantifier_expr.where(), new_guard);
2007+
check_rec(quantifier_expr.where(), new_guard, false);
19842008
return;
19852009
}
19862010
else if(expr.id() == ID_address_of)
@@ -2007,7 +2031,7 @@ void goto_check_ct::check_rec(const exprt &expr, const guardt &guard)
20072031
}
20082032

20092033
for(const auto &op : expr.operands())
2010-
check_rec(op, guard);
2034+
check_rec(op, guard, false);
20112035

20122036
if(expr.type().id() == ID_c_enum_tag)
20132037
enum_range_check(expr, guard);
@@ -2048,6 +2072,10 @@ void goto_check_ct::check_rec(const exprt &expr, const guardt &guard)
20482072
{
20492073
pointer_validity_check(to_dereference_expr(expr), expr, guard);
20502074
}
2075+
else if(expr.id() == ID_symbol)
2076+
{
2077+
uninitialized_check(to_symbol_expr(expr), guard);
2078+
}
20512079
else if(requires_pointer_primitive_check(expr))
20522080
{
20532081
pointer_primitive_check(expr, guard);
@@ -2059,9 +2087,9 @@ void goto_check_ct::check_rec(const exprt &expr, const guardt &guard)
20592087
}
20602088
}
20612089

2062-
void goto_check_ct::check(const exprt &expr)
2090+
void goto_check_ct::check(const exprt &expr, bool is_lhs)
20632091
{
2064-
check_rec(expr, identity);
2092+
check_rec(expr, identity, is_lhs);
20652093
}
20662094

20672095
void goto_check_ct::memory_leak_check(const irep_idt &function_id)
@@ -2151,7 +2179,7 @@ void goto_check_ct::goto_check(
21512179

21522180
if(i.has_condition())
21532181
{
2154-
check(i.condition());
2182+
check(i.condition(), false);
21552183
}
21562184

21572185
// magic ERROR label?
@@ -2184,12 +2212,12 @@ void goto_check_ct::goto_check(
21842212

21852213
if(statement == ID_expression)
21862214
{
2187-
check(code);
2215+
check(code, false);
21882216
}
21892217
else if(statement == ID_printf)
21902218
{
21912219
for(const auto &op : code.operands())
2192-
check(op);
2220+
check(op, false);
21932221
}
21942222
}
21952223
else if(i.is_assign())
@@ -2202,10 +2230,10 @@ void goto_check_ct::goto_check(
22022230
{
22032231
flag_overridet resetter(i.source_location());
22042232
resetter.disable_flag(enable_enum_range_check, "enum_range_check");
2205-
check(assign_lhs);
2233+
check(assign_lhs, true);
22062234
}
22072235

2208-
check(assign_rhs);
2236+
check(assign_rhs, false);
22092237

22102238
// the LHS might invalidate any assertion
22112239
invalidate(assign_lhs);
@@ -2217,12 +2245,13 @@ void goto_check_ct::goto_check(
22172245
{
22182246
flag_overridet resetter(i.source_location());
22192247
resetter.disable_flag(enable_enum_range_check, "enum_range_check");
2220-
check(i.call_lhs());
2248+
check(i.call_lhs(), true);
22212249
}
2222-
check(i.call_function());
2250+
2251+
check(i.call_function(), false);
22232252

22242253
for(const auto &arg : i.call_arguments())
2225-
check(arg);
2254+
check(arg, false);
22262255

22272256
check_shadow_memory_api_calls(i);
22282257

@@ -2231,7 +2260,7 @@ void goto_check_ct::goto_check(
22312260
}
22322261
else if(i.is_set_return_value())
22332262
{
2234-
check(i.return_value());
2263+
check(i.return_value(), false);
22352264
// the return value invalidate any assertion
22362265
invalidate(i.return_value());
22372266
}
@@ -2342,7 +2371,7 @@ void goto_check_ct::check_shadow_memory_api_calls(
23422371
{
23432372
const exprt &expr = i.call_arguments()[0];
23442373
PRECONDITION(expr.type().id() == ID_pointer);
2345-
check(dereference_exprt(expr));
2374+
check(dereference_exprt(expr), false);
23462375
}
23472376
}
23482377

src/ansi-c/goto-conversion/goto_check_c.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ void goto_check_c(
3939

4040
#define OPT_GOTO_CHECK \
4141
"(bounds-check)(pointer-check)(memory-leak-check)(memory-cleanup-check)" \
42+
"(uninitialized-check)" \
4243
"(div-by-zero-check)(float-div-by-zero-check)" \
4344
"(enum-range-check)" \
4445
"(signed-overflow-check)(unsigned-overflow-check)" \
@@ -51,6 +52,7 @@ void goto_check_c(
5152
"(assert-to-assume)" \
5253
"(no-bounds-check)(no-pointer-check)(no-signed-overflow-check)" \
5354
"(no-pointer-primitive-check)(no-undefined-shift-check)" \
55+
"(no-uninitialized-check)" \
5456
"(no-div-by-zero-check)"
5557

5658
// clang-format off
@@ -59,6 +61,8 @@ void goto_check_c(
5961
" {y--bounds-check} \t enable array bounds checks (default on)\n" \
6062
" {y--no-bounds-check} \t disable array bounds checks\n" \
6163
" {y--pointer-check} \t enable pointer checks (default on)\n" \
64+
" {y--uninitialized-check} \t enable checks for uninitialized data (default off)\n" \
65+
" {y--no-uninitialized-check} \t disable checks for uninitialized data\n" \
6266
" {y--no-pointer-check} \t disable pointer checks\n" \
6367
" {y--memory-leak-check} \t enable memory leak checks\n" \
6468
" {y--memory-cleanup-check} \t enable memory cleanup checks\n" \
@@ -126,6 +130,7 @@ void goto_check_c(
126130
options.set_option("error-label", cmdline.get_values("error-label")); \
127131
PARSE_OPTION_OVERRIDE(cmdline, options, "bounds-check"); \
128132
PARSE_OPTION_OVERRIDE(cmdline, options, "pointer-check"); \
133+
PARSE_OPTION_OVERRIDE(cmdline, options, "uninitialized-check"); \
129134
PARSE_OPTION_OVERRIDE(cmdline, options, "div-by-zero-check"); \
130135
PARSE_OPTION_OVERRIDE(cmdline, options, "float-div-by-zero-check"); \
131136
PARSE_OPTION_OVERRIDE(cmdline, options, "signed-overflow-check"); \

0 commit comments

Comments
 (0)