Skip to content

Commit 72ddc18

Browse files
Merge pull request #5249 from hannes-steffenhagen-diffblue/feature/gcc-builtin-overflow-generic
Add generic gcc overflow builtins
2 parents 051911e + 47ae446 commit 72ddc18

File tree

7 files changed

+186
-4
lines changed

7 files changed

+186
-4
lines changed

regression/cbmc/gcc_builtin_add_overflow/main.c

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,13 +82,31 @@ void check_unsigned_long_long(void)
8282
unsigned long long const one = 1ull;
8383
unsigned long long result;
8484

85-
assert(!__builtin_uaddl_overflow(one, one, &result));
85+
assert(!__builtin_uaddll_overflow(one, one, &result));
8686
assert(result == 2ull);
8787
assert(!__builtin_uaddll_overflow(ULLONG_MAX / 2, ULLONG_MAX / 2, &result));
8888
assert(result + 1ull == ULLONG_MAX);
8989
assert(
9090
__builtin_uaddll_overflow(ULLONG_MAX / 2 + 2, ULLONG_MAX / 2 + 2, &result));
91-
assert(__builtin_uaddl_overflow(one, ULLONG_MAX, &result));
91+
assert(__builtin_uaddll_overflow(one, ULLONG_MAX, &result));
92+
assert(0 && "reachability");
93+
}
94+
95+
void check_generic(void)
96+
{
97+
unsigned char small_result;
98+
signed long long big_result;
99+
assert(!__builtin_add_overflow(17, 25, &small_result));
100+
assert(small_result == 42);
101+
assert(!__builtin_add_overflow(17, 25, &big_result));
102+
assert(big_result == 42ll);
103+
assert(__builtin_add_overflow(216, 129, &small_result));
104+
assert(!__builtin_add_overflow(216, 129, &big_result));
105+
assert(big_result == 345);
106+
assert(!__builtin_add_overflow(INT_MAX, INT_MAX, &big_result));
107+
assert(big_result == 2ll * INT_MAX);
108+
assert(
109+
__builtin_add_overflow(LLONG_MAX / 2 + 1, LLONG_MAX / 2 + 1, &big_result));
92110
assert(0 && "reachability");
93111
}
94112

@@ -100,4 +118,5 @@ int main(void)
100118
check_unsigned();
101119
check_unsigned_long();
102120
check_unsigned_long_long();
121+
check_generic();
103122
}

regression/cbmc/gcc_builtin_add_overflow/test.desc

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,24 @@ main.c
3939
\[check_unsigned_long.assertion.5\] line \d+ assertion __builtin_uaddl_overflow\(.* / 2 \+ 2, .* / 2 \+ 2, &result\): SUCCESS
4040
\[check_unsigned_long.assertion.6\] line \d+ assertion __builtin_uaddl_overflow\(one, .*, &result\): SUCCESS
4141
\[check_unsigned_long.assertion.7\] line \d+ assertion 0 && "reachability": FAILURE
42-
\[check_unsigned_long_long.assertion.1\] line \d+ assertion !__builtin_uaddl_overflow\(one, one, &result\): SUCCESS
42+
\[check_unsigned_long_long.assertion.1\] line \d+ assertion !__builtin_uaddll_overflow\(one, one, &result\): SUCCESS
4343
\[check_unsigned_long_long.assertion.2\] line \d+ assertion result == 2ull: SUCCESS
4444
\[check_unsigned_long_long.assertion.3\] line \d+ assertion !__builtin_uaddll_overflow\(.* / 2, .* / 2, &result\): SUCCESS
4545
\[check_unsigned_long_long.assertion.4\] line \d+ assertion result \+ 1ull == .*: SUCCESS
4646
\[check_unsigned_long_long.assertion.5\] line \d+ assertion __builtin_uaddll_overflow\(.* / 2 \+ 2, .* / 2 \+ 2, &result\): SUCCESS
47-
\[check_unsigned_long_long.assertion.6\] line \d+ assertion __builtin_uaddl_overflow\(one, .*, &result\): SUCCESS
47+
\[check_unsigned_long_long.assertion.6\] line \d+ assertion __builtin_uaddll_overflow\(one, .*, &result\): SUCCESS
4848
\[check_unsigned_long_long.assertion.7\] line \d+ assertion 0 && "reachability": FAILURE
49+
\[check_generic.assertion.1\] line \d+ assertion !__builtin_add_overflow\(17, 25, &small_result\): SUCCESS
50+
\[check_generic.assertion.2\] line \d+ assertion small_result == 42: SUCCESS
51+
\[check_generic.assertion.3\] line \d+ assertion !__builtin_add_overflow\(17, 25, &big_result\): SUCCESS
52+
\[check_generic.assertion.4\] line \d+ assertion big_result == 42ll: SUCCESS
53+
\[check_generic.assertion.5\] line \d+ assertion __builtin_add_overflow\(216, 129, &small_result\): SUCCESS
54+
\[check_generic.assertion.6\] line \d+ assertion !__builtin_add_overflow\(216, 129, &big_result\): SUCCESS
55+
\[check_generic.assertion.7\] line \d+ assertion big_result == 345: SUCCESS
56+
\[check_generic.assertion.8\] line \d+ assertion !__builtin_add_overflow\(.*, .*, &big_result\): SUCCESS
57+
\[check_generic.assertion.9\] line \d+ assertion big_result == 2ll \* .*: SUCCESS
58+
\[check_generic.assertion.10\] line \d+ assertion __builtin_add_overflow\(.* / 2 \+ 1, .*/ 2 \+ 1, &big_result\): SUCCESS
59+
\[check_generic.assertion.11\] line \d+ assertion 0 && "reachability": FAILURE
4960
VERIFICATION FAILED
5061
^EXIT=10$
5162
^SIGNAL=0$

