Skip to content

Commit 6daa8bd

Browse files
author
Daniel Kroening
authored
Merge pull request #1528 from tautschnig/shl-overflow
Check for overflow on left shift of signed ints
2 parents f9af374 + 1e3712c commit 6daa8bd

File tree

7 files changed

+137
-4
lines changed

7 files changed

+137
-4
lines changed
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
int main()
2+
{
3+
unsigned char x;
4+
unsigned r=x << ((sizeof(unsigned)-1)*8);
5+
r=x << ((sizeof(unsigned)-1)*8-1);
6+
r=(unsigned)x << ((sizeof(unsigned)-1)*8);
7+
8+
int s=-1 << ((sizeof(int)-1)*8);
9+
s=-256 << ((sizeof(int)-1)*8);
10+
return 0;
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CORE
2+
main.c
3+
--signed-overflow-check
4+
^SIGNAL=0$
5+
^\[.*\] arithmetic overflow on signed shl in .*: FAILURE$
6+
^\*\* 2 of 4 failed
7+
^VERIFICATION FAILED$
8+
--
9+
^warning: ignoring
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
int main()
2+
{
3+
unsigned char x;
4+
unsigned r=x << ((sizeof(unsigned)-1)*8);
5+
r=x << ((sizeof(unsigned)-1)*8-1);
6+
r=(unsigned)x << ((sizeof(unsigned)-1)*8);
7+
8+
int s=-1 << ((sizeof(int)-1)*8);
9+
s=-256 << ((sizeof(int)-1)*8);
10+
return 0;
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CORE
2+
main.c
3+
--undefined-shift-check
4+
^SIGNAL=0$
5+
^\[.*\] shift operand is negative in .*: FAILURE$
6+
^\*\* 1 of 2 failed
7+
^VERIFICATION FAILED$
8+
--
9+
^warning: ignoring

src/analyses/goto_check.cpp

+27
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,30 @@ void goto_checkt::undefined_shift_check(
286286
expr.find_source_location(),
287287
expr,
288288
guard);
289+
290+
if(op_type.id()==ID_signedbv && expr.id()==ID_shl)
291+
{
292+
binary_relation_exprt inequality(
293+
expr.op(), ID_ge, from_integer(0, op_type));
294+
295+
add_guarded_claim(
296+
inequality,
297+
"shift operand is negative",
298+
"undefined-shift",
299+
expr.find_source_location(),
300+
expr,
301+
guard);
302+
}
303+
}
304+
else
305+
{
306+
add_guarded_claim(
307+
false_exprt(),
308+
"shift of non-integer type",
309+
"undefined-shift",
310+
expr.find_source_location(),
311+
expr,
312+
guard);
289313
}
290314
}
291315

