Skip to content

Commit de08db4

Browse files
committed
add check for INT_MIN % -1
INT_MIN % -1 is, in principle, defined to be zero in ANSI C, C99, C++98, and C++11. Most compilers, however, fail to produce zero, and in some cases generate an exception. C11 explicitly makes this case undefined. This commit adds a check for this particular case, classified as signed overflow.
1 parent e6ce264 commit de08db4

File tree

3 files changed

+58
-5
lines changed

3 files changed

+58
-5
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#include <limits.h>
2+
3+
int main()
4+
{
5+
signed int x, y;
6+
7+
// this is undefined in C11
8+
y = -1;
9+
x = INT_MIN % y;
10+
11+
// always ok
12+
x = y % 2;
13+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
mod_overflow.c
3+
--signed-overflow-check
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^\[.*\] line 9 result of signed mod is not representable in .*: FAILURE$
7+
^VERIFICATION FAILED$
8+
--
9+
^\[.*\] line 12 result of signed mod is not representable in .*: FAILURE$
10+
^warning: ignoring

src/analyses/goto_check.cpp

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ class goto_checkt
104104
void bounds_check(const index_exprt &, const guardt &);
105105
void div_by_zero_check(const div_exprt &, const guardt &);
106106
void mod_by_zero_check(const mod_exprt &, const guardt &);
107+
void mod_overflow_check(const mod_exprt &, const guardt &);
107108
void undefined_shift_check(const shift_exprt &, const guardt &);
108109
void pointer_rel_check(const exprt &, const guardt &);
109110
void pointer_overflow_check(const exprt &, const guardt &);
@@ -348,6 +349,39 @@ void goto_checkt::mod_by_zero_check(
348349
guard);
349350
}
350351

352+
/// check a mod expression for the case INT_MIN % -1
353+
void goto_checkt::mod_overflow_check(const mod_exprt &expr, const guardt &guard)
354+
{
355+
if(!enable_signed_overflow_check)
356+
return;
357+
358+
const auto &type = expr.type();
359+
360+
if(type.id() == ID_signedbv)
361+
{
362+
// INT_MIN % -1 is, in principle, defined to be zero in
363+
// ANSI C, C99, C++98, and C++11. Most compilers, however,
364+
// fail to produce 0, and in some cases generate an exception.
365+
// C11 explicitly makes this case undefined.
366+
367+
notequal_exprt int_min_neq(
368+
expr.op0(), to_signedbv_type(type).smallest_expr());
369+
370+
notequal_exprt minus_one_neq(
371+
expr.op1(), from_integer(-1, expr.op1().type()));
372+
373+
add_guarded_claim(
374+
or_exprt(int_min_neq, minus_one_neq),
375+
"result of signed mod is not representable",
376+
"overflow",
377+
expr.find_source_location(),
378+
expr,
379+
guard);
380+
}
381+
382+
return;
383+
}
384+
351385
void goto_checkt::conversion_check(
352386
const exprt &expr,
353387
const guardt &guard)
@@ -563,11 +597,6 @@ void goto_checkt::integer_overflow_check(
563597

564598
return;
565599
}
566-
else if(expr.id()==ID_mod)
567-
{
568-
// these can't overflow
569-
return;
570-
}
571600
else if(expr.id()==ID_unary_minus)
572601
{
573602
if(type.id()==ID_signedbv)
@@ -1543,6 +1572,7 @@ void goto_checkt::check_rec(const exprt &expr, guardt &guard, bool address)
15431572
else if(expr.id()==ID_mod)
15441573
{
15451574
mod_by_zero_check(to_mod_expr(expr), guard);
1575+
mod_overflow_check(to_mod_expr(expr), guard);
15461576
}
15471577
else if(expr.id()==ID_plus || expr.id()==ID_minus ||
15481578
expr.id()==ID_mult ||

0 commit comments

Comments
 (0)