regression/cbmc/gcc_builtin_mul_overflow/main.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,22 @@ void check_unsigned_long_long(void)
104104
assert(0 && "reachability");
105105
}
106106

107+
void check_generic(void)
108+
{
109+
unsigned char small_result;
110+
long long big_result;
111+
112+
assert(__builtin_mul_overflow(100, 100, &small_result));
113+
assert(!__builtin_mul_overflow(100, 100, &big_result));
114+
assert(big_result == 10000);
115+
assert(!__builtin_mul_overflow(10, 10, &small_result));
116+
assert(small_result == 100);
117+
assert(!__builtin_mul_overflow(INT_MAX, INT_MAX, &big_result));
118+
assert(big_result == 4611686014132420609ll);
119+
assert(__builtin_mul_overflow(INT_MAX * 2ll, INT_MAX * 2ll, &big_result));
120+
assert(0 && "reachability");
121+
}
122+
107123
int main(void)
108124
{
109125
check_int();
@@ -112,5 +128,6 @@ int main(void)
112128
check_unsigned();
113129
check_unsigned_long();
114130
check_unsigned_long_long();
131+
check_generic();
115132
return 0;
116133
}

regression/cbmc/gcc_builtin_mul_overflow/test.desc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,14 @@ main.c
3737
\[check_unsigned_long_long.assertion.4\] line \d+ assertion result == lt_isqrt_of_unsigned_long_long_max \* lt_isqrt_of_unsigned_long_long_max: SUCCESS
3838
\[check_unsigned_long_long.assertion.5\] line \d+ assertion __builtin_umulll_overflow\( lt_isqrt_of_unsigned_long_long_max << 1, lt_isqrt_of_unsigned_long_long_max << 1, &result\): SUCCESS
3939
\[check_unsigned_long_long.assertion.6\] line \d+ assertion 0 && "reachability": FAILURE
40+
\[check_generic.assertion.1\] line \d+ assertion __builtin_mul_overflow\(100, 100, &small_result\): SUCCESS
41+
\[check_generic.assertion.2\] line \d+ assertion !__builtin_mul_overflow\(100, 100, &big_result\): SUCCESS
42+
\[check_generic.assertion.3\] line \d+ assertion big_result == 10000: SUCCESS
43+
\[check_generic.assertion.4\] line \d+ assertion !__builtin_mul_overflow\(10, 10, &small_result\): SUCCESS
44+
\[check_generic.assertion.5\] line \d+ assertion small_result == 100: SUCCESS
45+
\[check_generic.assertion.6\] line \d+ assertion !__builtin_mul_overflow\(.*, .*, &big_result\): SUCCESS
46+
\[check_generic.assertion.7\] line \d+ assertion big_result == 4611686014132420609ll: SUCCESS
47+
\[check_generic.assertion.8\] line \d+ assertion __builtin_mul_overflow\(.* \* 2ll, .* \* 2ll, &big_result\): SUCCESS
48+
\[check_generic.assertion.9\] line \d+ assertion 0 && "reachability": FAILURE
4049
^EXIT=10$
4150
^SIGNAL=0$