@@ -1418,6 +1442,9 @@ void goto_checkt::check_rec(
14181442
else if(expr.id()==ID_shl || expr.id()==ID_ashr || expr.id()==ID_lshr)
14191443
{
14201444
undefined_shift_check(to_shift_expr(expr), guard);
1445+
1446+
if(expr.id()==ID_shl && expr.type().id()==ID_signedbv)
1447+
integer_overflow_check(expr, guard);
14211448
}
14221449
else if(expr.id()==ID_mod)
14231450
{

src/solvers/flattening/boolbv_overflow.cpp

+69-4
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ Author: Daniel Kroening, [email protected]
88

99
#include "boolbv.h"
1010

11-
#include <cassert>
12-
11+
#include <util/invariant.h>
1312
#include <util/prefix.h>
1413
#include <util/string2int.h>
1514

@@ -59,7 +58,7 @@ literalt boolbvt::convert_overflow(const exprt &expr)
5958
if(operands[0].type()!=operands[1].type())
6059
throw "operand type mismatch on overflow-*";
6160

62-
assert(bv0.size()==bv1.size());
61+
DATA_INVARIANT(bv0.size()==bv1.size(), "operands size mismatch");
6362
std::size_t old_size=bv0.size();
6463
std::size_t new_size=old_size*2;
6564

@@ -86,7 +85,7 @@ literalt boolbvt::convert_overflow(const exprt &expr)
8685
bv_overflow.reserve(old_size);
8786

8887
// get top result bits, plus one more
89-
assert(old_size!=0);
88+
DATA_INVARIANT(old_size!=0, "zero-size operand");
9089
for(std::size_t i=old_size-1; i<result.size(); i++)
9190
bv_overflow.push_back(result[i]);
9291

@@ -96,6 +95,72 @@ literalt boolbvt::convert_overflow(const exprt &expr)
9695
return !prop.lor(all_one, all_zero);
9796
}
9897
}
98+
else if(expr.id() == ID_overflow_shl)
99+
{
100+
if(operands.size() != 2)
101+
throw "operator " + expr.id_string() + " takes two operands";
102+
103+
const bvt &bv0=convert_bv(operands[0]);
104+
const bvt &bv1=convert_bv(operands[1]);
105+
106+
std::size_t old_size = bv0.size();
107+
std::size_t new_size = old_size * 2;
108+
109+
bv_utilst::representationt rep=
110+
operands[0].type().id()==ID_signedbv?bv_utilst::representationt::SIGNED:
111+
bv_utilst::representationt::UNSIGNED;
112+
113+
bvt bv_ext=bv_utils.extension(bv0, new_size, rep);
114+
115+
bvt result=bv_utils.shift(bv_ext, bv_utilst::shiftt::LEFT, bv1);
116+
117+
// a negative shift is undefined; yet this isn't an overflow
118+
literalt neg_shift =
119+
operands[1].type().id() == ID_unsignedbv ?
120+
const_literal(false) :
121+
bv1.back(); // sign bit
122+
123+
// an undefined shift of a non-zero value always results in overflow; the
124+
// use of unsigned comparision is safe here as we cover the signed negative
125+
// case via neg_shift
126+
literalt undef =
127+
bv_utils.rel(
128+
bv1,
129+
ID_gt,
130+
bv_utils.build_constant(old_size, bv1.size()),
131+
bv_utilst::representationt::UNSIGNED);
132+
133+
literalt overflow;
134+
135+
if(rep == bv_utilst::representationt::UNSIGNED)
136+
{
137+
// get top result bits
138+
result.erase(result.begin(), result.begin() + old_size);
139+
140+
// one of the top bits is non-zero
141+
overflow=prop.lor(result);
142+
}
143+
else
144+
{
145+
// get top result bits plus sign bit
146+
DATA_INVARIANT(old_size != 0, "zero-size operand");
147+
result.erase(result.begin(), result.begin() + old_size - 1);
148+
149+
// the sign bit has changed
150+
literalt sign_change=!prop.lequal(bv0.back(), result.front());
151+
152+
// these need to be either all 1's or all 0's
153+
literalt all_one=prop.land(result);
154+
literalt all_zero=!prop.lor(result);
155+
156+
overflow=prop.lor(sign_change, !prop.lor(all_one, all_zero));
157+
}
158+
159+
// a negative shift isn't an overflow; else check the conditions built
160+
// above
161+
return
162+
prop.land(!neg_shift, prop.lselect(undef, prop.lor(bv0), overflow));
163+
}
99164
else if(expr.id()==ID_overflow_unary_minus)
100165
{
101166
if(operands.size()!=1)

src/util/irep_ids.def

+1
Original file line numberDiff line numberDiff line change
@@ -830,6 +830,7 @@ IREP_ID_TWO(C_java_generics_class_type, #java_generics_class_type)
830830
IREP_ID_TWO(generic_types, #generic_types)
831831
IREP_ID_TWO(type_variables, #type_variables)
832832
IREP_ID_ONE(havoc_object)
833+
IREP_ID_TWO(overflow_shl, overflow-shl)
833834

834835
#undef IREP_ID_ONE
835836
#undef IREP_ID_TWO

0 commit comments

Comments
 (0)