Skip to content

Commit 06ef36d

Browse files
authored
Merge pull request #4972 from angelhof/enum-range-check
Enum range check
2 parents 8099af0 + 933eb5e commit 06ef36d

File tree

11 files changed

+166
-13
lines changed

11 files changed

+166
-13
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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+
#pragma CPROVER check push
17+
#pragma CPROVER check disable "enum-range"
18+
enum day temp = 100;
19+
printf("%d\n", temp);
20+
#pragma CPROVER check pop
21+
return 0;
22+
}
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: 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: 46 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);
@@ -1807,6 +1844,8 @@ void goto_checkt::goto_check(
18071844
flag_resetter.set_flag(enable_memory_leak_check, false);
18081845
else if(d.first == "disable:div-by-zero-check")
18091846
flag_resetter.set_flag(enable_div_by_zero_check, false);
1847+
else if(d.first == "disable:enum-range-check")
1848+
flag_resetter.set_flag(enable_enum_range_check, false);
18101849
else if(d.first == "disable:signed-overflow-check")
18111850
flag_resetter.set_flag(enable_signed_overflow_check, false);
18121851
else if(d.first == "disable:unsigned-overflow-check")
@@ -1881,7 +1920,13 @@ void goto_checkt::goto_check(
18811920
{
18821921
const code_assignt &code_assign=to_code_assign(i.code);
18831922

1884-
check(code_assign.lhs());
1923+
// Reset the no_enum_check with the flag reset for exception
1924+
// safety
1925+
{
1926+
flag_resett no_enum_check_flag_resetter;
1927+
no_enum_check_flag_resetter.set_flag(no_enum_check, true);
1928+
check(code_assign.lhs());
1929+
}
18851930
check(code_assign.rhs());
18861931

18871932
// the LHS might invalidate any assertion

src/analyses/goto_check.h

Lines changed: 7 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
@@ -53,13 +54,15 @@ void goto_check(
5354
" --float-overflow-check check floating-point for +/-Inf\n" \
5455
" --nan-check check floating-point for NaN\n" \
5556
" --no-built-in-assertions ignore assertions in built-in library\n" \
57+
" --enum-range-check checks that all enum type expressions have values in the enum range\n" /* NOLINT(whitespace/line_length) */ \
5658
// clang-format on
5759

5860
#define PARSE_OPTIONS_GOTO_CHECK(cmdline, options) \
5961
options.set_option("bounds-check", cmdline.isset("bounds-check")); \
6062
options.set_option("pointer-check", cmdline.isset("pointer-check")); \
6163
options.set_option("memory-leak-check", cmdline.isset("memory-leak-check")); \
6264
options.set_option("div-by-zero-check", cmdline.isset("div-by-zero-check")); \
65+
options.set_option("enum-range-check", cmdline.isset("enum-range-check")); \
6366
options.set_option("signed-overflow-check", cmdline.isset("signed-overflow-check")); /* NOLINT(whitespace/line_length) */ \
6467
options.set_option("unsigned-overflow-check", cmdline.isset("unsigned-overflow-check")); /* NOLINT(whitespace/line_length) */ \
6568
options.set_option("pointer-overflow-check", cmdline.isset("pointer-overflow-check")); /* NOLINT(whitespace/line_length) */ \

src/ansi-c/c_typecheck_base.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,8 +228,9 @@ class c_typecheck_baset:
228228
typet enum_constant_type(
229229
const mp_integer &min, const mp_integer &max) const;
230230

231-
typet enum_underlying_type(
232-
const mp_integer &min, const mp_integer &max,
231+
bitvector_typet enum_underlying_type(
232+
const mp_integer &min,
233+
const mp_integer &max,
233234
bool is_packed) const;
234235

235236
// this cleans expressions in array types

src/ansi-c/c_typecheck_type.cpp

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1077,7 +1077,7 @@ typet c_typecheck_baset::enum_constant_type(
10771077
}
10781078
}
10791079

1080-
typet c_typecheck_baset::enum_underlying_type(
1080+
bitvector_typet c_typecheck_baset::enum_underlying_type(
10811081
const mp_integer &min_value,
10821082
const mp_integer &max_value,
10831083
bool is_packed) const
@@ -1215,6 +1215,8 @@ void c_typecheck_baset::typecheck_c_enum_type(typet &type)
12151215
c_enum_typet::c_enum_membert member;
12161216
member.set_identifier(identifier);
12171217
member.set_base_name(base_name);
1218+
// Note: The value will be correctly set to a bv type when we know
1219+
// the width of the bitvector
12181220
member.set_value(integer2string(value));
12191221
enum_members.push_back(member);
12201222

@@ -1228,6 +1230,20 @@ void c_typecheck_baset::typecheck_c_enum_type(typet &type)
12281230

12291231
bool is_packed=type.get_bool(ID_C_packed);
12301232

1233+
// We use a subtype to store the underlying type.
1234+
bitvector_typet underlying_type =
1235+
enum_underlying_type(min_value, max_value, is_packed);
1236+
1237+
// Get the width to make the values have a bitvector type
1238+
std::size_t width = underlying_type.get_width();
1239+
for(auto &member : enum_members)
1240+
{
1241+
// Note: This is inefficient as it first turns integers to strings
1242+
// and then turns them back to bvrep
1243+
auto value = string2integer(id2string(member.get_value()));
1244+
member.set_value(integer2bvrep(value, width));
1245+
}
1246+
12311247
// tag?
12321248
if(type.find(ID_tag).is_nil())
12331249
{
@@ -1268,10 +1284,6 @@ void c_typecheck_baset::typecheck_c_enum_type(typet &type)
12681284
for(const auto &member : enum_members)
12691285
body.push_back(member);
12701286

1271-
// We use a subtype to store the underlying type.
1272-
typet underlying_type=
1273-
enum_underlying_type(min_value, max_value, is_packed);
1274-
12751287
enum_tag_symbol.type.subtype()=underlying_type;
12761288

12771289
// is it in the symbol table already?

src/ansi-c/scanner.l

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,9 +235,10 @@ string_lit ("L"|"u"|"U"|"u8")?["]{s_char}*["]
235235
CPROVER_PREFIX "__CPROVER_"
236236

237237
arith_check ("conversion"|"undefined-shift"|"nan"|"div-by-zero")
238+
enum_check "enum-range"
238239
memory_check ("bounds"|"pointer"|"memory_leak")
239240
overflow_check ("signed"|"unsigned"|"pointer"|"float")"-overflow"
240-
named_check ["]({arith_check}|{memory_check}|{overflow_check})["]
241+
named_check ["]({arith_check}|{enum_check}|{memory_check}|{overflow_check})["]
241242

242243
%x GRAMMAR
243244
%x COMMENT1

0 commit comments

Comments
 (0)