regression/cbmc/gcc_builtin_sub_overflow/main.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,23 @@ void check_unsigned_long_long(void)
7171
assert(0 && "reachability");
7272
}
7373

74+
void check_generic(void)
75+
{
76+
unsigned char small_result;
77+
long long big_result;
78+
79+
assert(__builtin_sub_overflow(5, 10, &small_result));
80+
assert(!__builtin_sub_overflow(5, 10, &big_result));
81+
assert(big_result == -5ll);
82+
assert(!__builtin_sub_overflow(10, 5, &small_result));
83+
assert(small_result == 5);
84+
assert(!__builtin_sub_overflow(10, 5, &big_result));
85+
assert(big_result == 5ll);
86+
assert(!__builtin_sub_overflow(INT_MIN, INT_MAX, &big_result));
87+
assert(big_result == 2ll * INT_MIN + 1);
88+
assert(0 && "reachability");
89+
}
90+
7491
int main(void)
7592
{
7693
check_int();
@@ -79,4 +96,5 @@ int main(void)
7996
check_unsigned();
8097
check_unsigned_long();
8198
check_unsigned_long_long();
99+
check_generic();
82100
}

regression/cbmc/gcc_builtin_sub_overflow/test.desc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,15 @@ main.c
4040
\[check_unsigned_long_long.assertion.5\] line \d+ assertion result == 0ull: SUCCESS
4141
\[check_unsigned_long_long.assertion.6\] line \d+ assertion __builtin_usubll_overflow\(.* / 2ull, .*, &result\): SUCCESS
4242
\[check_unsigned_long_long.assertion.7\] line \d+ assertion 0 && "reachability": FAILURE
43+
\[check_generic.assertion.1\] line 79 assertion __builtin_sub_overflow\(5, 10, &small_result\): SUCCESS
44+
\[check_generic.assertion.2\] line 80 assertion !__builtin_sub_overflow\(5, 10, &big_result\): SUCCESS
45+
\[check_generic.assertion.3\] line 81 assertion big_result == -5ll: SUCCESS
46+
\[check_generic.assertion.4\] line 82 assertion !__builtin_sub_overflow\(10, 5, &small_result\): SUCCESS
47+
\[check_generic.assertion.5\] line 83 assertion small_result == 5: SUCCESS
48+
\[check_generic.assertion.6\] line 84 assertion !__builtin_sub_overflow\(10, 5, &big_result\): SUCCESS
49+
\[check_generic.assertion.7\] line 85 assertion big_result == 5ll: SUCCESS
50+
\[check_generic.assertion.8\] line 86 assertion !__builtin_sub_overflow\(.*, .*, &big_result\): SUCCESS
51+
\[check_generic.assertion.9\] line 87 assertion big_result == 2ll \* .* \+ 1: SUCCESS
52+
\[check_generic.assertion.10\] line 88 assertion 0 && "reachability": FAILURE
4353
^EXIT=10$
4454
^SIGNAL=0$

src/ansi-c/c_typecheck_expr.cpp

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Author: Daniel Kroening, [email protected]
1212
#include "c_typecheck_base.h"
1313

1414
#include <cassert>
15+
#include <sstream>
1516

1617
#include <util/arith_tools.h>
1718
#include <util/c_types.h>
@@ -33,6 +34,7 @@ Author: Daniel Kroening, [email protected]
3334
#include "builtin_factory.h"
3435
#include "c_qualifiers.h"
3536
#include "c_typecast.h"
37+
#include "expr2c.h"
3638
#include "padding.h"
3739
#include "type2name.h"
3840

