Skip to content

Commit 0a18c74

Browse files
committed
Use bitvector types to implement __builtin_{add,sub,mul}_overflow
The use of mathematical integers is not supported by the SAT back-end. Although GCC's specification describes the operations over mathematical integers, the same precision can be achieved by using bitvector types of sufficient width. The implementation now also introduces a fresh symbol to store the result of the full-precision operation. This ensures that the arguments to __builtin_{add,sub,mul}_overflow are evaluated exactly once.
1 parent a46f12d commit 0a18c74

File tree

3 files changed

+77
-27
lines changed

3 files changed

+77
-27
lines changed

regression/cbmc/gcc_builtin_add_overflow/main.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,15 @@ void check_generic(void)
110110
assert(0 && "reachability");
111111
}
112112

113+
void check_non_const()
114+
{
115+
int a, b, c, d, r;
116+
// this may overflow (negated assertion fails)
117+
assert(!__builtin_add_overflow(a, b, &r));
118+
// but need not overflow (assertion fails)
119+
assert(__builtin_add_overflow(c, d, &c));
120+
}
121+
113122
int main(void)
114123
{
115124
check_int();
@@ -119,4 +128,5 @@ int main(void)
119128
check_unsigned_long();
120129
check_unsigned_long_long();
121130
check_generic();
131+
check_non_const();
122132
}

regression/cbmc/gcc_builtin_add_overflow/test.desc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ main.c
5757
\[check_generic.assertion.9\] line \d+ assertion big_result == 2ll \* .*: SUCCESS
5858
\[check_generic.assertion.10\] line \d+ assertion __builtin_add_overflow\(.* / 2 \+ 1, .*/ 2 \+ 1, &big_result\): SUCCESS
5959
\[check_generic.assertion.11\] line \d+ assertion 0 && "reachability": FAILURE
60+
\[check_non_const\.assertion\.1\] line \d+ assertion !__builtin_add_overflow\(a, b, &r\): FAILURE
61+
\[check_non_const\.assertion\.2\] line \d+ assertion __builtin_add_overflow\(c, d, &c\): FAILURE
6062
VERIFICATION FAILED
6163
^EXIT=10$
6264
^SIGNAL=0$

src/ansi-c/c_typecheck_expr.cpp

Lines changed: 65 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Author: Daniel Kroening, [email protected]
1919
#include <util/config.h>
2020
#include <util/cprover_prefix.h>
2121
#include <util/expr_util.h>
22+
#include <util/fresh_symbol.h>
2223
#include <util/ieee_float.h>
2324
#include <util/mathematical_expr.h>
2425
#include <util/mathematical_types.h>
@@ -2862,48 +2863,85 @@ exprt c_typecheck_baset::do_special_functions(
28622863
}
28632864
}
28642865

2865-
// actual logic implementing the operators
2866+
// actual logic implementing the operators: perform operations on signed
2867+
// bitvector types of sufficiently large size to hold the result
28662868
auto const make_operation = [&identifier](exprt lhs, exprt rhs) -> exprt {
2869+
std::size_t lhs_ssize = to_bitvector_type(lhs.type()).get_width();
2870+
if(lhs.type().id() == ID_unsignedbv)
2871+
++lhs_ssize;
2872+
std::size_t rhs_ssize = to_bitvector_type(rhs.type()).get_width();
2873+
if(rhs.type().id() == ID_unsignedbv)
2874+
++rhs_ssize;
2875+
28672876
if(identifier == "__builtin_add_overflow")
28682877
{
2869-
return plus_exprt{lhs, rhs};
2878+
std::size_t ssize = std::max(lhs_ssize, rhs_ssize) + 1;
2879+
integer_bitvector_typet ssize_type{ID_signedbv, ssize};
2880+
return plus_exprt{typecast_exprt{lhs, ssize_type},
2881+
typecast_exprt{rhs, ssize_type}};
28702882
}
28712883
else if(identifier == "__builtin_sub_overflow")
28722884
{
2873-
return minus_exprt{lhs, rhs};
2885+
std::size_t ssize = std::max(lhs_ssize, rhs_ssize) + 1;
2886+
integer_bitvector_typet ssize_type{ID_signedbv, ssize};
2887+
return minus_exprt{typecast_exprt{lhs, ssize_type},
2888+
typecast_exprt{rhs, ssize_type}};
28742889
}
28752890
else
28762891
{
28772892
INVARIANT(
28782893
identifier == "__builtin_mul_overflow",
28792894
"the three overflow operations are add, sub and mul");
2880-
return mult_exprt{lhs, rhs};
2895+
std::size_t ssize = lhs_ssize + rhs_ssize;
2896+
integer_bitvector_typet ssize_type{ID_signedbv, ssize};
2897+
return mult_exprt{typecast_exprt{lhs, ssize_type},
2898+
typecast_exprt{rhs, ssize_type}};
28812899
}
28822900
};
28832901

