xref: /netbsd-src/external/apache2/llvm/dist/clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp (revision e038c9c4676b0f19b1b7dd08a940c6ed64a6d5ae)
1*e038c9c4Sjoerg //===-- IteratorModeling.cpp --------------------------------------*- C++ -*--//
2*e038c9c4Sjoerg //
3*e038c9c4Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*e038c9c4Sjoerg // See https://llvm.org/LICENSE.txt for license information.
5*e038c9c4Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*e038c9c4Sjoerg //
7*e038c9c4Sjoerg //===----------------------------------------------------------------------===//
8*e038c9c4Sjoerg //
9*e038c9c4Sjoerg // Defines a modeling-checker for modeling STL iterator-like iterators.
10*e038c9c4Sjoerg //
11*e038c9c4Sjoerg //===----------------------------------------------------------------------===//
12*e038c9c4Sjoerg //
13*e038c9c4Sjoerg // In the code, iterator can be represented as a:
14*e038c9c4Sjoerg // * type-I: typedef-ed pointer. Operations over such iterator, such as
15*e038c9c4Sjoerg //           comparisons or increments, are modeled straightforwardly by the
16*e038c9c4Sjoerg //           analyzer.
17*e038c9c4Sjoerg // * type-II: structure with its method bodies available.  Operations over such
18*e038c9c4Sjoerg //            iterator are inlined by the analyzer, and results of modeling
19*e038c9c4Sjoerg //            these operations are exposing implementation details of the
20*e038c9c4Sjoerg //            iterators, which is not necessarily helping.
21*e038c9c4Sjoerg // * type-III: completely opaque structure. Operations over such iterator are
22*e038c9c4Sjoerg //             modeled conservatively, producing conjured symbols everywhere.
23*e038c9c4Sjoerg //
24*e038c9c4Sjoerg // To handle all these types in a common way we introduce a structure called
25*e038c9c4Sjoerg // IteratorPosition which is an abstraction of the position the iterator
26*e038c9c4Sjoerg // represents using symbolic expressions. The checker handles all the
27*e038c9c4Sjoerg // operations on this structure.
28*e038c9c4Sjoerg //
29*e038c9c4Sjoerg // Additionally, depending on the circumstances, operators of types II and III
30*e038c9c4Sjoerg // can be represented as:
31*e038c9c4Sjoerg // * type-IIa, type-IIIa: conjured structure symbols - when returned by value
32*e038c9c4Sjoerg //                        from conservatively evaluated methods such as
33*e038c9c4Sjoerg //                        `.begin()`.
34*e038c9c4Sjoerg // * type-IIb, type-IIIb: memory regions of iterator-typed objects, such as
35*e038c9c4Sjoerg //                        variables or temporaries, when the iterator object is
36*e038c9c4Sjoerg //                        currently treated as an lvalue.
37*e038c9c4Sjoerg // * type-IIc, type-IIIc: compound values of iterator-typed objects, when the
38*e038c9c4Sjoerg //                        iterator object is treated as an rvalue taken of a
39*e038c9c4Sjoerg //                        particular lvalue, eg. a copy of "type-a" iterator
40*e038c9c4Sjoerg //                        object, or an iterator that existed before the
41*e038c9c4Sjoerg //                        analysis has started.
42*e038c9c4Sjoerg //
43*e038c9c4Sjoerg // To handle any of these three different representations stored in an SVal we
44*e038c9c4Sjoerg // use setter and getters functions which separate the three cases. To store
45*e038c9c4Sjoerg // them we use a pointer union of symbol and memory region.
46*e038c9c4Sjoerg //
47*e038c9c4Sjoerg // The checker works the following way: We record the begin and the
48*e038c9c4Sjoerg // past-end iterator for all containers whenever their `.begin()` and `.end()`
49*e038c9c4Sjoerg // are called. Since the Constraint Manager cannot handle such SVals we need
50*e038c9c4Sjoerg // to take over its role. We post-check equality and non-equality comparisons
51*e038c9c4Sjoerg // and record that the two sides are equal if we are in the 'equal' branch
52*e038c9c4Sjoerg // (true-branch for `==` and false-branch for `!=`).
53*e038c9c4Sjoerg //
54*e038c9c4Sjoerg // In case of type-I or type-II iterators we get a concrete integer as a result
55*e038c9c4Sjoerg // of the comparison (1 or 0) but in case of type-III we only get a Symbol. In
56*e038c9c4Sjoerg // this latter case we record the symbol and reload it in evalAssume() and do
57*e038c9c4Sjoerg // the propagation there. We also handle (maybe double) negated comparisons
58*e038c9c4Sjoerg // which are represented in the form of (x == 0 or x != 0) where x is the
59*e038c9c4Sjoerg // comparison itself.
60*e038c9c4Sjoerg //
61*e038c9c4Sjoerg // Since `SimpleConstraintManager` cannot handle complex symbolic expressions
62*e038c9c4Sjoerg // we only use expressions of the format S, S+n or S-n for iterator positions
63*e038c9c4Sjoerg // where S is a conjured symbol and n is an unsigned concrete integer. When
64*e038c9c4Sjoerg // making an assumption e.g. `S1 + n == S2 + m` we store `S1 - S2 == m - n` as
65*e038c9c4Sjoerg // a constraint which we later retrieve when doing an actual comparison.
66*e038c9c4Sjoerg 
67*e038c9c4Sjoerg #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
68*e038c9c4Sjoerg #include "clang/AST/DeclTemplate.h"
69*e038c9c4Sjoerg #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
70*e038c9c4Sjoerg #include "clang/StaticAnalyzer/Core/Checker.h"
71*e038c9c4Sjoerg #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
72*e038c9c4Sjoerg #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
73*e038c9c4Sjoerg #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
74*e038c9c4Sjoerg 
75*e038c9c4Sjoerg #include "Iterator.h"
76*e038c9c4Sjoerg 
77*e038c9c4Sjoerg #include <utility>
78*e038c9c4Sjoerg 
79*e038c9c4Sjoerg using namespace clang;
80*e038c9c4Sjoerg using namespace ento;
81*e038c9c4Sjoerg using namespace iterator;
82*e038c9c4Sjoerg 
83*e038c9c4Sjoerg namespace {
84*e038c9c4Sjoerg 
85*e038c9c4Sjoerg class IteratorModeling
86*e038c9c4Sjoerg     : public Checker<check::PostCall, check::PostStmt<UnaryOperator>,
87*e038c9c4Sjoerg                      check::PostStmt<BinaryOperator>,
88*e038c9c4Sjoerg                      check::PostStmt<MaterializeTemporaryExpr>,
89*e038c9c4Sjoerg                      check::Bind, check::LiveSymbols, check::DeadSymbols> {
90*e038c9c4Sjoerg 
91*e038c9c4Sjoerg   using AdvanceFn = void (IteratorModeling::*)(CheckerContext &, const Expr *,
92*e038c9c4Sjoerg                                                SVal, SVal, SVal) const;
93*e038c9c4Sjoerg 
94*e038c9c4Sjoerg   void handleOverloadedOperator(CheckerContext &C, const CallEvent &Call,
95*e038c9c4Sjoerg                                 OverloadedOperatorKind Op) const;
96*e038c9c4Sjoerg   void handleAdvanceLikeFunction(CheckerContext &C, const CallEvent &Call,
97*e038c9c4Sjoerg                                  const Expr *OrigExpr,
98*e038c9c4Sjoerg                                  const AdvanceFn *Handler) const;
99*e038c9c4Sjoerg 
100*e038c9c4Sjoerg   void handleComparison(CheckerContext &C, const Expr *CE, SVal RetVal,
101*e038c9c4Sjoerg                         const SVal &LVal, const SVal &RVal,
102*e038c9c4Sjoerg                         OverloadedOperatorKind Op) const;
103*e038c9c4Sjoerg   void processComparison(CheckerContext &C, ProgramStateRef State,
104*e038c9c4Sjoerg                          SymbolRef Sym1, SymbolRef Sym2, const SVal &RetVal,
105*e038c9c4Sjoerg                          OverloadedOperatorKind Op) const;
106*e038c9c4Sjoerg   void handleIncrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
107*e038c9c4Sjoerg                        bool Postfix) const;
108*e038c9c4Sjoerg   void handleDecrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
109*e038c9c4Sjoerg                        bool Postfix) const;
110*e038c9c4Sjoerg   void handleRandomIncrOrDecr(CheckerContext &C, const Expr *CE,
111*e038c9c4Sjoerg                               OverloadedOperatorKind Op, const SVal &RetVal,
112*e038c9c4Sjoerg                               const SVal &Iterator, const SVal &Amount) const;
113*e038c9c4Sjoerg   void handlePtrIncrOrDecr(CheckerContext &C, const Expr *Iterator,
114*e038c9c4Sjoerg                            OverloadedOperatorKind OK, SVal Offset) const;
115*e038c9c4Sjoerg   void handleAdvance(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter,
116*e038c9c4Sjoerg                      SVal Amount) const;
117*e038c9c4Sjoerg   void handlePrev(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter,
118*e038c9c4Sjoerg                   SVal Amount) const;
119*e038c9c4Sjoerg   void handleNext(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter,
120*e038c9c4Sjoerg                   SVal Amount) const;
121*e038c9c4Sjoerg   void assignToContainer(CheckerContext &C, const Expr *CE, const SVal &RetVal,
122*e038c9c4Sjoerg                          const MemRegion *Cont) const;
123*e038c9c4Sjoerg   bool noChangeInAdvance(CheckerContext &C, SVal Iter, const Expr *CE) const;
124*e038c9c4Sjoerg   void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
125*e038c9c4Sjoerg                   const char *Sep) const override;
126*e038c9c4Sjoerg 
127*e038c9c4Sjoerg   // std::advance, std::prev & std::next
128*e038c9c4Sjoerg   CallDescriptionMap<AdvanceFn> AdvanceLikeFunctions = {
129*e038c9c4Sjoerg       // template<class InputIt, class Distance>
130*e038c9c4Sjoerg       // void advance(InputIt& it, Distance n);
131*e038c9c4Sjoerg       {{{"std", "advance"}, 2}, &IteratorModeling::handleAdvance},
132*e038c9c4Sjoerg 
133*e038c9c4Sjoerg       // template<class BidirIt>
134*e038c9c4Sjoerg       // BidirIt prev(
135*e038c9c4Sjoerg       //   BidirIt it,
136*e038c9c4Sjoerg       //   typename std::iterator_traits<BidirIt>::difference_type n = 1);
137*e038c9c4Sjoerg       {{{"std", "prev"}, 2}, &IteratorModeling::handlePrev},
138*e038c9c4Sjoerg 
139*e038c9c4Sjoerg       // template<class ForwardIt>
140*e038c9c4Sjoerg       // ForwardIt next(
141*e038c9c4Sjoerg       //   ForwardIt it,
142*e038c9c4Sjoerg       //   typename std::iterator_traits<ForwardIt>::difference_type n = 1);
143*e038c9c4Sjoerg       {{{"std", "next"}, 2}, &IteratorModeling::handleNext},
144*e038c9c4Sjoerg   };
145*e038c9c4Sjoerg 
146*e038c9c4Sjoerg public:
147*e038c9c4Sjoerg   IteratorModeling() = default;
148*e038c9c4Sjoerg 
149*e038c9c4Sjoerg   void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
150*e038c9c4Sjoerg   void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const;
151*e038c9c4Sjoerg   void checkPostStmt(const UnaryOperator *UO, CheckerContext &C) const;
152*e038c9c4Sjoerg   void checkPostStmt(const BinaryOperator *BO, CheckerContext &C) const;
153*e038c9c4Sjoerg   void checkPostStmt(const CXXConstructExpr *CCE, CheckerContext &C) const;
154*e038c9c4Sjoerg   void checkPostStmt(const DeclStmt *DS, CheckerContext &C) const;
155*e038c9c4Sjoerg   void checkPostStmt(const MaterializeTemporaryExpr *MTE,
156*e038c9c4Sjoerg                      CheckerContext &C) const;
157*e038c9c4Sjoerg   void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
158*e038c9c4Sjoerg   void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
159*e038c9c4Sjoerg };
160*e038c9c4Sjoerg 
161*e038c9c4Sjoerg bool isSimpleComparisonOperator(OverloadedOperatorKind OK);
162*e038c9c4Sjoerg bool isSimpleComparisonOperator(BinaryOperatorKind OK);
163*e038c9c4Sjoerg ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val);
164*e038c9c4Sjoerg ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1,
165*e038c9c4Sjoerg                               SymbolRef Sym2, bool Equal);
166*e038c9c4Sjoerg bool isBoundThroughLazyCompoundVal(const Environment &Env,
167*e038c9c4Sjoerg                                    const MemRegion *Reg);
168*e038c9c4Sjoerg const ExplodedNode *findCallEnter(const ExplodedNode *Node, const Expr *Call);
169*e038c9c4Sjoerg 
170*e038c9c4Sjoerg } // namespace
171*e038c9c4Sjoerg 
checkPostCall(const CallEvent & Call,CheckerContext & C) const172*e038c9c4Sjoerg void IteratorModeling::checkPostCall(const CallEvent &Call,
173*e038c9c4Sjoerg                                      CheckerContext &C) const {
174*e038c9c4Sjoerg   // Record new iterator positions and iterator position changes
175*e038c9c4Sjoerg   const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
176*e038c9c4Sjoerg   if (!Func)
177*e038c9c4Sjoerg     return;
178*e038c9c4Sjoerg 
179*e038c9c4Sjoerg   if (Func->isOverloadedOperator()) {
180*e038c9c4Sjoerg     const auto Op = Func->getOverloadedOperator();
181*e038c9c4Sjoerg     handleOverloadedOperator(C, Call, Op);
182*e038c9c4Sjoerg     return;
183*e038c9c4Sjoerg   }
184*e038c9c4Sjoerg 
185*e038c9c4Sjoerg   const auto *OrigExpr = Call.getOriginExpr();
186*e038c9c4Sjoerg   if (!OrigExpr)
187*e038c9c4Sjoerg     return;
188*e038c9c4Sjoerg 
189*e038c9c4Sjoerg   const AdvanceFn *Handler = AdvanceLikeFunctions.lookup(Call);
190*e038c9c4Sjoerg   if (Handler) {
191*e038c9c4Sjoerg     handleAdvanceLikeFunction(C, Call, OrigExpr, Handler);
192*e038c9c4Sjoerg     return;
193*e038c9c4Sjoerg   }
194*e038c9c4Sjoerg 
195*e038c9c4Sjoerg   if (!isIteratorType(Call.getResultType()))
196*e038c9c4Sjoerg     return;
197*e038c9c4Sjoerg 
198*e038c9c4Sjoerg   auto State = C.getState();
199*e038c9c4Sjoerg 
200*e038c9c4Sjoerg   // Already bound to container?
201*e038c9c4Sjoerg   if (getIteratorPosition(State, Call.getReturnValue()))
202*e038c9c4Sjoerg     return;
203*e038c9c4Sjoerg 
204*e038c9c4Sjoerg   // Copy-like and move constructors
205*e038c9c4Sjoerg   if (isa<CXXConstructorCall>(&Call) && Call.getNumArgs() == 1) {
206*e038c9c4Sjoerg     if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(0))) {
207*e038c9c4Sjoerg       State = setIteratorPosition(State, Call.getReturnValue(), *Pos);
208*e038c9c4Sjoerg       if (cast<CXXConstructorDecl>(Func)->isMoveConstructor()) {
209*e038c9c4Sjoerg         State = removeIteratorPosition(State, Call.getArgSVal(0));
210*e038c9c4Sjoerg       }
211*e038c9c4Sjoerg       C.addTransition(State);
212*e038c9c4Sjoerg       return;
213*e038c9c4Sjoerg     }
214*e038c9c4Sjoerg   }
215*e038c9c4Sjoerg 
216*e038c9c4Sjoerg   // Assumption: if return value is an iterator which is not yet bound to a
217*e038c9c4Sjoerg   //             container, then look for the first iterator argument of the
218*e038c9c4Sjoerg   //             same type as the return value and bind the return value to
219*e038c9c4Sjoerg   //             the same container. This approach works for STL algorithms.
220*e038c9c4Sjoerg   // FIXME: Add a more conservative mode
221*e038c9c4Sjoerg   for (unsigned i = 0; i < Call.getNumArgs(); ++i) {
222*e038c9c4Sjoerg     if (isIteratorType(Call.getArgExpr(i)->getType()) &&
223*e038c9c4Sjoerg         Call.getArgExpr(i)->getType().getNonReferenceType().getDesugaredType(
224*e038c9c4Sjoerg             C.getASTContext()).getTypePtr() ==
225*e038c9c4Sjoerg         Call.getResultType().getDesugaredType(C.getASTContext()).getTypePtr()) {
226*e038c9c4Sjoerg       if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(i))) {
227*e038c9c4Sjoerg         assignToContainer(C, OrigExpr, Call.getReturnValue(),
228*e038c9c4Sjoerg                           Pos->getContainer());
229*e038c9c4Sjoerg         return;
230*e038c9c4Sjoerg       }
231*e038c9c4Sjoerg     }
232*e038c9c4Sjoerg   }
233*e038c9c4Sjoerg }
234*e038c9c4Sjoerg 
checkBind(SVal Loc,SVal Val,const Stmt * S,CheckerContext & C) const235*e038c9c4Sjoerg void IteratorModeling::checkBind(SVal Loc, SVal Val, const Stmt *S,
236*e038c9c4Sjoerg                                  CheckerContext &C) const {
237*e038c9c4Sjoerg   auto State = C.getState();
238*e038c9c4Sjoerg   const auto *Pos = getIteratorPosition(State, Val);
239*e038c9c4Sjoerg   if (Pos) {
240*e038c9c4Sjoerg     State = setIteratorPosition(State, Loc, *Pos);
241*e038c9c4Sjoerg     C.addTransition(State);
242*e038c9c4Sjoerg   } else {
243*e038c9c4Sjoerg     const auto *OldPos = getIteratorPosition(State, Loc);
244*e038c9c4Sjoerg     if (OldPos) {
245*e038c9c4Sjoerg       State = removeIteratorPosition(State, Loc);
246*e038c9c4Sjoerg       C.addTransition(State);
247*e038c9c4Sjoerg     }
248*e038c9c4Sjoerg   }
249*e038c9c4Sjoerg }
250*e038c9c4Sjoerg 
checkPostStmt(const UnaryOperator * UO,CheckerContext & C) const251*e038c9c4Sjoerg void IteratorModeling::checkPostStmt(const UnaryOperator *UO,
252*e038c9c4Sjoerg                                      CheckerContext &C) const {
253*e038c9c4Sjoerg   UnaryOperatorKind OK = UO->getOpcode();
254*e038c9c4Sjoerg   if (!isIncrementOperator(OK) && !isDecrementOperator(OK))
255*e038c9c4Sjoerg     return;
256*e038c9c4Sjoerg 
257*e038c9c4Sjoerg   auto &SVB = C.getSValBuilder();
258*e038c9c4Sjoerg   handlePtrIncrOrDecr(C, UO->getSubExpr(),
259*e038c9c4Sjoerg                       isIncrementOperator(OK) ? OO_Plus : OO_Minus,
260*e038c9c4Sjoerg                       SVB.makeArrayIndex(1));
261*e038c9c4Sjoerg }
262*e038c9c4Sjoerg 
checkPostStmt(const BinaryOperator * BO,CheckerContext & C) const263*e038c9c4Sjoerg void IteratorModeling::checkPostStmt(const BinaryOperator *BO,
264*e038c9c4Sjoerg                                      CheckerContext &C) const {
265*e038c9c4Sjoerg   const ProgramStateRef State = C.getState();
266*e038c9c4Sjoerg   const BinaryOperatorKind OK = BO->getOpcode();
267*e038c9c4Sjoerg   const Expr *const LHS = BO->getLHS();
268*e038c9c4Sjoerg   const Expr *const RHS = BO->getRHS();
269*e038c9c4Sjoerg   const SVal LVal = State->getSVal(LHS, C.getLocationContext());
270*e038c9c4Sjoerg   const SVal RVal = State->getSVal(RHS, C.getLocationContext());
271*e038c9c4Sjoerg 
272*e038c9c4Sjoerg   if (isSimpleComparisonOperator(BO->getOpcode())) {
273*e038c9c4Sjoerg     SVal Result = State->getSVal(BO, C.getLocationContext());
274*e038c9c4Sjoerg     handleComparison(C, BO, Result, LVal, RVal,
275*e038c9c4Sjoerg                      BinaryOperator::getOverloadedOperator(OK));
276*e038c9c4Sjoerg   } else if (isRandomIncrOrDecrOperator(OK)) {
277*e038c9c4Sjoerg     // In case of operator+ the iterator can be either on the LHS (eg.: it + 1),
278*e038c9c4Sjoerg     // or on the RHS (eg.: 1 + it). Both cases are modeled.
279*e038c9c4Sjoerg     const bool IsIterOnLHS = BO->getLHS()->getType()->isPointerType();
280*e038c9c4Sjoerg     const Expr *const &IterExpr = IsIterOnLHS ? LHS : RHS;
281*e038c9c4Sjoerg     const Expr *const &AmountExpr = IsIterOnLHS ? RHS : LHS;
282*e038c9c4Sjoerg 
283*e038c9c4Sjoerg     // The non-iterator side must have an integral or enumeration type.
284*e038c9c4Sjoerg     if (!AmountExpr->getType()->isIntegralOrEnumerationType())
285*e038c9c4Sjoerg       return;
286*e038c9c4Sjoerg     const SVal &AmountVal = IsIterOnLHS ? RVal : LVal;
287*e038c9c4Sjoerg     handlePtrIncrOrDecr(C, IterExpr, BinaryOperator::getOverloadedOperator(OK),
288*e038c9c4Sjoerg                         AmountVal);
289*e038c9c4Sjoerg   }
290*e038c9c4Sjoerg }
291*e038c9c4Sjoerg 
checkPostStmt(const MaterializeTemporaryExpr * MTE,CheckerContext & C) const292*e038c9c4Sjoerg void IteratorModeling::checkPostStmt(const MaterializeTemporaryExpr *MTE,
293*e038c9c4Sjoerg                                      CheckerContext &C) const {
294*e038c9c4Sjoerg   /* Transfer iterator state to temporary objects */
295*e038c9c4Sjoerg   auto State = C.getState();
296*e038c9c4Sjoerg   const auto *Pos = getIteratorPosition(State, C.getSVal(MTE->getSubExpr()));
297*e038c9c4Sjoerg   if (!Pos)
298*e038c9c4Sjoerg     return;
299*e038c9c4Sjoerg   State = setIteratorPosition(State, C.getSVal(MTE), *Pos);
300*e038c9c4Sjoerg   C.addTransition(State);
301*e038c9c4Sjoerg }
302*e038c9c4Sjoerg 
checkLiveSymbols(ProgramStateRef State,SymbolReaper & SR) const303*e038c9c4Sjoerg void IteratorModeling::checkLiveSymbols(ProgramStateRef State,
304*e038c9c4Sjoerg                                         SymbolReaper &SR) const {
305*e038c9c4Sjoerg   // Keep symbolic expressions of iterator positions alive
306*e038c9c4Sjoerg   auto RegionMap = State->get<IteratorRegionMap>();
307*e038c9c4Sjoerg   for (const auto &Reg : RegionMap) {
308*e038c9c4Sjoerg     const auto Offset = Reg.second.getOffset();
309*e038c9c4Sjoerg     for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
310*e038c9c4Sjoerg       if (isa<SymbolData>(*i))
311*e038c9c4Sjoerg         SR.markLive(*i);
312*e038c9c4Sjoerg   }
313*e038c9c4Sjoerg 
314*e038c9c4Sjoerg   auto SymbolMap = State->get<IteratorSymbolMap>();
315*e038c9c4Sjoerg   for (const auto &Sym : SymbolMap) {
316*e038c9c4Sjoerg     const auto Offset = Sym.second.getOffset();
317*e038c9c4Sjoerg     for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
318*e038c9c4Sjoerg       if (isa<SymbolData>(*i))
319*e038c9c4Sjoerg         SR.markLive(*i);
320*e038c9c4Sjoerg   }
321*e038c9c4Sjoerg 
322*e038c9c4Sjoerg }
323*e038c9c4Sjoerg 
checkDeadSymbols(SymbolReaper & SR,CheckerContext & C) const324*e038c9c4Sjoerg void IteratorModeling::checkDeadSymbols(SymbolReaper &SR,
325*e038c9c4Sjoerg                                         CheckerContext &C) const {
326*e038c9c4Sjoerg   // Cleanup
327*e038c9c4Sjoerg   auto State = C.getState();
328*e038c9c4Sjoerg 
329*e038c9c4Sjoerg   auto RegionMap = State->get<IteratorRegionMap>();
330*e038c9c4Sjoerg   for (const auto &Reg : RegionMap) {
331*e038c9c4Sjoerg     if (!SR.isLiveRegion(Reg.first)) {
332*e038c9c4Sjoerg       // The region behind the `LazyCompoundVal` is often cleaned up before
333*e038c9c4Sjoerg       // the `LazyCompoundVal` itself. If there are iterator positions keyed
334*e038c9c4Sjoerg       // by these regions their cleanup must be deferred.
335*e038c9c4Sjoerg       if (!isBoundThroughLazyCompoundVal(State->getEnvironment(), Reg.first)) {
336*e038c9c4Sjoerg         State = State->remove<IteratorRegionMap>(Reg.first);
337*e038c9c4Sjoerg       }
338*e038c9c4Sjoerg     }
339*e038c9c4Sjoerg   }
340*e038c9c4Sjoerg 
341*e038c9c4Sjoerg   auto SymbolMap = State->get<IteratorSymbolMap>();
342*e038c9c4Sjoerg   for (const auto &Sym : SymbolMap) {
343*e038c9c4Sjoerg     if (!SR.isLive(Sym.first)) {
344*e038c9c4Sjoerg       State = State->remove<IteratorSymbolMap>(Sym.first);
345*e038c9c4Sjoerg     }
346*e038c9c4Sjoerg   }
347*e038c9c4Sjoerg 
348*e038c9c4Sjoerg   C.addTransition(State);
349*e038c9c4Sjoerg }
350*e038c9c4Sjoerg 
351*e038c9c4Sjoerg void
handleOverloadedOperator(CheckerContext & C,const CallEvent & Call,OverloadedOperatorKind Op) const352*e038c9c4Sjoerg IteratorModeling::handleOverloadedOperator(CheckerContext &C,
353*e038c9c4Sjoerg                                            const CallEvent &Call,
354*e038c9c4Sjoerg                                            OverloadedOperatorKind Op) const {
355*e038c9c4Sjoerg     if (isSimpleComparisonOperator(Op)) {
356*e038c9c4Sjoerg       const auto *OrigExpr = Call.getOriginExpr();
357*e038c9c4Sjoerg       if (!OrigExpr)
358*e038c9c4Sjoerg         return;
359*e038c9c4Sjoerg 
360*e038c9c4Sjoerg       if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
361*e038c9c4Sjoerg         handleComparison(C, OrigExpr, Call.getReturnValue(),
362*e038c9c4Sjoerg                          InstCall->getCXXThisVal(), Call.getArgSVal(0), Op);
363*e038c9c4Sjoerg         return;
364*e038c9c4Sjoerg       }
365*e038c9c4Sjoerg 
366*e038c9c4Sjoerg       handleComparison(C, OrigExpr, Call.getReturnValue(), Call.getArgSVal(0),
367*e038c9c4Sjoerg                          Call.getArgSVal(1), Op);
368*e038c9c4Sjoerg       return;
369*e038c9c4Sjoerg     } else if (isRandomIncrOrDecrOperator(Op)) {
370*e038c9c4Sjoerg       const auto *OrigExpr = Call.getOriginExpr();
371*e038c9c4Sjoerg       if (!OrigExpr)
372*e038c9c4Sjoerg         return;
373*e038c9c4Sjoerg 
374*e038c9c4Sjoerg       if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
375*e038c9c4Sjoerg         if (Call.getNumArgs() >= 1 &&
376*e038c9c4Sjoerg               Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) {
377*e038c9c4Sjoerg           handleRandomIncrOrDecr(C, OrigExpr, Op, Call.getReturnValue(),
378*e038c9c4Sjoerg                                  InstCall->getCXXThisVal(), Call.getArgSVal(0));
379*e038c9c4Sjoerg           return;
380*e038c9c4Sjoerg         }
381*e038c9c4Sjoerg       } else if (Call.getNumArgs() >= 2) {
382*e038c9c4Sjoerg         const Expr *FirstArg = Call.getArgExpr(0);
383*e038c9c4Sjoerg         const Expr *SecondArg = Call.getArgExpr(1);
384*e038c9c4Sjoerg         const QualType FirstType = FirstArg->getType();
385*e038c9c4Sjoerg         const QualType SecondType = SecondArg->getType();
386*e038c9c4Sjoerg 
387*e038c9c4Sjoerg         if (FirstType->isIntegralOrEnumerationType() ||
388*e038c9c4Sjoerg             SecondType->isIntegralOrEnumerationType()) {
389*e038c9c4Sjoerg           // In case of operator+ the iterator can be either on the LHS (eg.:
390*e038c9c4Sjoerg           // it + 1), or on the RHS (eg.: 1 + it). Both cases are modeled.
391*e038c9c4Sjoerg           const bool IsIterFirst = FirstType->isStructureOrClassType();
392*e038c9c4Sjoerg           const SVal FirstArg = Call.getArgSVal(0);
393*e038c9c4Sjoerg           const SVal SecondArg = Call.getArgSVal(1);
394*e038c9c4Sjoerg           const SVal &Iterator = IsIterFirst ? FirstArg : SecondArg;
395*e038c9c4Sjoerg           const SVal &Amount = IsIterFirst ? SecondArg : FirstArg;
396*e038c9c4Sjoerg 
397*e038c9c4Sjoerg           handleRandomIncrOrDecr(C, OrigExpr, Op, Call.getReturnValue(),
398*e038c9c4Sjoerg                                  Iterator, Amount);
399*e038c9c4Sjoerg           return;
400*e038c9c4Sjoerg         }
401*e038c9c4Sjoerg       }
402*e038c9c4Sjoerg     } else if (isIncrementOperator(Op)) {
403*e038c9c4Sjoerg       if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
404*e038c9c4Sjoerg         handleIncrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
405*e038c9c4Sjoerg                         Call.getNumArgs());
406*e038c9c4Sjoerg         return;
407*e038c9c4Sjoerg       }
408*e038c9c4Sjoerg 
409*e038c9c4Sjoerg       handleIncrement(C, Call.getReturnValue(), Call.getArgSVal(0),
410*e038c9c4Sjoerg                       Call.getNumArgs());
411*e038c9c4Sjoerg       return;
412*e038c9c4Sjoerg     } else if (isDecrementOperator(Op)) {
413*e038c9c4Sjoerg       if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
414*e038c9c4Sjoerg         handleDecrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
415*e038c9c4Sjoerg                         Call.getNumArgs());
416*e038c9c4Sjoerg         return;
417*e038c9c4Sjoerg       }
418*e038c9c4Sjoerg 
419*e038c9c4Sjoerg       handleDecrement(C, Call.getReturnValue(), Call.getArgSVal(0),
420*e038c9c4Sjoerg                         Call.getNumArgs());
421*e038c9c4Sjoerg       return;
422*e038c9c4Sjoerg     }
423*e038c9c4Sjoerg }
424*e038c9c4Sjoerg 
425*e038c9c4Sjoerg void
handleAdvanceLikeFunction(CheckerContext & C,const CallEvent & Call,const Expr * OrigExpr,const AdvanceFn * Handler) const426*e038c9c4Sjoerg IteratorModeling::handleAdvanceLikeFunction(CheckerContext &C,
427*e038c9c4Sjoerg                                             const CallEvent &Call,
428*e038c9c4Sjoerg                                             const Expr *OrigExpr,
429*e038c9c4Sjoerg                                             const AdvanceFn *Handler) const {
430*e038c9c4Sjoerg   if (!C.wasInlined) {
431*e038c9c4Sjoerg     (this->**Handler)(C, OrigExpr, Call.getReturnValue(),
432*e038c9c4Sjoerg                       Call.getArgSVal(0), Call.getArgSVal(1));
433*e038c9c4Sjoerg     return;
434*e038c9c4Sjoerg   }
435*e038c9c4Sjoerg 
436*e038c9c4Sjoerg   // If std::advance() was inlined, but a non-standard function it calls inside
437*e038c9c4Sjoerg   // was not, then we have to model it explicitly
438*e038c9c4Sjoerg   const auto *IdInfo = cast<FunctionDecl>(Call.getDecl())->getIdentifier();
439*e038c9c4Sjoerg   if (IdInfo) {
440*e038c9c4Sjoerg     if (IdInfo->getName() == "advance") {
441*e038c9c4Sjoerg       if (noChangeInAdvance(C, Call.getArgSVal(0), OrigExpr)) {
442*e038c9c4Sjoerg         (this->**Handler)(C, OrigExpr, Call.getReturnValue(),
443*e038c9c4Sjoerg                           Call.getArgSVal(0), Call.getArgSVal(1));
444*e038c9c4Sjoerg       }
445*e038c9c4Sjoerg     }
446*e038c9c4Sjoerg   }
447*e038c9c4Sjoerg }
448*e038c9c4Sjoerg 
handleComparison(CheckerContext & C,const Expr * CE,SVal RetVal,const SVal & LVal,const SVal & RVal,OverloadedOperatorKind Op) const449*e038c9c4Sjoerg void IteratorModeling::handleComparison(CheckerContext &C, const Expr *CE,
450*e038c9c4Sjoerg                                        SVal RetVal, const SVal &LVal,
451*e038c9c4Sjoerg                                        const SVal &RVal,
452*e038c9c4Sjoerg                                        OverloadedOperatorKind Op) const {
453*e038c9c4Sjoerg   // Record the operands and the operator of the comparison for the next
454*e038c9c4Sjoerg   // evalAssume, if the result is a symbolic expression. If it is a concrete
455*e038c9c4Sjoerg   // value (only one branch is possible), then transfer the state between
456*e038c9c4Sjoerg   // the operands according to the operator and the result
457*e038c9c4Sjoerg    auto State = C.getState();
458*e038c9c4Sjoerg   const auto *LPos = getIteratorPosition(State, LVal);
459*e038c9c4Sjoerg   const auto *RPos = getIteratorPosition(State, RVal);
460*e038c9c4Sjoerg   const MemRegion *Cont = nullptr;
461*e038c9c4Sjoerg   if (LPos) {
462*e038c9c4Sjoerg     Cont = LPos->getContainer();
463*e038c9c4Sjoerg   } else if (RPos) {
464*e038c9c4Sjoerg     Cont = RPos->getContainer();
465*e038c9c4Sjoerg   }
466*e038c9c4Sjoerg   if (!Cont)
467*e038c9c4Sjoerg     return;
468*e038c9c4Sjoerg 
469*e038c9c4Sjoerg   // At least one of the iterators has recorded positions. If one of them does
470*e038c9c4Sjoerg   // not then create a new symbol for the offset.
471*e038c9c4Sjoerg   SymbolRef Sym;
472*e038c9c4Sjoerg   if (!LPos || !RPos) {
473*e038c9c4Sjoerg     auto &SymMgr = C.getSymbolManager();
474*e038c9c4Sjoerg     Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
475*e038c9c4Sjoerg                                C.getASTContext().LongTy, C.blockCount());
476*e038c9c4Sjoerg     State = assumeNoOverflow(State, Sym, 4);
477*e038c9c4Sjoerg   }
478*e038c9c4Sjoerg 
479*e038c9c4Sjoerg   if (!LPos) {
480*e038c9c4Sjoerg     State = setIteratorPosition(State, LVal,
481*e038c9c4Sjoerg                                 IteratorPosition::getPosition(Cont, Sym));
482*e038c9c4Sjoerg     LPos = getIteratorPosition(State, LVal);
483*e038c9c4Sjoerg   } else if (!RPos) {
484*e038c9c4Sjoerg     State = setIteratorPosition(State, RVal,
485*e038c9c4Sjoerg                                 IteratorPosition::getPosition(Cont, Sym));
486*e038c9c4Sjoerg     RPos = getIteratorPosition(State, RVal);
487*e038c9c4Sjoerg   }
488*e038c9c4Sjoerg 
489*e038c9c4Sjoerg   // If the value for which we just tried to set a new iterator position is
490*e038c9c4Sjoerg   // an `SVal`for which no iterator position can be set then the setting was
491*e038c9c4Sjoerg   // unsuccessful. We cannot handle the comparison in this case.
492*e038c9c4Sjoerg   if (!LPos || !RPos)
493*e038c9c4Sjoerg     return;
494*e038c9c4Sjoerg 
495*e038c9c4Sjoerg   // We cannot make assumptions on `UnknownVal`. Let us conjure a symbol
496*e038c9c4Sjoerg   // instead.
497*e038c9c4Sjoerg   if (RetVal.isUnknown()) {
498*e038c9c4Sjoerg     auto &SymMgr = C.getSymbolManager();
499*e038c9c4Sjoerg     auto *LCtx = C.getLocationContext();
500*e038c9c4Sjoerg     RetVal = nonloc::SymbolVal(SymMgr.conjureSymbol(
501*e038c9c4Sjoerg         CE, LCtx, C.getASTContext().BoolTy, C.blockCount()));
502*e038c9c4Sjoerg     State = State->BindExpr(CE, LCtx, RetVal);
503*e038c9c4Sjoerg   }
504*e038c9c4Sjoerg 
505*e038c9c4Sjoerg   processComparison(C, State, LPos->getOffset(), RPos->getOffset(), RetVal, Op);
506*e038c9c4Sjoerg }
507*e038c9c4Sjoerg 
processComparison(CheckerContext & C,ProgramStateRef State,SymbolRef Sym1,SymbolRef Sym2,const SVal & RetVal,OverloadedOperatorKind Op) const508*e038c9c4Sjoerg void IteratorModeling::processComparison(CheckerContext &C,
509*e038c9c4Sjoerg                                          ProgramStateRef State, SymbolRef Sym1,
510*e038c9c4Sjoerg                                          SymbolRef Sym2, const SVal &RetVal,
511*e038c9c4Sjoerg                                          OverloadedOperatorKind Op) const {
512*e038c9c4Sjoerg   if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) {
513*e038c9c4Sjoerg     if ((State = relateSymbols(State, Sym1, Sym2,
514*e038c9c4Sjoerg                               (Op == OO_EqualEqual) ==
515*e038c9c4Sjoerg                                (TruthVal->getValue() != 0)))) {
516*e038c9c4Sjoerg       C.addTransition(State);
517*e038c9c4Sjoerg     } else {
518*e038c9c4Sjoerg       C.generateSink(State, C.getPredecessor());
519*e038c9c4Sjoerg     }
520*e038c9c4Sjoerg     return;
521*e038c9c4Sjoerg   }
522*e038c9c4Sjoerg 
523*e038c9c4Sjoerg   const auto ConditionVal = RetVal.getAs<DefinedSVal>();
524*e038c9c4Sjoerg   if (!ConditionVal)
525*e038c9c4Sjoerg     return;
526*e038c9c4Sjoerg 
527*e038c9c4Sjoerg   if (auto StateTrue = relateSymbols(State, Sym1, Sym2, Op == OO_EqualEqual)) {
528*e038c9c4Sjoerg     StateTrue = StateTrue->assume(*ConditionVal, true);
529*e038c9c4Sjoerg     C.addTransition(StateTrue);
530*e038c9c4Sjoerg   }
531*e038c9c4Sjoerg 
532*e038c9c4Sjoerg   if (auto StateFalse = relateSymbols(State, Sym1, Sym2, Op != OO_EqualEqual)) {
533*e038c9c4Sjoerg     StateFalse = StateFalse->assume(*ConditionVal, false);
534*e038c9c4Sjoerg     C.addTransition(StateFalse);
535*e038c9c4Sjoerg   }
536*e038c9c4Sjoerg }
537*e038c9c4Sjoerg 
handleIncrement(CheckerContext & C,const SVal & RetVal,const SVal & Iter,bool Postfix) const538*e038c9c4Sjoerg void IteratorModeling::handleIncrement(CheckerContext &C, const SVal &RetVal,
539*e038c9c4Sjoerg                                        const SVal &Iter, bool Postfix) const {
540*e038c9c4Sjoerg   // Increment the symbolic expressions which represents the position of the
541*e038c9c4Sjoerg   // iterator
542*e038c9c4Sjoerg   auto State = C.getState();
543*e038c9c4Sjoerg   auto &BVF = C.getSymbolManager().getBasicVals();
544*e038c9c4Sjoerg 
545*e038c9c4Sjoerg   const auto *Pos = getIteratorPosition(State, Iter);
546*e038c9c4Sjoerg   if (!Pos)
547*e038c9c4Sjoerg     return;
548*e038c9c4Sjoerg 
549*e038c9c4Sjoerg   auto NewState =
550*e038c9c4Sjoerg     advancePosition(State, Iter, OO_Plus,
551*e038c9c4Sjoerg                     nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
552*e038c9c4Sjoerg   assert(NewState &&
553*e038c9c4Sjoerg          "Advancing position by concrete int should always be successful");
554*e038c9c4Sjoerg 
555*e038c9c4Sjoerg   const auto *NewPos = getIteratorPosition(NewState, Iter);
556*e038c9c4Sjoerg   assert(NewPos &&
557*e038c9c4Sjoerg          "Iterator should have position after successful advancement");
558*e038c9c4Sjoerg 
559*e038c9c4Sjoerg   State = setIteratorPosition(State, Iter, *NewPos);
560*e038c9c4Sjoerg   State = setIteratorPosition(State, RetVal, Postfix ? *Pos : *NewPos);
561*e038c9c4Sjoerg   C.addTransition(State);
562*e038c9c4Sjoerg }
563*e038c9c4Sjoerg 
handleDecrement(CheckerContext & C,const SVal & RetVal,const SVal & Iter,bool Postfix) const564*e038c9c4Sjoerg void IteratorModeling::handleDecrement(CheckerContext &C, const SVal &RetVal,
565*e038c9c4Sjoerg                                        const SVal &Iter, bool Postfix) const {
566*e038c9c4Sjoerg   // Decrement the symbolic expressions which represents the position of the
567*e038c9c4Sjoerg   // iterator
568*e038c9c4Sjoerg   auto State = C.getState();
569*e038c9c4Sjoerg   auto &BVF = C.getSymbolManager().getBasicVals();
570*e038c9c4Sjoerg 
571*e038c9c4Sjoerg   const auto *Pos = getIteratorPosition(State, Iter);
572*e038c9c4Sjoerg   if (!Pos)
573*e038c9c4Sjoerg     return;
574*e038c9c4Sjoerg 
575*e038c9c4Sjoerg   auto NewState =
576*e038c9c4Sjoerg     advancePosition(State, Iter, OO_Minus,
577*e038c9c4Sjoerg                     nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
578*e038c9c4Sjoerg   assert(NewState &&
579*e038c9c4Sjoerg          "Advancing position by concrete int should always be successful");
580*e038c9c4Sjoerg 
581*e038c9c4Sjoerg   const auto *NewPos = getIteratorPosition(NewState, Iter);
582*e038c9c4Sjoerg   assert(NewPos &&
583*e038c9c4Sjoerg          "Iterator should have position after successful advancement");
584*e038c9c4Sjoerg 
585*e038c9c4Sjoerg   State = setIteratorPosition(State, Iter, *NewPos);
586*e038c9c4Sjoerg   State = setIteratorPosition(State, RetVal, Postfix ? *Pos : *NewPos);
587*e038c9c4Sjoerg   C.addTransition(State);
588*e038c9c4Sjoerg }
589*e038c9c4Sjoerg 
handleRandomIncrOrDecr(CheckerContext & C,const Expr * CE,OverloadedOperatorKind Op,const SVal & RetVal,const SVal & Iterator,const SVal & Amount) const590*e038c9c4Sjoerg void IteratorModeling::handleRandomIncrOrDecr(CheckerContext &C, const Expr *CE,
591*e038c9c4Sjoerg                                               OverloadedOperatorKind Op,
592*e038c9c4Sjoerg                                               const SVal &RetVal,
593*e038c9c4Sjoerg                                               const SVal &Iterator,
594*e038c9c4Sjoerg                                               const SVal &Amount) const {
595*e038c9c4Sjoerg   // Increment or decrement the symbolic expressions which represents the
596*e038c9c4Sjoerg   // position of the iterator
597*e038c9c4Sjoerg   auto State = C.getState();
598*e038c9c4Sjoerg 
599*e038c9c4Sjoerg   const auto *Pos = getIteratorPosition(State, Iterator);
600*e038c9c4Sjoerg   if (!Pos)
601*e038c9c4Sjoerg     return;
602*e038c9c4Sjoerg 
603*e038c9c4Sjoerg   const auto *Value = &Amount;
604*e038c9c4Sjoerg   SVal Val;
605*e038c9c4Sjoerg   if (auto LocAmount = Amount.getAs<Loc>()) {
606*e038c9c4Sjoerg     Val = State->getRawSVal(*LocAmount);
607*e038c9c4Sjoerg     Value = &Val;
608*e038c9c4Sjoerg   }
609*e038c9c4Sjoerg 
610*e038c9c4Sjoerg   const auto &TgtVal =
611*e038c9c4Sjoerg       (Op == OO_PlusEqual || Op == OO_MinusEqual) ? Iterator : RetVal;
612*e038c9c4Sjoerg 
613*e038c9c4Sjoerg   // `AdvancedState` is a state where the position of `LHS` is advanced. We
614*e038c9c4Sjoerg   // only need this state to retrieve the new position, but we do not want
615*e038c9c4Sjoerg   // to change the position of `LHS` (in every case).
616*e038c9c4Sjoerg   auto AdvancedState = advancePosition(State, Iterator, Op, *Value);
617*e038c9c4Sjoerg   if (AdvancedState) {
618*e038c9c4Sjoerg     const auto *NewPos = getIteratorPosition(AdvancedState, Iterator);
619*e038c9c4Sjoerg     assert(NewPos &&
620*e038c9c4Sjoerg            "Iterator should have position after successful advancement");
621*e038c9c4Sjoerg 
622*e038c9c4Sjoerg     State = setIteratorPosition(State, TgtVal, *NewPos);
623*e038c9c4Sjoerg     C.addTransition(State);
624*e038c9c4Sjoerg   } else {
625*e038c9c4Sjoerg     assignToContainer(C, CE, TgtVal, Pos->getContainer());
626*e038c9c4Sjoerg   }
627*e038c9c4Sjoerg }
628*e038c9c4Sjoerg 
handlePtrIncrOrDecr(CheckerContext & C,const Expr * Iterator,OverloadedOperatorKind OK,SVal Offset) const629*e038c9c4Sjoerg void IteratorModeling::handlePtrIncrOrDecr(CheckerContext &C,
630*e038c9c4Sjoerg                                            const Expr *Iterator,
631*e038c9c4Sjoerg                                            OverloadedOperatorKind OK,
632*e038c9c4Sjoerg                                            SVal Offset) const {
633*e038c9c4Sjoerg   if (!Offset.getAs<DefinedSVal>())
634*e038c9c4Sjoerg     return;
635*e038c9c4Sjoerg 
636*e038c9c4Sjoerg   QualType PtrType = Iterator->getType();
637*e038c9c4Sjoerg   if (!PtrType->isPointerType())
638*e038c9c4Sjoerg     return;
639*e038c9c4Sjoerg   QualType ElementType = PtrType->getPointeeType();
640*e038c9c4Sjoerg 
641*e038c9c4Sjoerg   ProgramStateRef State = C.getState();
642*e038c9c4Sjoerg   SVal OldVal = State->getSVal(Iterator, C.getLocationContext());
643*e038c9c4Sjoerg 
644*e038c9c4Sjoerg   const IteratorPosition *OldPos = getIteratorPosition(State, OldVal);
645*e038c9c4Sjoerg   if (!OldPos)
646*e038c9c4Sjoerg     return;
647*e038c9c4Sjoerg 
648*e038c9c4Sjoerg   SVal NewVal;
649*e038c9c4Sjoerg   if (OK == OO_Plus || OK == OO_PlusEqual) {
650*e038c9c4Sjoerg     NewVal = State->getLValue(ElementType, Offset, OldVal);
651*e038c9c4Sjoerg   } else {
652*e038c9c4Sjoerg     auto &SVB = C.getSValBuilder();
653*e038c9c4Sjoerg     SVal NegatedOffset = SVB.evalMinus(Offset.castAs<NonLoc>());
654*e038c9c4Sjoerg     NewVal = State->getLValue(ElementType, NegatedOffset, OldVal);
655*e038c9c4Sjoerg   }
656*e038c9c4Sjoerg 
657*e038c9c4Sjoerg   // `AdvancedState` is a state where the position of `Old` is advanced. We
658*e038c9c4Sjoerg   // only need this state to retrieve the new position, but we do not want
659*e038c9c4Sjoerg   // ever to change the position of `OldVal`.
660*e038c9c4Sjoerg   auto AdvancedState = advancePosition(State, OldVal, OK, Offset);
661*e038c9c4Sjoerg   if (AdvancedState) {
662*e038c9c4Sjoerg     const IteratorPosition *NewPos = getIteratorPosition(AdvancedState, OldVal);
663*e038c9c4Sjoerg     assert(NewPos &&
664*e038c9c4Sjoerg            "Iterator should have position after successful advancement");
665*e038c9c4Sjoerg 
666*e038c9c4Sjoerg     ProgramStateRef NewState = setIteratorPosition(State, NewVal, *NewPos);
667*e038c9c4Sjoerg     C.addTransition(NewState);
668*e038c9c4Sjoerg   } else {
669*e038c9c4Sjoerg     assignToContainer(C, Iterator, NewVal, OldPos->getContainer());
670*e038c9c4Sjoerg   }
671*e038c9c4Sjoerg }
672*e038c9c4Sjoerg 
handleAdvance(CheckerContext & C,const Expr * CE,SVal RetVal,SVal Iter,SVal Amount) const673*e038c9c4Sjoerg void IteratorModeling::handleAdvance(CheckerContext &C, const Expr *CE,
674*e038c9c4Sjoerg                                      SVal RetVal, SVal Iter,
675*e038c9c4Sjoerg                                      SVal Amount) const {
676*e038c9c4Sjoerg   handleRandomIncrOrDecr(C, CE, OO_PlusEqual, RetVal, Iter, Amount);
677*e038c9c4Sjoerg }
678*e038c9c4Sjoerg 
handlePrev(CheckerContext & C,const Expr * CE,SVal RetVal,SVal Iter,SVal Amount) const679*e038c9c4Sjoerg void IteratorModeling::handlePrev(CheckerContext &C, const Expr *CE,
680*e038c9c4Sjoerg                                   SVal RetVal, SVal Iter, SVal Amount) const {
681*e038c9c4Sjoerg   handleRandomIncrOrDecr(C, CE, OO_Minus, RetVal, Iter, Amount);
682*e038c9c4Sjoerg }
683*e038c9c4Sjoerg 
handleNext(CheckerContext & C,const Expr * CE,SVal RetVal,SVal Iter,SVal Amount) const684*e038c9c4Sjoerg void IteratorModeling::handleNext(CheckerContext &C, const Expr *CE,
685*e038c9c4Sjoerg                                   SVal RetVal, SVal Iter, SVal Amount) const {
686*e038c9c4Sjoerg   handleRandomIncrOrDecr(C, CE, OO_Plus, RetVal, Iter, Amount);
687*e038c9c4Sjoerg }
688*e038c9c4Sjoerg 
assignToContainer(CheckerContext & C,const Expr * CE,const SVal & RetVal,const MemRegion * Cont) const689*e038c9c4Sjoerg void IteratorModeling::assignToContainer(CheckerContext &C, const Expr *CE,
690*e038c9c4Sjoerg                                          const SVal &RetVal,
691*e038c9c4Sjoerg                                          const MemRegion *Cont) const {
692*e038c9c4Sjoerg   Cont = Cont->getMostDerivedObjectRegion();
693*e038c9c4Sjoerg 
694*e038c9c4Sjoerg   auto State = C.getState();
695*e038c9c4Sjoerg   const auto *LCtx = C.getLocationContext();
696*e038c9c4Sjoerg   State = createIteratorPosition(State, RetVal, Cont, CE, LCtx, C.blockCount());
697*e038c9c4Sjoerg 
698*e038c9c4Sjoerg   C.addTransition(State);
699*e038c9c4Sjoerg }
700*e038c9c4Sjoerg 
noChangeInAdvance(CheckerContext & C,SVal Iter,const Expr * CE) const701*e038c9c4Sjoerg bool IteratorModeling::noChangeInAdvance(CheckerContext &C, SVal Iter,
702*e038c9c4Sjoerg                                          const Expr *CE) const {
703*e038c9c4Sjoerg   // Compare the iterator position before and after the call. (To be called
704*e038c9c4Sjoerg   // from `checkPostCall()`.)
705*e038c9c4Sjoerg   const auto StateAfter = C.getState();
706*e038c9c4Sjoerg 
707*e038c9c4Sjoerg   const auto *PosAfter = getIteratorPosition(StateAfter, Iter);
708*e038c9c4Sjoerg   // If we have no position after the call of `std::advance`, then we are not
709*e038c9c4Sjoerg   // interested. (Modeling of an inlined `std::advance()` should not remove the
710*e038c9c4Sjoerg   // position in any case.)
711*e038c9c4Sjoerg   if (!PosAfter)
712*e038c9c4Sjoerg     return false;
713*e038c9c4Sjoerg 
714*e038c9c4Sjoerg   const ExplodedNode *N = findCallEnter(C.getPredecessor(), CE);
715*e038c9c4Sjoerg   assert(N && "Any call should have a `CallEnter` node.");
716*e038c9c4Sjoerg 
717*e038c9c4Sjoerg   const auto StateBefore = N->getState();
718*e038c9c4Sjoerg   const auto *PosBefore = getIteratorPosition(StateBefore, Iter);
719*e038c9c4Sjoerg   // FIXME: `std::advance()` should not create a new iterator position but
720*e038c9c4Sjoerg   //        change existing ones. However, in case of iterators implemented as
721*e038c9c4Sjoerg   //        pointers the handling of parameters in `std::advance()`-like
722*e038c9c4Sjoerg   //        functions is still incomplete which may result in cases where
723*e038c9c4Sjoerg   //        the new position is assigned to the wrong pointer. This causes
724*e038c9c4Sjoerg   //        crash if we use an assertion here.
725*e038c9c4Sjoerg   if (!PosBefore)
726*e038c9c4Sjoerg     return false;
727*e038c9c4Sjoerg 
728*e038c9c4Sjoerg   return PosBefore->getOffset() == PosAfter->getOffset();
729*e038c9c4Sjoerg }
730*e038c9c4Sjoerg 
printState(raw_ostream & Out,ProgramStateRef State,const char * NL,const char * Sep) const731*e038c9c4Sjoerg void IteratorModeling::printState(raw_ostream &Out, ProgramStateRef State,
732*e038c9c4Sjoerg                                   const char *NL, const char *Sep) const {
733*e038c9c4Sjoerg   auto SymbolMap = State->get<IteratorSymbolMap>();
734*e038c9c4Sjoerg   auto RegionMap = State->get<IteratorRegionMap>();
735*e038c9c4Sjoerg   // Use a counter to add newlines before every line except the first one.
736*e038c9c4Sjoerg   unsigned Count = 0;
737*e038c9c4Sjoerg 
738*e038c9c4Sjoerg   if (!SymbolMap.isEmpty() || !RegionMap.isEmpty()) {
739*e038c9c4Sjoerg     Out << Sep << "Iterator Positions :" << NL;
740*e038c9c4Sjoerg     for (const auto &Sym : SymbolMap) {
741*e038c9c4Sjoerg       if (Count++)
742*e038c9c4Sjoerg         Out << NL;
743*e038c9c4Sjoerg 
744*e038c9c4Sjoerg       Sym.first->dumpToStream(Out);
745*e038c9c4Sjoerg       Out << " : ";
746*e038c9c4Sjoerg       const auto Pos = Sym.second;
747*e038c9c4Sjoerg       Out << (Pos.isValid() ? "Valid" : "Invalid") << " ; Container == ";
748*e038c9c4Sjoerg       Pos.getContainer()->dumpToStream(Out);
749*e038c9c4Sjoerg       Out<<" ; Offset == ";
750*e038c9c4Sjoerg       Pos.getOffset()->dumpToStream(Out);
751*e038c9c4Sjoerg     }
752*e038c9c4Sjoerg 
753*e038c9c4Sjoerg     for (const auto &Reg : RegionMap) {
754*e038c9c4Sjoerg       if (Count++)
755*e038c9c4Sjoerg         Out << NL;
756*e038c9c4Sjoerg 
757*e038c9c4Sjoerg       Reg.first->dumpToStream(Out);
758*e038c9c4Sjoerg       Out << " : ";
759*e038c9c4Sjoerg       const auto Pos = Reg.second;
760*e038c9c4Sjoerg       Out << (Pos.isValid() ? "Valid" : "Invalid") << " ; Container == ";
761*e038c9c4Sjoerg       Pos.getContainer()->dumpToStream(Out);
762*e038c9c4Sjoerg       Out<<" ; Offset == ";
763*e038c9c4Sjoerg       Pos.getOffset()->dumpToStream(Out);
764*e038c9c4Sjoerg     }
765*e038c9c4Sjoerg   }
766*e038c9c4Sjoerg }
767*e038c9c4Sjoerg 
768*e038c9c4Sjoerg namespace {
769*e038c9c4Sjoerg 
isSimpleComparisonOperator(OverloadedOperatorKind OK)770*e038c9c4Sjoerg bool isSimpleComparisonOperator(OverloadedOperatorKind OK) {
771*e038c9c4Sjoerg   return OK == OO_EqualEqual || OK == OO_ExclaimEqual;
772*e038c9c4Sjoerg }
773*e038c9c4Sjoerg 
isSimpleComparisonOperator(BinaryOperatorKind OK)774*e038c9c4Sjoerg bool isSimpleComparisonOperator(BinaryOperatorKind OK) {
775*e038c9c4Sjoerg   return OK == BO_EQ || OK == BO_NE;
776*e038c9c4Sjoerg }
777*e038c9c4Sjoerg 
removeIteratorPosition(ProgramStateRef State,const SVal & Val)778*e038c9c4Sjoerg ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val) {
779*e038c9c4Sjoerg   if (auto Reg = Val.getAsRegion()) {
780*e038c9c4Sjoerg     Reg = Reg->getMostDerivedObjectRegion();
781*e038c9c4Sjoerg     return State->remove<IteratorRegionMap>(Reg);
782*e038c9c4Sjoerg   } else if (const auto Sym = Val.getAsSymbol()) {
783*e038c9c4Sjoerg     return State->remove<IteratorSymbolMap>(Sym);
784*e038c9c4Sjoerg   } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
785*e038c9c4Sjoerg     return State->remove<IteratorRegionMap>(LCVal->getRegion());
786*e038c9c4Sjoerg   }
787*e038c9c4Sjoerg   return nullptr;
788*e038c9c4Sjoerg }
789*e038c9c4Sjoerg 
relateSymbols(ProgramStateRef State,SymbolRef Sym1,SymbolRef Sym2,bool Equal)790*e038c9c4Sjoerg ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1,
791*e038c9c4Sjoerg                               SymbolRef Sym2, bool Equal) {
792*e038c9c4Sjoerg   auto &SVB = State->getStateManager().getSValBuilder();
793*e038c9c4Sjoerg 
794*e038c9c4Sjoerg   // FIXME: This code should be reworked as follows:
795*e038c9c4Sjoerg   // 1. Subtract the operands using evalBinOp().
796*e038c9c4Sjoerg   // 2. Assume that the result doesn't overflow.
797*e038c9c4Sjoerg   // 3. Compare the result to 0.
798*e038c9c4Sjoerg   // 4. Assume the result of the comparison.
799*e038c9c4Sjoerg   const auto comparison =
800*e038c9c4Sjoerg     SVB.evalBinOp(State, BO_EQ, nonloc::SymbolVal(Sym1),
801*e038c9c4Sjoerg                   nonloc::SymbolVal(Sym2), SVB.getConditionType());
802*e038c9c4Sjoerg 
803*e038c9c4Sjoerg   assert(comparison.getAs<DefinedSVal>() &&
804*e038c9c4Sjoerg     "Symbol comparison must be a `DefinedSVal`");
805*e038c9c4Sjoerg 
806*e038c9c4Sjoerg   auto NewState = State->assume(comparison.castAs<DefinedSVal>(), Equal);
807*e038c9c4Sjoerg   if (!NewState)
808*e038c9c4Sjoerg     return nullptr;
809*e038c9c4Sjoerg 
810*e038c9c4Sjoerg   if (const auto CompSym = comparison.getAsSymbol()) {
811*e038c9c4Sjoerg     assert(isa<SymIntExpr>(CompSym) &&
812*e038c9c4Sjoerg            "Symbol comparison must be a `SymIntExpr`");
813*e038c9c4Sjoerg     assert(BinaryOperator::isComparisonOp(
814*e038c9c4Sjoerg                cast<SymIntExpr>(CompSym)->getOpcode()) &&
815*e038c9c4Sjoerg            "Symbol comparison must be a comparison");
816*e038c9c4Sjoerg     return assumeNoOverflow(NewState, cast<SymIntExpr>(CompSym)->getLHS(), 2);
817*e038c9c4Sjoerg   }
818*e038c9c4Sjoerg 
819*e038c9c4Sjoerg   return NewState;
820*e038c9c4Sjoerg }
821*e038c9c4Sjoerg 
isBoundThroughLazyCompoundVal(const Environment & Env,const MemRegion * Reg)822*e038c9c4Sjoerg bool isBoundThroughLazyCompoundVal(const Environment &Env,
823*e038c9c4Sjoerg                                    const MemRegion *Reg) {
824*e038c9c4Sjoerg   for (const auto &Binding : Env) {
825*e038c9c4Sjoerg     if (const auto LCVal = Binding.second.getAs<nonloc::LazyCompoundVal>()) {
826*e038c9c4Sjoerg       if (LCVal->getRegion() == Reg)
827*e038c9c4Sjoerg         return true;
828*e038c9c4Sjoerg     }
829*e038c9c4Sjoerg   }
830*e038c9c4Sjoerg 
831*e038c9c4Sjoerg   return false;
832*e038c9c4Sjoerg }
833*e038c9c4Sjoerg 
findCallEnter(const ExplodedNode * Node,const Expr * Call)834*e038c9c4Sjoerg const ExplodedNode *findCallEnter(const ExplodedNode *Node, const Expr *Call) {
835*e038c9c4Sjoerg   while (Node) {
836*e038c9c4Sjoerg     ProgramPoint PP = Node->getLocation();
837*e038c9c4Sjoerg     if (auto Enter = PP.getAs<CallEnter>()) {
838*e038c9c4Sjoerg       if (Enter->getCallExpr() == Call)
839*e038c9c4Sjoerg         break;
840*e038c9c4Sjoerg     }
841*e038c9c4Sjoerg 
842*e038c9c4Sjoerg     Node = Node->getFirstPred();
843*e038c9c4Sjoerg   }
844*e038c9c4Sjoerg 
845*e038c9c4Sjoerg   return Node;
846*e038c9c4Sjoerg }
847*e038c9c4Sjoerg 
848*e038c9c4Sjoerg } // namespace
849*e038c9c4Sjoerg 
registerIteratorModeling(CheckerManager & mgr)850*e038c9c4Sjoerg void ento::registerIteratorModeling(CheckerManager &mgr) {
851*e038c9c4Sjoerg   mgr.registerChecker<IteratorModeling>();
852*e038c9c4Sjoerg }
853*e038c9c4Sjoerg 
shouldRegisterIteratorModeling(const CheckerManager & mgr)854*e038c9c4Sjoerg bool ento::shouldRegisterIteratorModeling(const CheckerManager &mgr) {
855*e038c9c4Sjoerg   return true;
856*e038c9c4Sjoerg }
857