@@ -2801,6 +2803,102 @@ exprt c_typecheck_baset::do_special_functions(
28012803
overflow.type() = bool_typet{};
28022804
return overflow;
28032805
}
2806+
else if(
2807+
identifier == "__builtin_add_overflow" ||
2808+
identifier == "__builtin_sub_overflow" ||
2809+
identifier == "__builtin_mul_overflow")
2810+
{
2811+
// check function signature
2812+
if(expr.arguments().size() != 3)
2813+
{
2814+
std::ostringstream error_message;
2815+
error_message << expr.source_location().as_string() << ": " << identifier
2816+
<< " takes exactly 3 arguments, but "
2817+
<< expr.arguments().size() << " were provided";
2818+
throw invalid_source_file_exceptiont{error_message.str()};
2819+
}
2820+
2821+
auto lhs = expr.arguments()[0];
2822+
auto rhs = expr.arguments()[1];
2823+
auto result_ptr = expr.arguments()[2];
2824+
2825+
typecheck_expr(lhs);
2826+
typecheck_expr(rhs);
2827+
typecheck_expr(result_ptr);
2828+
2829+
{
2830+
auto const raise_wrong_argument_error =
2831+
[this,
2832+
identifier](const exprt &wrong_argument, std::size_t argument_number) {
2833+
std::ostringstream error_message;
2834+
error_message << wrong_argument.source_location().as_string() << ": "
2835+
<< identifier << " has signature " << identifier
2836+
<< "(integral, integral, integral*), "
2837+
<< "but argument " << argument_number << " ("
2838+
<< expr2c(wrong_argument, *this) << ") has type `"
2839+
<< type2c(wrong_argument.type(), *this) << '`';
2840+
throw invalid_source_file_exceptiont{error_message.str()};
2841+
};
2842+
for(auto const arg_index : {0, 1})
2843+
{
2844+
auto const &argument = expr.arguments()[arg_index];
2845+
2846+
if(!is_signed_or_unsigned_bitvector(argument.type()))
2847+
{
2848+
raise_wrong_argument_error(argument, arg_index + 1);
2849+
}
2850+
}
2851+
if(
2852+
result_ptr.type().id() != ID_pointer ||
2853+
!is_signed_or_unsigned_bitvector(result_ptr.type().subtype()))
2854+
{
2855+
raise_wrong_argument_error(result_ptr, 3);
2856+
}
2857+
}
2858+
2859+
// actual logic implementing the operators
2860+
auto const make_operation = [&identifier](exprt lhs, exprt rhs) -> exprt {
2861+
if(identifier == "__builtin_add_overflow")
2862+
{
2863+
return plus_exprt{lhs, rhs};
2864+
}
2865+
else if(identifier == "__builtin_sub_overflow")
2866+
{
2867+
return minus_exprt{lhs, rhs};
2868+
}
2869+
else
2870+
{
2871+
INVARIANT(
2872+
identifier == "__builtin_mul_overflow",
2873+
"the three overflow operations are add, sub and mul");
2874+
return mult_exprt{lhs, rhs};
2875+
}
2876+
};
2877+
2878+
// we’re basically generating this expression
2879+
// (*result = (result_type)((integer)lhs OP (integer)rhs)),
2880+
// ((integer)result == (integer)lhs OP (integer)rhs)
2881+
// i.e. perform the operation (+, -, *) on arbitrary length integer,
2882+
// cast to result type, check if the casted result is still equivalent
2883+
// to the arbitrary length result.
2884+
auto operation = make_operation(
2885+
typecast_exprt{lhs, integer_typet{}},
2886+
typecast_exprt{rhs, integer_typet{}});
2887+
2888+
auto operation_result =
2889+
typecast_exprt{operation, result_ptr.type().subtype()};
2890+
typecheck_expr_typecast(operation_result);
2891+
auto overflow_check = notequal_exprt{
2892+
typecast_exprt{dereference_exprt{result_ptr}, integer_typet{}},
2893+
operation};
2894+
typecheck_expr(overflow_check);
2895+
return exprt{ID_comma,
2896+
bool_typet{},
2897+
{side_effect_expr_assignt{dereference_exprt{result_ptr},
2898+
operation_result,
2899+
expr.source_location()},
2900+
overflow_check}};
2901+
}
28042902
else
28052903
return nil_exprt();
28062904
}

0 commit comments

Comments
 (0)