Skip to content

Commit b326498

Browse files
author
Daniel Kroening
authored
Merge pull request #3662 from diffblue/shl-overflow
shift left overflow check now done in goto_check
2 parents 5752f19 + 3be9b5e commit b326498

File tree

5 files changed

+120
-6
lines changed

5 files changed

+120
-6
lines changed
Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,24 @@
11
int main()
22
{
33
unsigned char x;
4+
5+
// signed, owing to promotion, and may overflow
46
unsigned r=x << ((sizeof(unsigned)-1)*8);
7+
8+
// signed, owing to promotion, and cannot overflow
59
r=x << ((sizeof(unsigned)-1)*8-1);
10+
11+
// unsigned
612
r=(unsigned)x << ((sizeof(unsigned)-1)*8);
713

8-
int s=-1 << ((sizeof(int)-1)*8);
9-
s=-256 << ((sizeof(int)-1)*8);
14+
// negative value or zero, not an overflow
15+
int s = -x << ((sizeof(int) - 1) * 8);
16+
17+
// overflow
18+
s = 1 << x;
19+
20+
// distance too far, not an overflow
21+
s = s << 100;
22+
1023
return 0;
1124
}

regression/cbmc/Overflow_Leftshift1/test.desc

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,13 @@ main.c
33
--signed-overflow-check
44
^EXIT=10$
55
^SIGNAL=0$
6-
^\[.*\] .* arithmetic overflow on signed shl in .*: FAILURE$
7-
^\*\* 2 of 4 failed
6+
^\[.*\] line 6 arithmetic overflow on signed shl in .*: FAILURE$
7+
^\[.*\] line 9 arithmetic overflow on signed shl in .*: SUCCESS$
8+
^\[.*\] line 15 arithmetic overflow on signed shl in .*: SUCCESS$
9+
^\[.*\] line 18 arithmetic overflow on signed shl in .*: FAILURE$
10+
^\*\* 2 of 5 failed
811
^VERIFICATION FAILED$
912
--
1013
^warning: ignoring
14+
^\[.*\] line 12 arithmetic overflow on signed shl in .*: .*
15+
^\[.*\] line 21 arithmetic overflow on signed shl in .*: .*

scripts/delete_failing_smt2_solver_tests

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@ rm Linking7/test.desc
3737
rm Linking7/member-name-mismatch.desc
3838
rm Malloc23/test.desc
3939
rm Multi_Dimensional_Array2/test.desc
40-
rm Overflow_Leftshift1/test.desc
41-
rm Overflow_Subtraction1/test.desc
4240
rm Pointer_Arithmetic11/test.desc
4341
rm Pointer_byte_extract2/test.desc
4442
rm Pointer_byte_extract5/no-simplify.desc

src/analyses/goto_check.cpp

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,81 @@ void goto_checkt::integer_overflow_check(
589589

590590
return;
591591
}
592+
else if(expr.id() == ID_shl)
593+
{
594+
if(type.id() == ID_signedbv)
595+
{
596+
const auto &shl_expr = to_shl_expr(expr);
597+
const auto &op = shl_expr.op();
598+
const auto &op_type = to_signedbv_type(type);
599+
const auto op_width = op_type.get_width();
600+
const auto &distance = shl_expr.distance();
601+
const auto &distance_type = distance.type();
602+
603+
// a left shift of a negative value is undefined;
604+
// yet this isn't an overflow
605+
exprt neg_value_shift;
606+
607+
if(op_type.id() == ID_unsignedbv)
608+
neg_value_shift = false_exprt();
609+
else
610+
neg_value_shift =
611+
binary_relation_exprt(op, ID_lt, from_integer(0, op_type));
612+
613+
// a shift with negative distance is undefined;
614+
// yet this isn't an overflow
615+
exprt neg_dist_shift;
616+
617+
if(distance_type.id() == ID_unsignedbv)
618+
neg_dist_shift = false_exprt();
619+
else
620+
neg_dist_shift =
621+
binary_relation_exprt(op, ID_lt, from_integer(0, distance_type));
622+
623+
// shifting a non-zero value by more than its width is undefined;
624+
// yet this isn't an overflow
625+
const exprt dist_too_large = binary_predicate_exprt(
626+
distance, ID_gt, from_integer(op_width, distance_type));
627+
628+
const exprt op_zero = equal_exprt(op, from_integer(0, op_type));
629+
630+
auto new_type = to_bitvector_type(op_type);
631+
new_type.set_width(op_width * 2);
632+
633+
const exprt op_ext = typecast_exprt(op, new_type);
634+
635+
const exprt op_ext_shifted = shl_exprt(op_ext, distance);
636+
637+
// get top bits of the shifted operand
638+
const exprt top_bits = extractbits_exprt(
639+
op_ext_shifted,
640+
new_type.get_width() - 1,
641+
op_width - 1,
642+
unsignedbv_typet(op_width + 1));
643+
644+
const exprt top_bits_zero =
645+
equal_exprt(top_bits, from_integer(0, top_bits.type()));
646+
647+
// a negative distance shift isn't an overflow;
648+
// a negative value shift isn't an overflow;
649+
// a shift that's too far isn't an overflow;
650+
// a shift of zero isn't overflow;
651+
// else check the top bits
652+
add_guarded_claim(
653+
disjunction({neg_value_shift,
654+
neg_dist_shift,
655+
dist_too_large,
656+
op_zero,
657+
top_bits_zero}),
658+
"arithmetic overflow on signed shl",
659+
"overflow",
660+
expr.find_source_location(),
661+
expr,
662+
guard);
663+
}
664+
665+
return;
666+
}
592667

593668
exprt overflow("overflow-"+expr.id_string(), bool_typet());
594669
overflow.operands()=expr.operands();

src/util/std_expr.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2959,6 +2959,29 @@ class shl_exprt:public shift_exprt
29592959
}
29602960
};
29612961

2962+
/// \brief Cast an exprt to a \ref shl_exprt
2963+
///
2964+
/// \a expr must be known to be \ref shl_exprt.
2965+
///
2966+
/// \param expr: Source expression
2967+
/// \return Object of type \ref shl_exprt
2968+
inline const shl_exprt &to_shl_expr(const exprt &expr)
2969+
{
2970+
PRECONDITION(expr.id() == ID_shl);
2971+
DATA_INVARIANT(
2972+
expr.operands().size() == 2, "Bit-wise shl must have two operands");
2973+
return static_cast<const shl_exprt &>(expr);
2974+
}
2975+
2976+
/// \copydoc to_shl_expr(const exprt &)
2977+
inline shl_exprt &to_shl_expr(exprt &expr)
2978+
{
2979+
PRECONDITION(expr.id() == ID_shl);
2980+
DATA_INVARIANT(
2981+
expr.operands().size() == 2, "Bit-wise shl must have two operands");
2982+
return static_cast<shl_exprt &>(expr);
2983+
}
2984+
29622985
/// \brief Arithmetic right shift
29632986
class ashr_exprt:public shift_exprt
29642987
{

0 commit comments

Comments
 (0)