1616
1717#include " clang/Basic/Builtins.h"
1818#include " clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
19+ #include " clang/StaticAnalyzer/Checkers/Taint.h"
1920#include " clang/StaticAnalyzer/Core/Checker.h"
2021#include " clang/StaticAnalyzer/Core/CheckerManager.h"
2122#include " clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
2223#include " clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
2324#include " clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
25+ #include " clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
2426#include " clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
27+ #include " clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
2528
2629using namespace clang ;
2730using namespace ento ;
31+ using namespace taint ;
2832
2933namespace {
3034
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+
3194class BuiltinFunctionChecker : public Checker <eval::Call> {
3295public:
3396 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 ;
34106
35107private:
36108 // From: clang/include/clang/Basic/Builtins.def
@@ -50,6 +122,102 @@ class BuiltinFunctionChecker : public Checker<eval::Call> {
50122
51123} // namespace
52124
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+
53221bool BuiltinFunctionChecker::isBuiltinLikeFunction (
54222 const CallEvent &Call) const {
55223 const auto *FD = llvm::dyn_cast_or_null<FunctionDecl>(Call.getDecl ());
@@ -82,10 +250,41 @@ bool BuiltinFunctionChecker::evalCall(const CallEvent &Call,
82250 return true ;
83251 }
84252
85- switch (FD->getBuiltinID ()) {
253+ unsigned BI = FD->getBuiltinID ();
254+
255+ switch (BI) {
86256 default :
87257 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 ;
89288 case Builtin::BI__builtin_assume:
90289 case Builtin::BI__assume: {
91290 assert (Call.getNumArgs () > 0 );
0 commit comments