@@ -30,6 +30,14 @@ using namespace ento;
30
30
31
31
namespace {
32
32
33
+ QualType getSufficientTypeForOverflowOp (CheckerContext &C, const QualType &T) {
34
+ assert (T->isIntegerType ());
35
+ ASTContext &ACtx = C.getASTContext ();
36
+
37
+ unsigned BitWidth = ACtx.getIntWidth (T);
38
+ return ACtx.getIntTypeForBitwidth (BitWidth * 2 , T->isSignedIntegerType ());
39
+ }
40
+
33
41
QualType getOverflowBuiltinResultType (const CallEvent &Call) {
34
42
assert (Call.getNumArgs () == 3 );
35
43
@@ -73,15 +81,18 @@ QualType getOverflowBuiltinResultType(const CallEvent &Call, CheckerContext &C,
73
81
return getOverflowBuiltinResultType (Call);
74
82
default :
75
83
assert (false && " Unknown overflow builtin" );
84
+ return Ast.IntTy ;
76
85
}
77
86
}
78
87
79
88
class BuiltinFunctionChecker : public Checker <eval::Call> {
80
89
public:
81
90
bool evalCall (const CallEvent &Call, CheckerContext &C) const ;
82
- void HandleOverflowBuiltin (const CallEvent &Call, CheckerContext &C,
91
+ void handleOverflowBuiltin (const CallEvent &Call, CheckerContext &C,
83
92
BinaryOperator::Opcode Op,
84
93
QualType ResultType) const ;
94
+ std::pair<bool , bool > checkOverflow (CheckerContext &C, SVal RetVal,
95
+ QualType Res) const ;
85
96
86
97
private:
87
98
// From: clang/include/clang/Basic/Builtins.def
@@ -101,7 +112,36 @@ class BuiltinFunctionChecker : public Checker<eval::Call> {
101
112
102
113
} // namespace
103
114
104
- void BuiltinFunctionChecker::HandleOverflowBuiltin (const CallEvent &Call,
115
+ std::pair<bool , bool >
116
+ BuiltinFunctionChecker::checkOverflow (CheckerContext &C, SVal RetVal,
117
+ QualType Res) const {
118
+ ProgramStateRef State = C.getState ();
119
+ SValBuilder &SVB = C.getSValBuilder ();
120
+ auto SvalToBool = [&](SVal val) {
121
+ return State->isNonNull (val).isConstrainedTrue ();
122
+ };
123
+ ASTContext &ACtx = C.getASTContext ();
124
+
125
+ assert (Res->isIntegerType ());
126
+
127
+ unsigned BitWidth = ACtx.getIntWidth (Res);
128
+ SVal MinType = nonloc::ConcreteInt (
129
+ llvm::APSInt::getMinValue (BitWidth, Res->isUnsignedIntegerType ()));
130
+ SVal MaxType = nonloc::ConcreteInt (
131
+ llvm::APSInt::getMaxValue (BitWidth, Res->isUnsignedIntegerType ()));
132
+
133
+ bool IsGreaterMax =
134
+ SvalToBool (SVB.evalBinOp (State, BO_GT, RetVal, MaxType, Res));
135
+ bool IsLessMin =
136
+ SvalToBool (SVB.evalBinOp (State, BO_LT, RetVal, MinType, Res));
137
+
138
+ bool IsLeMax = SvalToBool (SVB.evalBinOp (State, BO_LE, RetVal, MaxType, Res));
139
+ bool IsGeMin = SvalToBool (SVB.evalBinOp (State, BO_GE, RetVal, MinType, Res));
140
+
141
+ return {IsGreaterMax || IsLessMin, IsLeMax && IsGeMin};
142
+ }
143
+
144
+ void BuiltinFunctionChecker::handleOverflowBuiltin (const CallEvent &Call,
105
145
CheckerContext &C,
106
146
BinaryOperator::Opcode Op,
107
147
QualType ResultType) const {
@@ -115,14 +155,12 @@ void BuiltinFunctionChecker::HandleOverflowBuiltin(const CallEvent &Call,
115
155
SVal Arg1 = Call.getArgSVal (0 );
116
156
SVal Arg2 = Call.getArgSVal (1 );
117
157
158
+ SVal RetValMax = SVB.evalBinOp (State, Op, Arg1, Arg2,
159
+ getSufficientTypeForOverflowOp (C, ResultType));
118
160
SVal RetVal = SVB.evalBinOp (State, Op, Arg1, Arg2, ResultType);
119
161
120
- // TODO: Handle overflows with values that known to overflow. Like INT_MAX + 1
121
- // should not produce state for non-overflow case and threat it as
122
- // unreachable.
123
-
124
- // Handle non-overflow case.
125
- {
162
+ auto [Overflow, NotOverflow] = checkOverflow (C, RetValMax, ResultType);
163
+ if (NotOverflow) {
126
164
ProgramStateRef StateSuccess =
127
165
State->BindExpr (CE, C.getLocationContext (), SVB.makeTruthVal (false ));
128
166
@@ -132,11 +170,9 @@ void BuiltinFunctionChecker::HandleOverflowBuiltin(const CallEvent &Call,
132
170
C.addTransition (StateSuccess);
133
171
}
134
172
135
- // Handle overflow case.
136
- {
173
+ if (Overflow)
137
174
C.addTransition (
138
175
State->BindExpr (CE, C.getLocationContext (), SVB.makeTruthVal (true )));
139
- }
140
176
}
141
177
142
178
bool BuiltinFunctionChecker::isBuiltinLikeFunction (
@@ -183,7 +219,7 @@ bool BuiltinFunctionChecker::evalCall(const CallEvent &Call,
183
219
case Builtin::BI__builtin_umul_overflow:
184
220
case Builtin::BI__builtin_umull_overflow:
185
221
case Builtin::BI__builtin_umulll_overflow:
186
- HandleOverflowBuiltin (Call, C, BO_Mul,
222
+ handleOverflowBuiltin (Call, C, BO_Mul,
187
223
getOverflowBuiltinResultType (Call, C, BI));
188
224
return true ;
189
225
case Builtin::BI__builtin_sub_overflow:
@@ -193,7 +229,7 @@ bool BuiltinFunctionChecker::evalCall(const CallEvent &Call,
193
229
case Builtin::BI__builtin_usub_overflow:
194
230
case Builtin::BI__builtin_usubl_overflow:
195
231
case Builtin::BI__builtin_usubll_overflow:
196
- HandleOverflowBuiltin (Call, C, BO_Sub,
232
+ handleOverflowBuiltin (Call, C, BO_Sub,
197
233
getOverflowBuiltinResultType (Call, C, BI));
198
234
return true ;
199
235
case Builtin::BI__builtin_add_overflow:
@@ -203,7 +239,7 @@ bool BuiltinFunctionChecker::evalCall(const CallEvent &Call,
203
239
case Builtin::BI__builtin_uadd_overflow:
204
240
case Builtin::BI__builtin_uaddl_overflow:
205
241
case Builtin::BI__builtin_uaddll_overflow:
206
- HandleOverflowBuiltin (Call, C, BO_Add,
242
+ handleOverflowBuiltin (Call, C, BO_Add,
207
243
getOverflowBuiltinResultType (Call, C, BI));
208
244
return true ;
209
245
case Builtin::BI__builtin_assume:
0 commit comments