Skip to content

Commit 85b90f3

Browse files
committed
Add an enum-in-range check.
This check can be enabled by using the flag --enum-range-check in goto-instrument. It inserts assertions before each use of any enum type expressions that the expression's value is one of the valid enum values. Note: It doesn't insert checks for occurrences of enum expressions on the left hand side of assignments.
1 parent 72aafbe commit 85b90f3

File tree

7 files changed

+112
-5
lines changed

7 files changed

+112
-5
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#include <stdio.h>
2+
3+
enum day
4+
{
5+
sunday = 1,
6+
monday,
7+
tuesday = 5,
8+
wednesday,
9+
thursday = 10,
10+
friday,
11+
saturday
12+
};
13+
14+
int main()
15+
{
16+
enum day temp = friday;
17+
temp = sunday;
18+
temp = monday;
19+
temp = tuesday;
20+
temp = wednesday;
21+
temp = thursday;
22+
temp = thursday + 1;
23+
temp = saturday;
24+
printf("%d\n", temp);
25+
return 0;
26+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
main.c
3+
--enum-range-check
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
^warning: ignoring
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#include <stdio.h>
2+
3+
enum day
4+
{
5+
sunday = 1,
6+
monday,
7+
tuesday = 5,
8+
wednesday,
9+
thursday = 10,
10+
friday,
11+
saturday
12+
};
13+
14+
int main()
15+
{
16+
enum day temp = 100;
17+
printf("%d\n", temp);
18+
return 0;
19+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
main.c
3+
--enum-range-check
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^VERIFICATION FAILED$
7+
--
8+
^warning: ignoring

src/analyses/goto_check.cpp

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,12 @@ class goto_checkt
4747
ns(_ns),
4848
local_bitvector_analysis(nullptr)
4949
{
50+
no_enum_check = false;
5051
enable_bounds_check=_options.get_bool_option("bounds-check");
5152
enable_pointer_check=_options.get_bool_option("pointer-check");
5253
enable_memory_leak_check=_options.get_bool_option("memory-leak-check");
5354
enable_div_by_zero_check=_options.get_bool_option("div-by-zero-check");
55+
enable_enum_range_check = _options.get_bool_option("enum-range-check");
5456
enable_signed_overflow_check=
5557
_options.get_bool_option("signed-overflow-check");
5658
enable_unsigned_overflow_check=
@@ -90,6 +92,7 @@ class goto_checkt
9092
std::unique_ptr<local_bitvector_analysist> local_bitvector_analysis;
9193
goto_programt::const_targett current_target;
9294
guard_managert guard_manager;
95+
bool no_enum_check;
9396

9497
/// Check an address-of expression:
9598
/// if it is a dereference then check the pointer
@@ -167,6 +170,7 @@ class goto_checkt
167170
void div_by_zero_check(const div_exprt &, const guardt &);
168171
void mod_by_zero_check(const mod_exprt &, const guardt &);
169172
void mod_overflow_check(const mod_exprt &, const guardt &);
173+
void enum_range_check(const exprt &, const guardt &);
170174
void undefined_shift_check(const shift_exprt &, const guardt &);
171175
void pointer_rel_check(const binary_relation_exprt &, const guardt &);
172176
void pointer_overflow_check(const exprt &, const guardt &);
@@ -220,6 +224,7 @@ class goto_checkt
220224
bool enable_pointer_check;
221225
bool enable_memory_leak_check;
222226
bool enable_div_by_zero_check;
227+
bool enable_enum_range_check;
223228
bool enable_signed_overflow_check;
224229
bool enable_unsigned_overflow_check;
225230
bool enable_pointer_overflow_check;
@@ -325,6 +330,35 @@ void goto_checkt::div_by_zero_check(
325330
guard);
326331
}
327332

333+
void goto_checkt::enum_range_check(const exprt &expr, const guardt &guard)
334+
{
335+
if(!enable_enum_range_check || no_enum_check)
336+
return;
337+
338+
const c_enum_tag_typet &c_enum_tag_type = to_c_enum_tag_type(expr.type());
339+
symbolt enum_type = ns.lookup(c_enum_tag_type.get_identifier());
340+
const c_enum_typet &c_enum_type = to_c_enum_type(enum_type.type);
341+
342+
const c_enum_typet::memberst enum_values = c_enum_type.members();
343+
344+
std::vector<exprt> disjuncts;
345+
for(const auto &enum_value : enum_values)
346+
{
347+
const constant_exprt val{enum_value.get_value(), c_enum_tag_type};
348+
disjuncts.push_back(equal_exprt(expr, val));
349+
}
350+
351+
const exprt check = disjunction(disjuncts);
352+
353+
add_guarded_property(
354+
check,
355+
"enum range check",
356+
"enum-range-check",
357+
expr.find_source_location(),
358+
expr,
359+
guard);
360+
}
361+
328362
void goto_checkt::undefined_shift_check(
329363
const shift_exprt &expr,
330364
const guardt &guard)
@@ -1668,6 +1702,9 @@ void goto_checkt::check_rec(const exprt &expr, guardt &guard)
16681702
forall_operands(it, expr)
16691703
check_rec(*it, guard);
16701704

1705+
if(expr.type().id() == ID_c_enum_tag)
1706+
enum_range_check(expr, guard);
1707+
16711708
if(expr.id()==ID_index)
16721709
{
16731710
bounds_check(to_index_expr(expr), guard);
@@ -1880,7 +1917,13 @@ void goto_checkt::goto_check(
18801917
{
18811918
const code_assignt &code_assign=to_code_assign(i.code);
18821919

1883-
check(code_assign.lhs());
1920+
// Reset the no_enum_check with the flag reset for exception
1921+
// safety
1922+
{
1923+
flag_resett no_enum_check_flag_resetter;
1924+
no_enum_check_flag_resetter.set_flag(no_enum_check, true);
1925+
check(code_assign.lhs());
1926+
}
18841927
check(code_assign.rhs());
18851928

18861929
// the LHS might invalidate any assertion

src/analyses/goto_check.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,11 @@ void goto_check(
3333
const optionst &options,
3434
goto_modelt &goto_model);
3535

36-
#define OPT_GOTO_CHECK \
37-
"(bounds-check)(pointer-check)(memory-leak-check)" \
38-
"(div-by-zero-check)(signed-overflow-check)(unsigned-overflow-check)" \
39-
"(pointer-overflow-check)(conversion-check)(undefined-shift-check)" \
36+
#define OPT_GOTO_CHECK \
37+
"(bounds-check)(pointer-check)(memory-leak-check)" \
38+
"(div-by-zero-check)(enum-range-check)(signed-overflow-check)(unsigned-" \
39+
"overflow-check)" \
40+
"(pointer-overflow-check)(conversion-check)(undefined-shift-check)" \
4041
"(float-overflow-check)(nan-check)(no-built-in-assertions)"
4142

4243
// clang-format off
@@ -60,6 +61,7 @@ void goto_check(
6061
options.set_option("pointer-check", cmdline.isset("pointer-check")); \
6162
options.set_option("memory-leak-check", cmdline.isset("memory-leak-check")); \
6263
options.set_option("div-by-zero-check", cmdline.isset("div-by-zero-check")); \
64+
options.set_option("enum-range-check", cmdline.isset("enum-range-check")); \
6365
options.set_option("signed-overflow-check", cmdline.isset("signed-overflow-check")); /* NOLINT(whitespace/line_length) */ \
6466
options.set_option("unsigned-overflow-check", cmdline.isset("unsigned-overflow-check")); /* NOLINT(whitespace/line_length) */ \
6567
options.set_option("pointer-overflow-check", cmdline.isset("pointer-overflow-check")); /* NOLINT(whitespace/line_length) */ \

src/goto-instrument/goto_instrument_parse_options.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1624,6 +1624,7 @@ void goto_instrument_parse_optionst::help()
16241624
"Safety checks:\n"
16251625
" --no-assertions ignore user assertions\n"
16261626
HELP_GOTO_CHECK
1627+
" --enum-range-check checks that all enum type expressions have values in the enum range\n" // NOLINT(*)
16271628
" --uninitialized-check add checks for uninitialized locals (experimental)\n" // NOLINT(*)
16281629
" --error-label label check that label is unreachable\n"
16291630
" --stack-depth n add check that call stack size of non-inlined functions never exceeds n\n" // NOLINT(*)

0 commit comments

Comments
 (0)