16
16
17
17
#include " clang/Basic/Builtins.h"
18
18
#include " clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
19
+ #include " clang/StaticAnalyzer/Checkers/Taint.h"
19
20
#include " clang/StaticAnalyzer/Core/Checker.h"
20
21
#include " clang/StaticAnalyzer/Core/CheckerManager.h"
21
22
#include " clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
22
23
#include " clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
23
24
#include " clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
25
+ #include " clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
24
26
#include " clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
27
+ #include " clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
25
28
26
29
using namespace clang ;
27
30
using namespace ento ;
31
+ using namespace taint ;
28
32
29
33
namespace {
30
34
35
+ QualType getSufficientTypeForOverflowOp (CheckerContext &C, const QualType &T) {
36
+ // Calling a builtin with a non-integer type result produces compiler error.
37
+ assert (T->isIntegerType ());
38
+
39
+ ASTContext &ACtx = C.getASTContext ();
40
+
41
+ unsigned BitWidth = ACtx.getIntWidth (T);
42
+ return ACtx.getIntTypeForBitwidth (BitWidth * 2 , T->isSignedIntegerType ());
43
+ }
44
+
45
+ QualType getOverflowBuiltinResultType (const CallEvent &Call) {
46
+ // Calling a builtin with an incorrect argument count produces compiler error.
47
+ assert (Call.getNumArgs () == 3 );
48
+
49
+ return Call.getArgExpr (2 )->getType ()->getPointeeType ();
50
+ }
51
+
52
+ QualType getOverflowBuiltinResultType (const CallEvent &Call, CheckerContext &C,
53
+ unsigned BI) {
54
+ // Calling a builtin with an incorrect argument count produces compiler error.
55
+ assert (Call.getNumArgs () == 3 );
56
+
57
+ ASTContext &ACtx = C.getASTContext ();
58
+
59
+ switch (BI) {
60
+ case Builtin::BI__builtin_smul_overflow:
61
+ case Builtin::BI__builtin_ssub_overflow:
62
+ case Builtin::BI__builtin_sadd_overflow:
63
+ return ACtx.IntTy ;
64
+ case Builtin::BI__builtin_smull_overflow:
65
+ case Builtin::BI__builtin_ssubl_overflow:
66
+ case Builtin::BI__builtin_saddl_overflow:
67
+ return ACtx.LongTy ;
68
+ case Builtin::BI__builtin_smulll_overflow:
69
+ case Builtin::BI__builtin_ssubll_overflow:
70
+ case Builtin::BI__builtin_saddll_overflow:
71
+ return ACtx.LongLongTy ;
72
+ case Builtin::BI__builtin_umul_overflow:
73
+ case Builtin::BI__builtin_usub_overflow:
74
+ case Builtin::BI__builtin_uadd_overflow:
75
+ return ACtx.UnsignedIntTy ;
76
+ case Builtin::BI__builtin_umull_overflow:
77
+ case Builtin::BI__builtin_usubl_overflow:
78
+ case Builtin::BI__builtin_uaddl_overflow:
79
+ return ACtx.UnsignedLongTy ;
80
+ case Builtin::BI__builtin_umulll_overflow:
81
+ case Builtin::BI__builtin_usubll_overflow:
82
+ case Builtin::BI__builtin_uaddll_overflow:
83
+ return ACtx.UnsignedLongLongTy ;
84
+ case Builtin::BI__builtin_mul_overflow:
85
+ case Builtin::BI__builtin_sub_overflow:
86
+ case Builtin::BI__builtin_add_overflow:
87
+ return getOverflowBuiltinResultType (Call);
88
+ default :
89
+ assert (false && " Unknown overflow builtin" );
90
+ return ACtx.IntTy ;
91
+ }
92
+ }
93
+
31
94
class BuiltinFunctionChecker : public Checker <eval::Call> {
32
95
public:
33
96
bool evalCall (const CallEvent &Call, CheckerContext &C) const ;
97
+ void handleOverflowBuiltin (const CallEvent &Call, CheckerContext &C,
98
+ BinaryOperator::Opcode Op,
99
+ QualType ResultType) const ;
100
+ const NoteTag *createBuiltinNoOverflowNoteTag (CheckerContext &C,
101
+ bool BothFeasible, SVal Arg1,
102
+ SVal Arg2, SVal Result) const ;
103
+ const NoteTag *createBuiltinOverflowNoteTag (CheckerContext &C) const ;
104
+ std::pair<bool , bool > checkOverflow (CheckerContext &C, SVal RetVal,
105
+ QualType Res) const ;
34
106
35
107
private:
36
108
// From: clang/include/clang/Basic/Builtins.def
@@ -50,6 +122,102 @@ class BuiltinFunctionChecker : public Checker<eval::Call> {
50
122
51
123
} // namespace
52
124
125
+ const NoteTag *BuiltinFunctionChecker::createBuiltinNoOverflowNoteTag (
126
+ CheckerContext &C, bool BothFeasible, SVal Arg1, SVal Arg2,
127
+ SVal Result) const {
128
+ return C.getNoteTag ([Result, Arg1, Arg2, BothFeasible](
129
+ PathSensitiveBugReport &BR, llvm::raw_ostream &OS) {
130
+ if (!BR.isInteresting (Result))
131
+ return ;
132
+
133
+ // Propagate interestingness to input argumets if result is interesting.
134
+ BR.markInteresting (Arg1);
135
+ BR.markInteresting (Arg2);
136
+
137
+ if (BothFeasible)
138
+ OS << " Assuming no overflow" ;
139
+ });
140
+ }
141
+
142
+ const NoteTag *
143
+ BuiltinFunctionChecker::createBuiltinOverflowNoteTag (CheckerContext &C) const {
144
+ return C.getNoteTag ([](PathSensitiveBugReport &BR,
145
+ llvm::raw_ostream &OS) { OS << " Assuming overflow" ; },
146
+ /* isPrunable=*/ true );
147
+ }
148
+
149
+ std::pair<bool , bool >
150
+ BuiltinFunctionChecker::checkOverflow (CheckerContext &C, SVal RetVal,
151
+ QualType Res) const {
152
+ // Calling a builtin with a non-integer type result produces compiler error.
153
+ assert (Res->isIntegerType ());
154
+
155
+ unsigned BitWidth = C.getASTContext ().getIntWidth (Res);
156
+ bool IsUnsigned = Res->isUnsignedIntegerType ();
157
+
158
+ auto MinValType = llvm::APSInt::getMinValue (BitWidth, IsUnsigned);
159
+ auto MaxValType = llvm::APSInt::getMaxValue (BitWidth, IsUnsigned);
160
+ nonloc::ConcreteInt MinVal{MinValType};
161
+ nonloc::ConcreteInt MaxVal{MaxValType};
162
+
163
+ SValBuilder &SVB = C.getSValBuilder ();
164
+ ProgramStateRef State = C.getState ();
165
+ SVal IsLeMax = SVB.evalBinOp (State, BO_LE, RetVal, MaxVal, Res);
166
+ SVal IsGeMin = SVB.evalBinOp (State, BO_GE, RetVal, MinVal, Res);
167
+
168
+ auto [MayNotOverflow, MayOverflow] =
169
+ State->assume (IsLeMax.castAs <DefinedOrUnknownSVal>());
170
+ auto [MayNotUnderflow, MayUnderflow] =
171
+ State->assume (IsGeMin.castAs <DefinedOrUnknownSVal>());
172
+
173
+ return {MayOverflow || MayUnderflow, MayNotOverflow && MayNotUnderflow};
174
+ }
175
+
176
+ void BuiltinFunctionChecker::handleOverflowBuiltin (const CallEvent &Call,
177
+ CheckerContext &C,
178
+ BinaryOperator::Opcode Op,
179
+ QualType ResultType) const {
180
+ // Calling a builtin with an incorrect argument count produces compiler error.
181
+ assert (Call.getNumArgs () == 3 );
182
+
183
+ ProgramStateRef State = C.getState ();
184
+ SValBuilder &SVB = C.getSValBuilder ();
185
+ const Expr *CE = Call.getOriginExpr ();
186
+
187
+ SVal Arg1 = Call.getArgSVal (0 );
188
+ SVal Arg2 = Call.getArgSVal (1 );
189
+
190
+ SVal RetValMax = SVB.evalBinOp (State, Op, Arg1, Arg2,
191
+ getSufficientTypeForOverflowOp (C, ResultType));
192
+ SVal RetVal = SVB.evalBinOp (State, Op, Arg1, Arg2, ResultType);
193
+
194
+ auto [Overflow, NotOverflow] = checkOverflow (C, RetValMax, ResultType);
195
+ if (NotOverflow) {
196
+ ProgramStateRef StateNoOverflow =
197
+ State->BindExpr (CE, C.getLocationContext (), SVB.makeTruthVal (false ));
198
+
199
+ if (auto L = Call.getArgSVal (2 ).getAs <Loc>()) {
200
+ StateNoOverflow =
201
+ StateNoOverflow->bindLoc (*L, RetVal, C.getLocationContext ());
202
+
203
+ // Propagate taint if any of the argumets were tainted
204
+ if (isTainted (State, Arg1) || isTainted (State, Arg2))
205
+ StateNoOverflow = addTaint (StateNoOverflow, *L);
206
+ }
207
+
208
+ C.addTransition (
209
+ StateNoOverflow,
210
+ createBuiltinNoOverflowNoteTag (
211
+ C, /* BothFeasible=*/ NotOverflow && Overflow, Arg1, Arg2, RetVal));
212
+ }
213
+
214
+ if (Overflow) {
215
+ C.addTransition (
216
+ State->BindExpr (CE, C.getLocationContext (), SVB.makeTruthVal (true )),
217
+ createBuiltinOverflowNoteTag (C));
218
+ }
219
+ }
220
+
53
221
bool BuiltinFunctionChecker::isBuiltinLikeFunction (
54
222
const CallEvent &Call) const {
55
223
const auto *FD = llvm::dyn_cast_or_null<FunctionDecl>(Call.getDecl ());
@@ -82,10 +250,41 @@ bool BuiltinFunctionChecker::evalCall(const CallEvent &Call,
82
250
return true ;
83
251
}
84
252
85
- switch (FD->getBuiltinID ()) {
253
+ unsigned BI = FD->getBuiltinID ();
254
+
255
+ switch (BI) {
86
256
default :
87
257
return false ;
88
-
258
+ case Builtin::BI__builtin_mul_overflow:
259
+ case Builtin::BI__builtin_smul_overflow:
260
+ case Builtin::BI__builtin_smull_overflow:
261
+ case Builtin::BI__builtin_smulll_overflow:
262
+ case Builtin::BI__builtin_umul_overflow:
263
+ case Builtin::BI__builtin_umull_overflow:
264
+ case Builtin::BI__builtin_umulll_overflow:
265
+ handleOverflowBuiltin (Call, C, BO_Mul,
266
+ getOverflowBuiltinResultType (Call, C, BI));
267
+ return true ;
268
+ case Builtin::BI__builtin_sub_overflow:
269
+ case Builtin::BI__builtin_ssub_overflow:
270
+ case Builtin::BI__builtin_ssubl_overflow:
271
+ case Builtin::BI__builtin_ssubll_overflow:
272
+ case Builtin::BI__builtin_usub_overflow:
273
+ case Builtin::BI__builtin_usubl_overflow:
274
+ case Builtin::BI__builtin_usubll_overflow:
275
+ handleOverflowBuiltin (Call, C, BO_Sub,
276
+ getOverflowBuiltinResultType (Call, C, BI));
277
+ return true ;
278
+ case Builtin::BI__builtin_add_overflow:
279
+ case Builtin::BI__builtin_sadd_overflow:
280
+ case Builtin::BI__builtin_saddl_overflow:
281
+ case Builtin::BI__builtin_saddll_overflow:
282
+ case Builtin::BI__builtin_uadd_overflow:
283
+ case Builtin::BI__builtin_uaddl_overflow:
284
+ case Builtin::BI__builtin_uaddll_overflow:
285
+ handleOverflowBuiltin (Call, C, BO_Add,
286
+ getOverflowBuiltinResultType (Call, C, BI));
287
+ return true ;
89
288
case Builtin::BI__builtin_assume:
90
289
case Builtin::BI__assume: {
91
290
assert (Call.getNumArgs () > 0 );
0 commit comments