2884-
// we’re basically generating this expression
2885-
// (*result = (result_type)((integer)lhs OP (integer)rhs)),
2886-
// ((integer)result == (integer)lhs OP (integer)rhs)
2887-
// i.e. perform the operation (+, -, *) on arbitrary length integer,
2888-
// cast to result type, check if the casted result is still equivalent
2889-
// to the arbitrary length result.
2890-
auto operation = make_operation(
2891-
typecast_exprt{lhs, integer_typet{}},
2892-
typecast_exprt{rhs, integer_typet{}});
2893-
2894-
auto operation_result =
2895-
typecast_exprt{operation, result_ptr.type().subtype()};
2896-
typecheck_expr_typecast(operation_result);
2897-
auto overflow_check = notequal_exprt{
2898-
typecast_exprt{dereference_exprt{result_ptr}, integer_typet{}},
2899-
operation};
2900-
typecheck_expr(overflow_check);
2901-
return exprt{ID_comma,
2902-
bool_typet{},
2903-
{side_effect_expr_assignt{dereference_exprt{result_ptr},
2904-
operation_result,
2905-
expr.source_location()},
2906-
overflow_check}};
2902+
// Generating the following statement expression:
2903+
// ({ large_signedbv tmp = (large_signedbv)lhs OP (large_signedbv)rhs;
2904+
// *result = (result_type)large_signedbv;
2905+
// (large_signedbv)*result != tmp; })
2906+
// This performs the operation (+, -, *) on a signed bitvector type of
2907+
// sufficiently large width to store the precise result, cast to result
2908+
// type, check if the casted result is not equivalent to the full-length
2909+
// result.
2910+
auto operation = make_operation(lhs, rhs);
2911+
// Disable overflow checks as the operation cannot overflow on the larger
2912+
// type
2913+
operation.add_source_location() = expr.source_location();
2914+
operation.add_source_location().add_pragma("disable:signed-overflow-check");
2915+
2916+
const symbolt &new_symbol = get_fresh_aux_symbol(
2917+
operation.type(),
2918+
id2string(current_symbol.name) + "$tmp",
2919+
id2string(current_symbol.base_name) + "$tmp",
2920+
expr.source_location(),
2921+
mode,
2922+
symbol_table);
2923+
2924+
code_declt decl{new_symbol.symbol_expr()};
2925+
decl.add_source_location() = expr.source_location();
2926+
2927+
code_assignt tmp_assignment{decl.symbol(), operation};
2928+
tmp_assignment.add_source_location() = expr.source_location();
2929+
2930+
const auto &result_type = to_pointer_type(result_ptr.type()).subtype();
2931+
code_assignt result_assignment{dereference_exprt{result_ptr},
2932+
typecast_exprt{decl.symbol(), result_type}};
2933+
result_assignment.add_source_location() = expr.source_location();
2934+
2935+
notequal_exprt overflow_check{
2936+
typecast_exprt{dereference_exprt{result_ptr}, operation.type()},
2937+
decl.symbol()};
2938+
overflow_check.add_source_location() = expr.source_location();
2939+
code_expressiont return_expr{overflow_check};
2940+
2941+
code_blockt statements{
2942+
{decl, tmp_assignment, result_assignment, return_expr}};
2943+
return side_effect_expr_statement_expressiont{
2944+
statements, overflow_check.type(), expr.source_location()};
29072945
}
29082946
else
29092947
return nil_exprt();

0 commit comments

Comments
 (0)