1afb13afcSAdam Balogh //===-- IteratorModeling.cpp --------------------------------------*- C++ -*--//
2afb13afcSAdam Balogh //
3afb13afcSAdam Balogh // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4afb13afcSAdam Balogh // See https://llvm.org/LICENSE.txt for license information.
5afb13afcSAdam Balogh // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6afb13afcSAdam Balogh //
7afb13afcSAdam Balogh //===----------------------------------------------------------------------===//
8afb13afcSAdam Balogh //
99a08a3faSAdam Balogh // Defines a modeling-checker for modeling STL iterator-like iterators.
10afb13afcSAdam Balogh //
11afb13afcSAdam Balogh //===----------------------------------------------------------------------===//
12afb13afcSAdam Balogh //
13afb13afcSAdam Balogh // In the code, iterator can be represented as a:
14afb13afcSAdam Balogh // * type-I: typedef-ed pointer. Operations over such iterator, such as
15afb13afcSAdam Balogh //           comparisons or increments, are modeled straightforwardly by the
16afb13afcSAdam Balogh //           analyzer.
17afb13afcSAdam Balogh // * type-II: structure with its method bodies available.  Operations over such
18afb13afcSAdam Balogh //            iterator are inlined by the analyzer, and results of modeling
19afb13afcSAdam Balogh //            these operations are exposing implementation details of the
20afb13afcSAdam Balogh //            iterators, which is not necessarily helping.
21afb13afcSAdam Balogh // * type-III: completely opaque structure. Operations over such iterator are
22afb13afcSAdam Balogh //             modeled conservatively, producing conjured symbols everywhere.
23afb13afcSAdam Balogh //
24afb13afcSAdam Balogh // To handle all these types in a common way we introduce a structure called
25afb13afcSAdam Balogh // IteratorPosition which is an abstraction of the position the iterator
26afb13afcSAdam Balogh // represents using symbolic expressions. The checker handles all the
27afb13afcSAdam Balogh // operations on this structure.
28afb13afcSAdam Balogh //
29afb13afcSAdam Balogh // Additionally, depending on the circumstances, operators of types II and III
30afb13afcSAdam Balogh // can be represented as:
31afb13afcSAdam Balogh // * type-IIa, type-IIIa: conjured structure symbols - when returned by value
32afb13afcSAdam Balogh //                        from conservatively evaluated methods such as
33afb13afcSAdam Balogh //                        `.begin()`.
34afb13afcSAdam Balogh // * type-IIb, type-IIIb: memory regions of iterator-typed objects, such as
35afb13afcSAdam Balogh //                        variables or temporaries, when the iterator object is
36afb13afcSAdam Balogh //                        currently treated as an lvalue.
37afb13afcSAdam Balogh // * type-IIc, type-IIIc: compound values of iterator-typed objects, when the
38afb13afcSAdam Balogh //                        iterator object is treated as an rvalue taken of a
39afb13afcSAdam Balogh //                        particular lvalue, eg. a copy of "type-a" iterator
40afb13afcSAdam Balogh //                        object, or an iterator that existed before the
41afb13afcSAdam Balogh //                        analysis has started.
42afb13afcSAdam Balogh //
43afb13afcSAdam Balogh // To handle any of these three different representations stored in an SVal we
44afb13afcSAdam Balogh // use setter and getters functions which separate the three cases. To store
45afb13afcSAdam Balogh // them we use a pointer union of symbol and memory region.
46afb13afcSAdam Balogh //
47afb13afcSAdam Balogh // The checker works the following way: We record the begin and the
48afb13afcSAdam Balogh // past-end iterator for all containers whenever their `.begin()` and `.end()`
49afb13afcSAdam Balogh // are called. Since the Constraint Manager cannot handle such SVals we need
50afb13afcSAdam Balogh // to take over its role. We post-check equality and non-equality comparisons
51afb13afcSAdam Balogh // and record that the two sides are equal if we are in the 'equal' branch
52afb13afcSAdam Balogh // (true-branch for `==` and false-branch for `!=`).
53afb13afcSAdam Balogh //
54afb13afcSAdam Balogh // In case of type-I or type-II iterators we get a concrete integer as a result
55afb13afcSAdam Balogh // of the comparison (1 or 0) but in case of type-III we only get a Symbol. In
56afb13afcSAdam Balogh // this latter case we record the symbol and reload it in evalAssume() and do
57afb13afcSAdam Balogh // the propagation there. We also handle (maybe double) negated comparisons
58afb13afcSAdam Balogh // which are represented in the form of (x == 0 or x != 0) where x is the
59afb13afcSAdam Balogh // comparison itself.
60afb13afcSAdam Balogh //
61afb13afcSAdam Balogh // Since `SimpleConstraintManager` cannot handle complex symbolic expressions
62afb13afcSAdam Balogh // we only use expressions of the format S, S+n or S-n for iterator positions
63afb13afcSAdam Balogh // where S is a conjured symbol and n is an unsigned concrete integer. When
64afb13afcSAdam Balogh // making an assumption e.g. `S1 + n == S2 + m` we store `S1 - S2 == m - n` as
65afb13afcSAdam Balogh // a constraint which we later retrieve when doing an actual comparison.
66afb13afcSAdam Balogh 
67afb13afcSAdam Balogh #include "clang/AST/DeclTemplate.h"
680b9d3a6eSBalazs Benics #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
69afb13afcSAdam Balogh #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
70afb13afcSAdam Balogh #include "clang/StaticAnalyzer/Core/Checker.h"
710b9d3a6eSBalazs Benics #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
72afb13afcSAdam Balogh #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
73afb13afcSAdam Balogh #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
74afb13afcSAdam Balogh #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
75afb13afcSAdam Balogh 
76afb13afcSAdam Balogh #include "Iterator.h"
77afb13afcSAdam Balogh 
78afb13afcSAdam Balogh #include <utility>
79afb13afcSAdam Balogh 
80afb13afcSAdam Balogh using namespace clang;
81afb13afcSAdam Balogh using namespace ento;
82afb13afcSAdam Balogh using namespace iterator;
83afb13afcSAdam Balogh 
84afb13afcSAdam Balogh namespace {
85afb13afcSAdam Balogh 
86afb13afcSAdam Balogh class IteratorModeling
879e63b190SAdam Balogh     : public Checker<check::PostCall, check::PostStmt<UnaryOperator>,
889e63b190SAdam Balogh                      check::PostStmt<BinaryOperator>,
899e63b190SAdam Balogh                      check::PostStmt<MaterializeTemporaryExpr>,
90afb13afcSAdam Balogh                      check::Bind, check::LiveSymbols, check::DeadSymbols> {
91afb13afcSAdam Balogh 
9260bad941SAdam Balogh   using AdvanceFn = void (IteratorModeling::*)(CheckerContext &, const Expr *,
9360bad941SAdam Balogh                                                SVal, SVal, SVal) const;
9460bad941SAdam Balogh 
9560bad941SAdam Balogh   void handleOverloadedOperator(CheckerContext &C, const CallEvent &Call,
9660bad941SAdam Balogh                                 OverloadedOperatorKind Op) const;
9760bad941SAdam Balogh   void handleAdvanceLikeFunction(CheckerContext &C, const CallEvent &Call,
9860bad941SAdam Balogh                                  const Expr *OrigExpr,
9960bad941SAdam Balogh                                  const AdvanceFn *Handler) const;
10060bad941SAdam Balogh 
101855d21a0SAdam Balogh   void handleComparison(CheckerContext &C, const Expr *CE, SVal RetVal,
102afb13afcSAdam Balogh                         const SVal &LVal, const SVal &RVal,
103afb13afcSAdam Balogh                         OverloadedOperatorKind Op) const;
104afb13afcSAdam Balogh   void processComparison(CheckerContext &C, ProgramStateRef State,
105afb13afcSAdam Balogh                          SymbolRef Sym1, SymbolRef Sym2, const SVal &RetVal,
106afb13afcSAdam Balogh                          OverloadedOperatorKind Op) const;
107afb13afcSAdam Balogh   void handleIncrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
108afb13afcSAdam Balogh                        bool Postfix) const;
109afb13afcSAdam Balogh   void handleDecrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
110afb13afcSAdam Balogh                        bool Postfix) const;
111afb13afcSAdam Balogh   void handleRandomIncrOrDecr(CheckerContext &C, const Expr *CE,
112afb13afcSAdam Balogh                               OverloadedOperatorKind Op, const SVal &RetVal,
113141cb8a1SEndre Fülöp                               const SVal &Iterator, const SVal &Amount) const;
1149e63b190SAdam Balogh   void handlePtrIncrOrDecr(CheckerContext &C, const Expr *Iterator,
1159e63b190SAdam Balogh                            OverloadedOperatorKind OK, SVal Offset) const;
11660bad941SAdam Balogh   void handleAdvance(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter,
11760bad941SAdam Balogh                      SVal Amount) const;
11860bad941SAdam Balogh   void handlePrev(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter,
11960bad941SAdam Balogh                   SVal Amount) const;
12060bad941SAdam Balogh   void handleNext(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter,
12160bad941SAdam Balogh                   SVal Amount) const;
122afb13afcSAdam Balogh   void assignToContainer(CheckerContext &C, const Expr *CE, const SVal &RetVal,
123afb13afcSAdam Balogh                          const MemRegion *Cont) const;
12460bad941SAdam Balogh   bool noChangeInAdvance(CheckerContext &C, SVal Iter, const Expr *CE) const;
1256e9c5894SAdam Balogh   void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
1266e9c5894SAdam Balogh                   const char *Sep) const override;
1276e9c5894SAdam Balogh 
12860bad941SAdam Balogh   // std::advance, std::prev & std::next
12960bad941SAdam Balogh   CallDescriptionMap<AdvanceFn> AdvanceLikeFunctions = {
13060bad941SAdam Balogh       // template<class InputIt, class Distance>
13160bad941SAdam Balogh       // void advance(InputIt& it, Distance n);
13260bad941SAdam Balogh       {{{"std", "advance"}, 2}, &IteratorModeling::handleAdvance},
13360bad941SAdam Balogh 
13460bad941SAdam Balogh       // template<class BidirIt>
13560bad941SAdam Balogh       // BidirIt prev(
13660bad941SAdam Balogh       //   BidirIt it,
13760bad941SAdam Balogh       //   typename std::iterator_traits<BidirIt>::difference_type n = 1);
13860bad941SAdam Balogh       {{{"std", "prev"}, 2}, &IteratorModeling::handlePrev},
13960bad941SAdam Balogh 
14060bad941SAdam Balogh       // template<class ForwardIt>
14160bad941SAdam Balogh       // ForwardIt next(
14260bad941SAdam Balogh       //   ForwardIt it,
14360bad941SAdam Balogh       //   typename std::iterator_traits<ForwardIt>::difference_type n = 1);
14460bad941SAdam Balogh       {{{"std", "next"}, 2}, &IteratorModeling::handleNext},
14560bad941SAdam Balogh   };
14660bad941SAdam Balogh 
147afb13afcSAdam Balogh public:
14860bad941SAdam Balogh   IteratorModeling() = default;
149afb13afcSAdam Balogh 
150afb13afcSAdam Balogh   void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
151afb13afcSAdam Balogh   void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const;
1529e63b190SAdam Balogh   void checkPostStmt(const UnaryOperator *UO, CheckerContext &C) const;
1539e63b190SAdam Balogh   void checkPostStmt(const BinaryOperator *BO, CheckerContext &C) const;
154afb13afcSAdam Balogh   void checkPostStmt(const MaterializeTemporaryExpr *MTE,
155afb13afcSAdam Balogh                      CheckerContext &C) const;
156afb13afcSAdam Balogh   void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
157afb13afcSAdam Balogh   void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
158afb13afcSAdam Balogh };
159afb13afcSAdam Balogh 
160afb13afcSAdam Balogh bool isSimpleComparisonOperator(OverloadedOperatorKind OK);
1619e63b190SAdam Balogh bool isSimpleComparisonOperator(BinaryOperatorKind OK);
162afb13afcSAdam Balogh ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val);
163afb13afcSAdam Balogh ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1,
164afb13afcSAdam Balogh                               SymbolRef Sym2, bool Equal);
165afb13afcSAdam Balogh bool isBoundThroughLazyCompoundVal(const Environment &Env,
166afb13afcSAdam Balogh                                    const MemRegion *Reg);
16760bad941SAdam Balogh const ExplodedNode *findCallEnter(const ExplodedNode *Node, const Expr *Call);
168afb13afcSAdam Balogh 
169afb13afcSAdam Balogh } // namespace
170afb13afcSAdam Balogh 
checkPostCall(const CallEvent & Call,CheckerContext & C) const171afb13afcSAdam Balogh void IteratorModeling::checkPostCall(const CallEvent &Call,
172afb13afcSAdam Balogh                                      CheckerContext &C) const {
173afb13afcSAdam Balogh   // Record new iterator positions and iterator position changes
174afb13afcSAdam Balogh   const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
175afb13afcSAdam Balogh   if (!Func)
176afb13afcSAdam Balogh     return;
177afb13afcSAdam Balogh 
178afb13afcSAdam Balogh   if (Func->isOverloadedOperator()) {
179afb13afcSAdam Balogh     const auto Op = Func->getOverloadedOperator();
18060bad941SAdam Balogh     handleOverloadedOperator(C, Call, Op);
18160bad941SAdam Balogh     return;
18260bad941SAdam Balogh   }
18360bad941SAdam Balogh 
184afb13afcSAdam Balogh   const auto *OrigExpr = Call.getOriginExpr();
185afb13afcSAdam Balogh   if (!OrigExpr)
186afb13afcSAdam Balogh     return;
187afb13afcSAdam Balogh 
18860bad941SAdam Balogh   const AdvanceFn *Handler = AdvanceLikeFunctions.lookup(Call);
18960bad941SAdam Balogh   if (Handler) {
19060bad941SAdam Balogh     handleAdvanceLikeFunction(C, Call, OrigExpr, Handler);
191afb13afcSAdam Balogh     return;
192afb13afcSAdam Balogh   }
193afb13afcSAdam Balogh 
1949a08a3faSAdam Balogh   if (!isIteratorType(Call.getResultType()))
195afb13afcSAdam Balogh     return;
196afb13afcSAdam Balogh 
197afb13afcSAdam Balogh   auto State = C.getState();
198afb13afcSAdam Balogh 
199afb13afcSAdam Balogh   // Already bound to container?
200afb13afcSAdam Balogh   if (getIteratorPosition(State, Call.getReturnValue()))
201afb13afcSAdam Balogh     return;
202afb13afcSAdam Balogh 
203afb13afcSAdam Balogh   // Copy-like and move constructors
204afb13afcSAdam Balogh   if (isa<CXXConstructorCall>(&Call) && Call.getNumArgs() == 1) {
205afb13afcSAdam Balogh     if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(0))) {
206afb13afcSAdam Balogh       State = setIteratorPosition(State, Call.getReturnValue(), *Pos);
207afb13afcSAdam Balogh       if (cast<CXXConstructorDecl>(Func)->isMoveConstructor()) {
208afb13afcSAdam Balogh         State = removeIteratorPosition(State, Call.getArgSVal(0));
209afb13afcSAdam Balogh       }
210afb13afcSAdam Balogh       C.addTransition(State);
211afb13afcSAdam Balogh       return;
212afb13afcSAdam Balogh     }
213afb13afcSAdam Balogh   }
214afb13afcSAdam Balogh 
215afb13afcSAdam Balogh   // Assumption: if return value is an iterator which is not yet bound to a
2169e63b190SAdam Balogh   //             container, then look for the first iterator argument of the
2179e63b190SAdam Balogh   //             same type as the return value and bind the return value to
2189e63b190SAdam Balogh   //             the same container. This approach works for STL algorithms.
219afb13afcSAdam Balogh   // FIXME: Add a more conservative mode
220afb13afcSAdam Balogh   for (unsigned i = 0; i < Call.getNumArgs(); ++i) {
2219e63b190SAdam Balogh     if (isIteratorType(Call.getArgExpr(i)->getType()) &&
2229e63b190SAdam Balogh         Call.getArgExpr(i)->getType().getNonReferenceType().getDesugaredType(
2239e63b190SAdam Balogh             C.getASTContext()).getTypePtr() ==
2249e63b190SAdam Balogh         Call.getResultType().getDesugaredType(C.getASTContext()).getTypePtr()) {
225afb13afcSAdam Balogh       if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(i))) {
226afb13afcSAdam Balogh         assignToContainer(C, OrigExpr, Call.getReturnValue(),
227afb13afcSAdam Balogh                           Pos->getContainer());
228afb13afcSAdam Balogh         return;
229afb13afcSAdam Balogh       }
230afb13afcSAdam Balogh     }
231afb13afcSAdam Balogh   }
232afb13afcSAdam Balogh }
233afb13afcSAdam Balogh 
checkBind(SVal Loc,SVal Val,const Stmt * S,CheckerContext & C) const234afb13afcSAdam Balogh void IteratorModeling::checkBind(SVal Loc, SVal Val, const Stmt *S,
235afb13afcSAdam Balogh                                  CheckerContext &C) const {
236afb13afcSAdam Balogh   auto State = C.getState();
237afb13afcSAdam Balogh   const auto *Pos = getIteratorPosition(State, Val);
238afb13afcSAdam Balogh   if (Pos) {
239afb13afcSAdam Balogh     State = setIteratorPosition(State, Loc, *Pos);
240afb13afcSAdam Balogh     C.addTransition(State);
241afb13afcSAdam Balogh   } else {
242afb13afcSAdam Balogh     const auto *OldPos = getIteratorPosition(State, Loc);
243afb13afcSAdam Balogh     if (OldPos) {
244afb13afcSAdam Balogh       State = removeIteratorPosition(State, Loc);
245afb13afcSAdam Balogh       C.addTransition(State);
246afb13afcSAdam Balogh     }
247afb13afcSAdam Balogh   }
248afb13afcSAdam Balogh }
249afb13afcSAdam Balogh 
checkPostStmt(const UnaryOperator * UO,CheckerContext & C) const2509e63b190SAdam Balogh void IteratorModeling::checkPostStmt(const UnaryOperator *UO,
2519e63b190SAdam Balogh                                      CheckerContext &C) const {
2529e63b190SAdam Balogh   UnaryOperatorKind OK = UO->getOpcode();
2539e63b190SAdam Balogh   if (!isIncrementOperator(OK) && !isDecrementOperator(OK))
2549e63b190SAdam Balogh     return;
2559e63b190SAdam Balogh 
2569e63b190SAdam Balogh   auto &SVB = C.getSValBuilder();
2579e63b190SAdam Balogh   handlePtrIncrOrDecr(C, UO->getSubExpr(),
2589e63b190SAdam Balogh                       isIncrementOperator(OK) ? OO_Plus : OO_Minus,
2599e63b190SAdam Balogh                       SVB.makeArrayIndex(1));
2609e63b190SAdam Balogh }
2619e63b190SAdam Balogh 
checkPostStmt(const BinaryOperator * BO,CheckerContext & C) const2629e63b190SAdam Balogh void IteratorModeling::checkPostStmt(const BinaryOperator *BO,
2639e63b190SAdam Balogh                                      CheckerContext &C) const {
264141cb8a1SEndre Fülöp   const ProgramStateRef State = C.getState();
265141cb8a1SEndre Fülöp   const BinaryOperatorKind OK = BO->getOpcode();
266141cb8a1SEndre Fülöp   const Expr *const LHS = BO->getLHS();
267141cb8a1SEndre Fülöp   const Expr *const RHS = BO->getRHS();
268141cb8a1SEndre Fülöp   const SVal LVal = State->getSVal(LHS, C.getLocationContext());
269141cb8a1SEndre Fülöp   const SVal RVal = State->getSVal(RHS, C.getLocationContext());
2709e63b190SAdam Balogh 
2719e63b190SAdam Balogh   if (isSimpleComparisonOperator(BO->getOpcode())) {
2729e63b190SAdam Balogh     SVal Result = State->getSVal(BO, C.getLocationContext());
2739e63b190SAdam Balogh     handleComparison(C, BO, Result, LVal, RVal,
2749e63b190SAdam Balogh                      BinaryOperator::getOverloadedOperator(OK));
2759e63b190SAdam Balogh   } else if (isRandomIncrOrDecrOperator(OK)) {
276141cb8a1SEndre Fülöp     // In case of operator+ the iterator can be either on the LHS (eg.: it + 1),
277141cb8a1SEndre Fülöp     // or on the RHS (eg.: 1 + it). Both cases are modeled.
278141cb8a1SEndre Fülöp     const bool IsIterOnLHS = BO->getLHS()->getType()->isPointerType();
279141cb8a1SEndre Fülöp     const Expr *const &IterExpr = IsIterOnLHS ? LHS : RHS;
280141cb8a1SEndre Fülöp     const Expr *const &AmountExpr = IsIterOnLHS ? RHS : LHS;
281141cb8a1SEndre Fülöp 
282141cb8a1SEndre Fülöp     // The non-iterator side must have an integral or enumeration type.
283141cb8a1SEndre Fülöp     if (!AmountExpr->getType()->isIntegralOrEnumerationType())
284a59d4ae4SAdam Balogh       return;
285141cb8a1SEndre Fülöp     const SVal &AmountVal = IsIterOnLHS ? RVal : LVal;
286141cb8a1SEndre Fülöp     handlePtrIncrOrDecr(C, IterExpr, BinaryOperator::getOverloadedOperator(OK),
287141cb8a1SEndre Fülöp                         AmountVal);
2889e63b190SAdam Balogh   }
2899e63b190SAdam Balogh }
2909e63b190SAdam Balogh 
checkPostStmt(const MaterializeTemporaryExpr * MTE,CheckerContext & C) const291afb13afcSAdam Balogh void IteratorModeling::checkPostStmt(const MaterializeTemporaryExpr *MTE,
292afb13afcSAdam Balogh                                      CheckerContext &C) const {
293afb13afcSAdam Balogh   /* Transfer iterator state to temporary objects */
294afb13afcSAdam Balogh   auto State = C.getState();
295afb13afcSAdam Balogh   const auto *Pos = getIteratorPosition(State, C.getSVal(MTE->getSubExpr()));
296afb13afcSAdam Balogh   if (!Pos)
297afb13afcSAdam Balogh     return;
298afb13afcSAdam Balogh   State = setIteratorPosition(State, C.getSVal(MTE), *Pos);
299afb13afcSAdam Balogh   C.addTransition(State);
300afb13afcSAdam Balogh }
301afb13afcSAdam Balogh 
checkLiveSymbols(ProgramStateRef State,SymbolReaper & SR) const302afb13afcSAdam Balogh void IteratorModeling::checkLiveSymbols(ProgramStateRef State,
303afb13afcSAdam Balogh                                         SymbolReaper &SR) const {
3049a08a3faSAdam Balogh   // Keep symbolic expressions of iterator positions alive
305afb13afcSAdam Balogh   auto RegionMap = State->get<IteratorRegionMap>();
30670d592d6SMark de Wever   for (const auto &Reg : RegionMap) {
307afb13afcSAdam Balogh     const auto Offset = Reg.second.getOffset();
308afb13afcSAdam Balogh     for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
309afb13afcSAdam Balogh       if (isa<SymbolData>(*i))
310afb13afcSAdam Balogh         SR.markLive(*i);
311afb13afcSAdam Balogh   }
312afb13afcSAdam Balogh 
313afb13afcSAdam Balogh   auto SymbolMap = State->get<IteratorSymbolMap>();
31470d592d6SMark de Wever   for (const auto &Sym : SymbolMap) {
315afb13afcSAdam Balogh     const auto Offset = Sym.second.getOffset();
316afb13afcSAdam Balogh     for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
317afb13afcSAdam Balogh       if (isa<SymbolData>(*i))
318afb13afcSAdam Balogh         SR.markLive(*i);
319afb13afcSAdam Balogh   }
320afb13afcSAdam Balogh 
321afb13afcSAdam Balogh }
322afb13afcSAdam Balogh 
checkDeadSymbols(SymbolReaper & SR,CheckerContext & C) const323afb13afcSAdam Balogh void IteratorModeling::checkDeadSymbols(SymbolReaper &SR,
324afb13afcSAdam Balogh                                         CheckerContext &C) const {
325afb13afcSAdam Balogh   // Cleanup
326afb13afcSAdam Balogh   auto State = C.getState();
327afb13afcSAdam Balogh 
328afb13afcSAdam Balogh   auto RegionMap = State->get<IteratorRegionMap>();
32970d592d6SMark de Wever   for (const auto &Reg : RegionMap) {
330afb13afcSAdam Balogh     if (!SR.isLiveRegion(Reg.first)) {
331afb13afcSAdam Balogh       // The region behind the `LazyCompoundVal` is often cleaned up before
332afb13afcSAdam Balogh       // the `LazyCompoundVal` itself. If there are iterator positions keyed
333afb13afcSAdam Balogh       // by these regions their cleanup must be deferred.
334afb13afcSAdam Balogh       if (!isBoundThroughLazyCompoundVal(State->getEnvironment(), Reg.first)) {
335afb13afcSAdam Balogh         State = State->remove<IteratorRegionMap>(Reg.first);
336afb13afcSAdam Balogh       }
337afb13afcSAdam Balogh     }
338afb13afcSAdam Balogh   }
339afb13afcSAdam Balogh 
340afb13afcSAdam Balogh   auto SymbolMap = State->get<IteratorSymbolMap>();
34170d592d6SMark de Wever   for (const auto &Sym : SymbolMap) {
342afb13afcSAdam Balogh     if (!SR.isLive(Sym.first)) {
343afb13afcSAdam Balogh       State = State->remove<IteratorSymbolMap>(Sym.first);
344afb13afcSAdam Balogh     }
345afb13afcSAdam Balogh   }
346afb13afcSAdam Balogh 
347afb13afcSAdam Balogh   C.addTransition(State);
348afb13afcSAdam Balogh }
349afb13afcSAdam Balogh 
35060bad941SAdam Balogh void
handleOverloadedOperator(CheckerContext & C,const CallEvent & Call,OverloadedOperatorKind Op) const35160bad941SAdam Balogh IteratorModeling::handleOverloadedOperator(CheckerContext &C,
35260bad941SAdam Balogh                                            const CallEvent &Call,
35360bad941SAdam Balogh                                            OverloadedOperatorKind Op) const {
35460bad941SAdam Balogh     if (isSimpleComparisonOperator(Op)) {
35560bad941SAdam Balogh       const auto *OrigExpr = Call.getOriginExpr();
35660bad941SAdam Balogh       if (!OrigExpr)
35760bad941SAdam Balogh         return;
35860bad941SAdam Balogh 
35960bad941SAdam Balogh       if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
36060bad941SAdam Balogh         handleComparison(C, OrigExpr, Call.getReturnValue(),
36160bad941SAdam Balogh                          InstCall->getCXXThisVal(), Call.getArgSVal(0), Op);
36260bad941SAdam Balogh         return;
36360bad941SAdam Balogh       }
36460bad941SAdam Balogh 
36560bad941SAdam Balogh       handleComparison(C, OrigExpr, Call.getReturnValue(), Call.getArgSVal(0),
36660bad941SAdam Balogh                          Call.getArgSVal(1), Op);
36760bad941SAdam Balogh       return;
36860bad941SAdam Balogh     } else if (isRandomIncrOrDecrOperator(Op)) {
36960bad941SAdam Balogh       const auto *OrigExpr = Call.getOriginExpr();
37060bad941SAdam Balogh       if (!OrigExpr)
37160bad941SAdam Balogh         return;
37260bad941SAdam Balogh 
37360bad941SAdam Balogh       if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
37460bad941SAdam Balogh         if (Call.getNumArgs() >= 1 &&
37560bad941SAdam Balogh               Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) {
37660bad941SAdam Balogh           handleRandomIncrOrDecr(C, OrigExpr, Op, Call.getReturnValue(),
37760bad941SAdam Balogh                                  InstCall->getCXXThisVal(), Call.getArgSVal(0));
37860bad941SAdam Balogh           return;
37960bad941SAdam Balogh         }
380141cb8a1SEndre Fülöp       } else if (Call.getNumArgs() >= 2) {
381141cb8a1SEndre Fülöp         const Expr *FirstArg = Call.getArgExpr(0);
382141cb8a1SEndre Fülöp         const Expr *SecondArg = Call.getArgExpr(1);
383141cb8a1SEndre Fülöp         const QualType FirstType = FirstArg->getType();
384141cb8a1SEndre Fülöp         const QualType SecondType = SecondArg->getType();
385141cb8a1SEndre Fülöp 
386141cb8a1SEndre Fülöp         if (FirstType->isIntegralOrEnumerationType() ||
387141cb8a1SEndre Fülöp             SecondType->isIntegralOrEnumerationType()) {
388141cb8a1SEndre Fülöp           // In case of operator+ the iterator can be either on the LHS (eg.:
389141cb8a1SEndre Fülöp           // it + 1), or on the RHS (eg.: 1 + it). Both cases are modeled.
390141cb8a1SEndre Fülöp           const bool IsIterFirst = FirstType->isStructureOrClassType();
391141cb8a1SEndre Fülöp           const SVal FirstArg = Call.getArgSVal(0);
392141cb8a1SEndre Fülöp           const SVal SecondArg = Call.getArgSVal(1);
393141cb8a1SEndre Fülöp           const SVal &Iterator = IsIterFirst ? FirstArg : SecondArg;
394141cb8a1SEndre Fülöp           const SVal &Amount = IsIterFirst ? SecondArg : FirstArg;
395141cb8a1SEndre Fülöp 
39660bad941SAdam Balogh           handleRandomIncrOrDecr(C, OrigExpr, Op, Call.getReturnValue(),
397141cb8a1SEndre Fülöp                                  Iterator, Amount);
39860bad941SAdam Balogh           return;
39960bad941SAdam Balogh         }
40060bad941SAdam Balogh       }
40160bad941SAdam Balogh     } else if (isIncrementOperator(Op)) {
40260bad941SAdam Balogh       if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
40360bad941SAdam Balogh         handleIncrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
40460bad941SAdam Balogh                         Call.getNumArgs());
40560bad941SAdam Balogh         return;
40660bad941SAdam Balogh       }
40760bad941SAdam Balogh 
40860bad941SAdam Balogh       handleIncrement(C, Call.getReturnValue(), Call.getArgSVal(0),
40960bad941SAdam Balogh                       Call.getNumArgs());
41060bad941SAdam Balogh       return;
41160bad941SAdam Balogh     } else if (isDecrementOperator(Op)) {
41260bad941SAdam Balogh       if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
41360bad941SAdam Balogh         handleDecrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
41460bad941SAdam Balogh                         Call.getNumArgs());
41560bad941SAdam Balogh         return;
41660bad941SAdam Balogh       }
41760bad941SAdam Balogh 
41860bad941SAdam Balogh       handleDecrement(C, Call.getReturnValue(), Call.getArgSVal(0),
41960bad941SAdam Balogh                         Call.getNumArgs());
42060bad941SAdam Balogh       return;
42160bad941SAdam Balogh     }
42260bad941SAdam Balogh }
42360bad941SAdam Balogh 
42460bad941SAdam Balogh void
handleAdvanceLikeFunction(CheckerContext & C,const CallEvent & Call,const Expr * OrigExpr,const AdvanceFn * Handler) const42560bad941SAdam Balogh IteratorModeling::handleAdvanceLikeFunction(CheckerContext &C,
42660bad941SAdam Balogh                                             const CallEvent &Call,
42760bad941SAdam Balogh                                             const Expr *OrigExpr,
42860bad941SAdam Balogh                                             const AdvanceFn *Handler) const {
42960bad941SAdam Balogh   if (!C.wasInlined) {
43060bad941SAdam Balogh     (this->**Handler)(C, OrigExpr, Call.getReturnValue(),
43160bad941SAdam Balogh                       Call.getArgSVal(0), Call.getArgSVal(1));
43260bad941SAdam Balogh     return;
43360bad941SAdam Balogh   }
43460bad941SAdam Balogh 
43560bad941SAdam Balogh   // If std::advance() was inlined, but a non-standard function it calls inside
43660bad941SAdam Balogh   // was not, then we have to model it explicitly
43760bad941SAdam Balogh   const auto *IdInfo = cast<FunctionDecl>(Call.getDecl())->getIdentifier();
43860bad941SAdam Balogh   if (IdInfo) {
43960bad941SAdam Balogh     if (IdInfo->getName() == "advance") {
44060bad941SAdam Balogh       if (noChangeInAdvance(C, Call.getArgSVal(0), OrigExpr)) {
44160bad941SAdam Balogh         (this->**Handler)(C, OrigExpr, Call.getReturnValue(),
44260bad941SAdam Balogh                           Call.getArgSVal(0), Call.getArgSVal(1));
44360bad941SAdam Balogh       }
44460bad941SAdam Balogh     }
44560bad941SAdam Balogh   }
44660bad941SAdam Balogh }
44760bad941SAdam Balogh 
handleComparison(CheckerContext & C,const Expr * CE,SVal RetVal,const SVal & LVal,const SVal & RVal,OverloadedOperatorKind Op) const448afb13afcSAdam Balogh void IteratorModeling::handleComparison(CheckerContext &C, const Expr *CE,
449855d21a0SAdam Balogh                                        SVal RetVal, const SVal &LVal,
450afb13afcSAdam Balogh                                        const SVal &RVal,
451afb13afcSAdam Balogh                                        OverloadedOperatorKind Op) const {
452afb13afcSAdam Balogh   // Record the operands and the operator of the comparison for the next
453afb13afcSAdam Balogh   // evalAssume, if the result is a symbolic expression. If it is a concrete
454afb13afcSAdam Balogh   // value (only one branch is possible), then transfer the state between
455afb13afcSAdam Balogh   // the operands according to the operator and the result
456afb13afcSAdam Balogh    auto State = C.getState();
457afb13afcSAdam Balogh   const auto *LPos = getIteratorPosition(State, LVal);
458afb13afcSAdam Balogh   const auto *RPos = getIteratorPosition(State, RVal);
459afb13afcSAdam Balogh   const MemRegion *Cont = nullptr;
460afb13afcSAdam Balogh   if (LPos) {
461afb13afcSAdam Balogh     Cont = LPos->getContainer();
462afb13afcSAdam Balogh   } else if (RPos) {
463afb13afcSAdam Balogh     Cont = RPos->getContainer();
464afb13afcSAdam Balogh   }
465afb13afcSAdam Balogh   if (!Cont)
466afb13afcSAdam Balogh     return;
467afb13afcSAdam Balogh 
468ba8cda98SDenys Petrov   // At least one of the iterators has recorded positions. If one of them does
469afb13afcSAdam Balogh   // not then create a new symbol for the offset.
470afb13afcSAdam Balogh   SymbolRef Sym;
471afb13afcSAdam Balogh   if (!LPos || !RPos) {
472afb13afcSAdam Balogh     auto &SymMgr = C.getSymbolManager();
473afb13afcSAdam Balogh     Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
474afb13afcSAdam Balogh                                C.getASTContext().LongTy, C.blockCount());
475afb13afcSAdam Balogh     State = assumeNoOverflow(State, Sym, 4);
476afb13afcSAdam Balogh   }
477afb13afcSAdam Balogh 
478afb13afcSAdam Balogh   if (!LPos) {
479afb13afcSAdam Balogh     State = setIteratorPosition(State, LVal,
480afb13afcSAdam Balogh                                 IteratorPosition::getPosition(Cont, Sym));
481afb13afcSAdam Balogh     LPos = getIteratorPosition(State, LVal);
482afb13afcSAdam Balogh   } else if (!RPos) {
483afb13afcSAdam Balogh     State = setIteratorPosition(State, RVal,
484afb13afcSAdam Balogh                                 IteratorPosition::getPosition(Cont, Sym));
485afb13afcSAdam Balogh     RPos = getIteratorPosition(State, RVal);
486afb13afcSAdam Balogh   }
487afb13afcSAdam Balogh 
488a59d4ae4SAdam Balogh   // If the value for which we just tried to set a new iterator position is
489a59d4ae4SAdam Balogh   // an `SVal`for which no iterator position can be set then the setting was
490a59d4ae4SAdam Balogh   // unsuccessful. We cannot handle the comparison in this case.
491a59d4ae4SAdam Balogh   if (!LPos || !RPos)
492a59d4ae4SAdam Balogh     return;
493a59d4ae4SAdam Balogh 
494ba8cda98SDenys Petrov   // We cannot make assumptions on `UnknownVal`. Let us conjure a symbol
495855d21a0SAdam Balogh   // instead.
496855d21a0SAdam Balogh   if (RetVal.isUnknown()) {
497855d21a0SAdam Balogh     auto &SymMgr = C.getSymbolManager();
498855d21a0SAdam Balogh     auto *LCtx = C.getLocationContext();
499855d21a0SAdam Balogh     RetVal = nonloc::SymbolVal(SymMgr.conjureSymbol(
500855d21a0SAdam Balogh         CE, LCtx, C.getASTContext().BoolTy, C.blockCount()));
501855d21a0SAdam Balogh     State = State->BindExpr(CE, LCtx, RetVal);
502855d21a0SAdam Balogh   }
503855d21a0SAdam Balogh 
504afb13afcSAdam Balogh   processComparison(C, State, LPos->getOffset(), RPos->getOffset(), RetVal, Op);
505afb13afcSAdam Balogh }
506afb13afcSAdam Balogh 
processComparison(CheckerContext & C,ProgramStateRef State,SymbolRef Sym1,SymbolRef Sym2,const SVal & RetVal,OverloadedOperatorKind Op) const507afb13afcSAdam Balogh void IteratorModeling::processComparison(CheckerContext &C,
508afb13afcSAdam Balogh                                          ProgramStateRef State, SymbolRef Sym1,
509afb13afcSAdam Balogh                                          SymbolRef Sym2, const SVal &RetVal,
510afb13afcSAdam Balogh                                          OverloadedOperatorKind Op) const {
511afb13afcSAdam Balogh   if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) {
512afb13afcSAdam Balogh     if ((State = relateSymbols(State, Sym1, Sym2,
513afb13afcSAdam Balogh                               (Op == OO_EqualEqual) ==
514afb13afcSAdam Balogh                                (TruthVal->getValue() != 0)))) {
515afb13afcSAdam Balogh       C.addTransition(State);
516afb13afcSAdam Balogh     } else {
517afb13afcSAdam Balogh       C.generateSink(State, C.getPredecessor());
518afb13afcSAdam Balogh     }
519afb13afcSAdam Balogh     return;
520afb13afcSAdam Balogh   }
521afb13afcSAdam Balogh 
522afb13afcSAdam Balogh   const auto ConditionVal = RetVal.getAs<DefinedSVal>();
523afb13afcSAdam Balogh   if (!ConditionVal)
524afb13afcSAdam Balogh     return;
525afb13afcSAdam Balogh 
526afb13afcSAdam Balogh   if (auto StateTrue = relateSymbols(State, Sym1, Sym2, Op == OO_EqualEqual)) {
527afb13afcSAdam Balogh     StateTrue = StateTrue->assume(*ConditionVal, true);
528afb13afcSAdam Balogh     C.addTransition(StateTrue);
529afb13afcSAdam Balogh   }
530afb13afcSAdam Balogh 
531afb13afcSAdam Balogh   if (auto StateFalse = relateSymbols(State, Sym1, Sym2, Op != OO_EqualEqual)) {
532afb13afcSAdam Balogh     StateFalse = StateFalse->assume(*ConditionVal, false);
533afb13afcSAdam Balogh     C.addTransition(StateFalse);
534afb13afcSAdam Balogh   }
535afb13afcSAdam Balogh }
536afb13afcSAdam Balogh 
handleIncrement(CheckerContext & C,const SVal & RetVal,const SVal & Iter,bool Postfix) const537afb13afcSAdam Balogh void IteratorModeling::handleIncrement(CheckerContext &C, const SVal &RetVal,
538afb13afcSAdam Balogh                                        const SVal &Iter, bool Postfix) const {
539afb13afcSAdam Balogh   // Increment the symbolic expressions which represents the position of the
540afb13afcSAdam Balogh   // iterator
541afb13afcSAdam Balogh   auto State = C.getState();
542afb13afcSAdam Balogh   auto &BVF = C.getSymbolManager().getBasicVals();
543afb13afcSAdam Balogh 
544afb13afcSAdam Balogh   const auto *Pos = getIteratorPosition(State, Iter);
545afb13afcSAdam Balogh   if (!Pos)
546afb13afcSAdam Balogh     return;
547afb13afcSAdam Balogh 
548afb13afcSAdam Balogh   auto NewState =
549afb13afcSAdam Balogh     advancePosition(State, Iter, OO_Plus,
550afb13afcSAdam Balogh                     nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
551afb13afcSAdam Balogh   assert(NewState &&
552afb13afcSAdam Balogh          "Advancing position by concrete int should always be successful");
553afb13afcSAdam Balogh 
554afb13afcSAdam Balogh   const auto *NewPos = getIteratorPosition(NewState, Iter);
555afb13afcSAdam Balogh   assert(NewPos &&
556afb13afcSAdam Balogh          "Iterator should have position after successful advancement");
557afb13afcSAdam Balogh 
558afb13afcSAdam Balogh   State = setIteratorPosition(State, Iter, *NewPos);
559afb13afcSAdam Balogh   State = setIteratorPosition(State, RetVal, Postfix ? *Pos : *NewPos);
560afb13afcSAdam Balogh   C.addTransition(State);
561afb13afcSAdam Balogh }
562afb13afcSAdam Balogh 
handleDecrement(CheckerContext & C,const SVal & RetVal,const SVal & Iter,bool Postfix) const563afb13afcSAdam Balogh void IteratorModeling::handleDecrement(CheckerContext &C, const SVal &RetVal,
564afb13afcSAdam Balogh                                        const SVal &Iter, bool Postfix) const {
565afb13afcSAdam Balogh   // Decrement the symbolic expressions which represents the position of the
566afb13afcSAdam Balogh   // iterator
567afb13afcSAdam Balogh   auto State = C.getState();
568afb13afcSAdam Balogh   auto &BVF = C.getSymbolManager().getBasicVals();
569afb13afcSAdam Balogh 
570afb13afcSAdam Balogh   const auto *Pos = getIteratorPosition(State, Iter);
571afb13afcSAdam Balogh   if (!Pos)
572afb13afcSAdam Balogh     return;
573afb13afcSAdam Balogh 
574afb13afcSAdam Balogh   auto NewState =
575afb13afcSAdam Balogh     advancePosition(State, Iter, OO_Minus,
576afb13afcSAdam Balogh                     nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
577afb13afcSAdam Balogh   assert(NewState &&
578afb13afcSAdam Balogh          "Advancing position by concrete int should always be successful");
579afb13afcSAdam Balogh 
580afb13afcSAdam Balogh   const auto *NewPos = getIteratorPosition(NewState, Iter);
581afb13afcSAdam Balogh   assert(NewPos &&
582afb13afcSAdam Balogh          "Iterator should have position after successful advancement");
583afb13afcSAdam Balogh 
584afb13afcSAdam Balogh   State = setIteratorPosition(State, Iter, *NewPos);
585afb13afcSAdam Balogh   State = setIteratorPosition(State, RetVal, Postfix ? *Pos : *NewPos);
586afb13afcSAdam Balogh   C.addTransition(State);
587afb13afcSAdam Balogh }
588afb13afcSAdam Balogh 
handleRandomIncrOrDecr(CheckerContext & C,const Expr * CE,OverloadedOperatorKind Op,const SVal & RetVal,const SVal & Iterator,const SVal & Amount) const589141cb8a1SEndre Fülöp void IteratorModeling::handleRandomIncrOrDecr(CheckerContext &C, const Expr *CE,
590afb13afcSAdam Balogh                                               OverloadedOperatorKind Op,
591afb13afcSAdam Balogh                                               const SVal &RetVal,
592141cb8a1SEndre Fülöp                                               const SVal &Iterator,
593141cb8a1SEndre Fülöp                                               const SVal &Amount) const {
594afb13afcSAdam Balogh   // Increment or decrement the symbolic expressions which represents the
595afb13afcSAdam Balogh   // position of the iterator
596afb13afcSAdam Balogh   auto State = C.getState();
597afb13afcSAdam Balogh 
598141cb8a1SEndre Fülöp   const auto *Pos = getIteratorPosition(State, Iterator);
599afb13afcSAdam Balogh   if (!Pos)
600afb13afcSAdam Balogh     return;
601afb13afcSAdam Balogh 
602141cb8a1SEndre Fülöp   const auto *Value = &Amount;
603141cb8a1SEndre Fülöp   SVal Val;
604141cb8a1SEndre Fülöp   if (auto LocAmount = Amount.getAs<Loc>()) {
605141cb8a1SEndre Fülöp     Val = State->getRawSVal(*LocAmount);
606141cb8a1SEndre Fülöp     Value = &Val;
607afb13afcSAdam Balogh   }
608afb13afcSAdam Balogh 
609141cb8a1SEndre Fülöp   const auto &TgtVal =
610141cb8a1SEndre Fülöp       (Op == OO_PlusEqual || Op == OO_MinusEqual) ? Iterator : RetVal;
611afb13afcSAdam Balogh 
612ea563daaSAdam Balogh   // `AdvancedState` is a state where the position of `LHS` is advanced. We
613ea563daaSAdam Balogh   // only need this state to retrieve the new position, but we do not want
614ea563daaSAdam Balogh   // to change the position of `LHS` (in every case).
615141cb8a1SEndre Fülöp   auto AdvancedState = advancePosition(State, Iterator, Op, *Value);
616ea563daaSAdam Balogh   if (AdvancedState) {
617141cb8a1SEndre Fülöp     const auto *NewPos = getIteratorPosition(AdvancedState, Iterator);
618afb13afcSAdam Balogh     assert(NewPos &&
619afb13afcSAdam Balogh            "Iterator should have position after successful advancement");
620afb13afcSAdam Balogh 
621ea563daaSAdam Balogh     State = setIteratorPosition(State, TgtVal, *NewPos);
622afb13afcSAdam Balogh     C.addTransition(State);
623afb13afcSAdam Balogh   } else {
624afb13afcSAdam Balogh     assignToContainer(C, CE, TgtVal, Pos->getContainer());
625afb13afcSAdam Balogh   }
626afb13afcSAdam Balogh }
627afb13afcSAdam Balogh 
handlePtrIncrOrDecr(CheckerContext & C,const Expr * Iterator,OverloadedOperatorKind OK,SVal Offset) const6289e63b190SAdam Balogh void IteratorModeling::handlePtrIncrOrDecr(CheckerContext &C,
6299e63b190SAdam Balogh                                            const Expr *Iterator,
6309e63b190SAdam Balogh                                            OverloadedOperatorKind OK,
6319e63b190SAdam Balogh                                            SVal Offset) const {
632*96ccb690SBalazs Benics   if (!isa<DefinedSVal>(Offset))
633a59d4ae4SAdam Balogh     return;
634a59d4ae4SAdam Balogh 
6359e63b190SAdam Balogh   QualType PtrType = Iterator->getType();
6369e63b190SAdam Balogh   if (!PtrType->isPointerType())
6379e63b190SAdam Balogh     return;
6389e63b190SAdam Balogh   QualType ElementType = PtrType->getPointeeType();
6399e63b190SAdam Balogh 
6409e63b190SAdam Balogh   ProgramStateRef State = C.getState();
6419e63b190SAdam Balogh   SVal OldVal = State->getSVal(Iterator, C.getLocationContext());
6429e63b190SAdam Balogh 
6439e63b190SAdam Balogh   const IteratorPosition *OldPos = getIteratorPosition(State, OldVal);
6449e63b190SAdam Balogh   if (!OldPos)
6459e63b190SAdam Balogh     return;
6469e63b190SAdam Balogh 
6479e63b190SAdam Balogh   SVal NewVal;
648a59d4ae4SAdam Balogh   if (OK == OO_Plus || OK == OO_PlusEqual) {
6499e63b190SAdam Balogh     NewVal = State->getLValue(ElementType, Offset, OldVal);
650a59d4ae4SAdam Balogh   } else {
651a59d4ae4SAdam Balogh     auto &SVB = C.getSValBuilder();
652a59d4ae4SAdam Balogh     SVal NegatedOffset = SVB.evalMinus(Offset.castAs<NonLoc>());
6539e63b190SAdam Balogh     NewVal = State->getLValue(ElementType, NegatedOffset, OldVal);
6549e63b190SAdam Balogh   }
6559e63b190SAdam Balogh 
6569e63b190SAdam Balogh   // `AdvancedState` is a state where the position of `Old` is advanced. We
6579e63b190SAdam Balogh   // only need this state to retrieve the new position, but we do not want
6589e63b190SAdam Balogh   // ever to change the position of `OldVal`.
6599e63b190SAdam Balogh   auto AdvancedState = advancePosition(State, OldVal, OK, Offset);
6609e63b190SAdam Balogh   if (AdvancedState) {
6619e63b190SAdam Balogh     const IteratorPosition *NewPos = getIteratorPosition(AdvancedState, OldVal);
6629e63b190SAdam Balogh     assert(NewPos &&
6639e63b190SAdam Balogh            "Iterator should have position after successful advancement");
6649e63b190SAdam Balogh 
6659e63b190SAdam Balogh     ProgramStateRef NewState = setIteratorPosition(State, NewVal, *NewPos);
6669e63b190SAdam Balogh     C.addTransition(NewState);
6679e63b190SAdam Balogh   } else {
6689e63b190SAdam Balogh     assignToContainer(C, Iterator, NewVal, OldPos->getContainer());
6699e63b190SAdam Balogh   }
6709e63b190SAdam Balogh }
6719e63b190SAdam Balogh 
handleAdvance(CheckerContext & C,const Expr * CE,SVal RetVal,SVal Iter,SVal Amount) const67260bad941SAdam Balogh void IteratorModeling::handleAdvance(CheckerContext &C, const Expr *CE,
67360bad941SAdam Balogh                                      SVal RetVal, SVal Iter,
67460bad941SAdam Balogh                                      SVal Amount) const {
67560bad941SAdam Balogh   handleRandomIncrOrDecr(C, CE, OO_PlusEqual, RetVal, Iter, Amount);
67660bad941SAdam Balogh }
67760bad941SAdam Balogh 
handlePrev(CheckerContext & C,const Expr * CE,SVal RetVal,SVal Iter,SVal Amount) const67860bad941SAdam Balogh void IteratorModeling::handlePrev(CheckerContext &C, const Expr *CE,
67960bad941SAdam Balogh                                   SVal RetVal, SVal Iter, SVal Amount) const {
68060bad941SAdam Balogh   handleRandomIncrOrDecr(C, CE, OO_Minus, RetVal, Iter, Amount);
68160bad941SAdam Balogh }
68260bad941SAdam Balogh 
handleNext(CheckerContext & C,const Expr * CE,SVal RetVal,SVal Iter,SVal Amount) const68360bad941SAdam Balogh void IteratorModeling::handleNext(CheckerContext &C, const Expr *CE,
68460bad941SAdam Balogh                                   SVal RetVal, SVal Iter, SVal Amount) const {
68560bad941SAdam Balogh   handleRandomIncrOrDecr(C, CE, OO_Plus, RetVal, Iter, Amount);
68660bad941SAdam Balogh }
68760bad941SAdam Balogh 
assignToContainer(CheckerContext & C,const Expr * CE,const SVal & RetVal,const MemRegion * Cont) const688afb13afcSAdam Balogh void IteratorModeling::assignToContainer(CheckerContext &C, const Expr *CE,
689afb13afcSAdam Balogh                                          const SVal &RetVal,
690afb13afcSAdam Balogh                                          const MemRegion *Cont) const {
691afb13afcSAdam Balogh   Cont = Cont->getMostDerivedObjectRegion();
692afb13afcSAdam Balogh 
693afb13afcSAdam Balogh   auto State = C.getState();
694b198f16eSAdam Balogh   const auto *LCtx = C.getLocationContext();
695b198f16eSAdam Balogh   State = createIteratorPosition(State, RetVal, Cont, CE, LCtx, C.blockCount());
696b198f16eSAdam Balogh 
697afb13afcSAdam Balogh   C.addTransition(State);
698afb13afcSAdam Balogh }
699afb13afcSAdam Balogh 
noChangeInAdvance(CheckerContext & C,SVal Iter,const Expr * CE) const70060bad941SAdam Balogh bool IteratorModeling::noChangeInAdvance(CheckerContext &C, SVal Iter,
70160bad941SAdam Balogh                                          const Expr *CE) const {
70260bad941SAdam Balogh   // Compare the iterator position before and after the call. (To be called
70360bad941SAdam Balogh   // from `checkPostCall()`.)
70460bad941SAdam Balogh   const auto StateAfter = C.getState();
70560bad941SAdam Balogh 
70660bad941SAdam Balogh   const auto *PosAfter = getIteratorPosition(StateAfter, Iter);
70760bad941SAdam Balogh   // If we have no position after the call of `std::advance`, then we are not
70860bad941SAdam Balogh   // interested. (Modeling of an inlined `std::advance()` should not remove the
70960bad941SAdam Balogh   // position in any case.)
71060bad941SAdam Balogh   if (!PosAfter)
71160bad941SAdam Balogh     return false;
71260bad941SAdam Balogh 
71360bad941SAdam Balogh   const ExplodedNode *N = findCallEnter(C.getPredecessor(), CE);
71460bad941SAdam Balogh   assert(N && "Any call should have a `CallEnter` node.");
71560bad941SAdam Balogh 
71660bad941SAdam Balogh   const auto StateBefore = N->getState();
71760bad941SAdam Balogh   const auto *PosBefore = getIteratorPosition(StateBefore, Iter);
718a59d4ae4SAdam Balogh   // FIXME: `std::advance()` should not create a new iterator position but
719a59d4ae4SAdam Balogh   //        change existing ones. However, in case of iterators implemented as
720a59d4ae4SAdam Balogh   //        pointers the handling of parameters in `std::advance()`-like
721a59d4ae4SAdam Balogh   //        functions is still incomplete which may result in cases where
722a59d4ae4SAdam Balogh   //        the new position is assigned to the wrong pointer. This causes
723a59d4ae4SAdam Balogh   //        crash if we use an assertion here.
724a59d4ae4SAdam Balogh   if (!PosBefore)
725a59d4ae4SAdam Balogh     return false;
72660bad941SAdam Balogh 
72760bad941SAdam Balogh   return PosBefore->getOffset() == PosAfter->getOffset();
72860bad941SAdam Balogh }
72960bad941SAdam Balogh 
printState(raw_ostream & Out,ProgramStateRef State,const char * NL,const char * Sep) const7306e9c5894SAdam Balogh void IteratorModeling::printState(raw_ostream &Out, ProgramStateRef State,
7316e9c5894SAdam Balogh                                   const char *NL, const char *Sep) const {
7326e9c5894SAdam Balogh   auto SymbolMap = State->get<IteratorSymbolMap>();
7336e9c5894SAdam Balogh   auto RegionMap = State->get<IteratorRegionMap>();
734ea563daaSAdam Balogh   // Use a counter to add newlines before every line except the first one.
735ea563daaSAdam Balogh   unsigned Count = 0;
7366e9c5894SAdam Balogh 
7376e9c5894SAdam Balogh   if (!SymbolMap.isEmpty() || !RegionMap.isEmpty()) {
7386e9c5894SAdam Balogh     Out << Sep << "Iterator Positions :" << NL;
739b6d9e976SMark de Wever     for (const auto &Sym : SymbolMap) {
740ea563daaSAdam Balogh       if (Count++)
741ea563daaSAdam Balogh         Out << NL;
742ea563daaSAdam Balogh 
7436e9c5894SAdam Balogh       Sym.first->dumpToStream(Out);
7446e9c5894SAdam Balogh       Out << " : ";
7456e9c5894SAdam Balogh       const auto Pos = Sym.second;
7466e9c5894SAdam Balogh       Out << (Pos.isValid() ? "Valid" : "Invalid") << " ; Container == ";
7476e9c5894SAdam Balogh       Pos.getContainer()->dumpToStream(Out);
7486e9c5894SAdam Balogh       Out<<" ; Offset == ";
7496e9c5894SAdam Balogh       Pos.getOffset()->dumpToStream(Out);
7506e9c5894SAdam Balogh     }
7516e9c5894SAdam Balogh 
752b6d9e976SMark de Wever     for (const auto &Reg : RegionMap) {
753ea563daaSAdam Balogh       if (Count++)
754ea563daaSAdam Balogh         Out << NL;
755ea563daaSAdam Balogh 
7566e9c5894SAdam Balogh       Reg.first->dumpToStream(Out);
7576e9c5894SAdam Balogh       Out << " : ";
7586e9c5894SAdam Balogh       const auto Pos = Reg.second;
7596e9c5894SAdam Balogh       Out << (Pos.isValid() ? "Valid" : "Invalid") << " ; Container == ";
7606e9c5894SAdam Balogh       Pos.getContainer()->dumpToStream(Out);
7616e9c5894SAdam Balogh       Out<<" ; Offset == ";
7626e9c5894SAdam Balogh       Pos.getOffset()->dumpToStream(Out);
7636e9c5894SAdam Balogh     }
7646e9c5894SAdam Balogh   }
7656e9c5894SAdam Balogh }
7666e9c5894SAdam Balogh 
767afb13afcSAdam Balogh namespace {
768afb13afcSAdam Balogh 
isSimpleComparisonOperator(OverloadedOperatorKind OK)769afb13afcSAdam Balogh bool isSimpleComparisonOperator(OverloadedOperatorKind OK) {
770afb13afcSAdam Balogh   return OK == OO_EqualEqual || OK == OO_ExclaimEqual;
771afb13afcSAdam Balogh }
772afb13afcSAdam Balogh 
isSimpleComparisonOperator(BinaryOperatorKind OK)7739e63b190SAdam Balogh bool isSimpleComparisonOperator(BinaryOperatorKind OK) {
7749e63b190SAdam Balogh   return OK == BO_EQ || OK == BO_NE;
7759e63b190SAdam Balogh }
7769e63b190SAdam Balogh 
removeIteratorPosition(ProgramStateRef State,const SVal & Val)777afb13afcSAdam Balogh ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val) {
778afb13afcSAdam Balogh   if (auto Reg = Val.getAsRegion()) {
779afb13afcSAdam Balogh     Reg = Reg->getMostDerivedObjectRegion();
780afb13afcSAdam Balogh     return State->remove<IteratorRegionMap>(Reg);
781afb13afcSAdam Balogh   } else if (const auto Sym = Val.getAsSymbol()) {
782afb13afcSAdam Balogh     return State->remove<IteratorSymbolMap>(Sym);
783afb13afcSAdam Balogh   } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
784afb13afcSAdam Balogh     return State->remove<IteratorRegionMap>(LCVal->getRegion());
785afb13afcSAdam Balogh   }
786afb13afcSAdam Balogh   return nullptr;
787afb13afcSAdam Balogh }
788afb13afcSAdam Balogh 
relateSymbols(ProgramStateRef State,SymbolRef Sym1,SymbolRef Sym2,bool Equal)789afb13afcSAdam Balogh ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1,
790afb13afcSAdam Balogh                               SymbolRef Sym2, bool Equal) {
791afb13afcSAdam Balogh   auto &SVB = State->getStateManager().getSValBuilder();
792afb13afcSAdam Balogh 
793afb13afcSAdam Balogh   // FIXME: This code should be reworked as follows:
794afb13afcSAdam Balogh   // 1. Subtract the operands using evalBinOp().
795afb13afcSAdam Balogh   // 2. Assume that the result doesn't overflow.
796afb13afcSAdam Balogh   // 3. Compare the result to 0.
797afb13afcSAdam Balogh   // 4. Assume the result of the comparison.
798afb13afcSAdam Balogh   const auto comparison =
799afb13afcSAdam Balogh     SVB.evalBinOp(State, BO_EQ, nonloc::SymbolVal(Sym1),
800afb13afcSAdam Balogh                   nonloc::SymbolVal(Sym2), SVB.getConditionType());
801afb13afcSAdam Balogh 
802*96ccb690SBalazs Benics   assert(isa<DefinedSVal>(comparison) &&
803afb13afcSAdam Balogh          "Symbol comparison must be a `DefinedSVal`");
804afb13afcSAdam Balogh 
805afb13afcSAdam Balogh   auto NewState = State->assume(comparison.castAs<DefinedSVal>(), Equal);
806afb13afcSAdam Balogh   if (!NewState)
807afb13afcSAdam Balogh     return nullptr;
808afb13afcSAdam Balogh 
809afb13afcSAdam Balogh   if (const auto CompSym = comparison.getAsSymbol()) {
810afb13afcSAdam Balogh     assert(isa<SymIntExpr>(CompSym) &&
811afb13afcSAdam Balogh            "Symbol comparison must be a `SymIntExpr`");
812afb13afcSAdam Balogh     assert(BinaryOperator::isComparisonOp(
813afb13afcSAdam Balogh                cast<SymIntExpr>(CompSym)->getOpcode()) &&
814afb13afcSAdam Balogh            "Symbol comparison must be a comparison");
815afb13afcSAdam Balogh     return assumeNoOverflow(NewState, cast<SymIntExpr>(CompSym)->getLHS(), 2);
816afb13afcSAdam Balogh   }
817afb13afcSAdam Balogh 
818afb13afcSAdam Balogh   return NewState;
819afb13afcSAdam Balogh }
820afb13afcSAdam Balogh 
isBoundThroughLazyCompoundVal(const Environment & Env,const MemRegion * Reg)821afb13afcSAdam Balogh bool isBoundThroughLazyCompoundVal(const Environment &Env,
822afb13afcSAdam Balogh                                    const MemRegion *Reg) {
82370d592d6SMark de Wever   for (const auto &Binding : Env) {
824afb13afcSAdam Balogh     if (const auto LCVal = Binding.second.getAs<nonloc::LazyCompoundVal>()) {
825afb13afcSAdam Balogh       if (LCVal->getRegion() == Reg)
826afb13afcSAdam Balogh         return true;
827afb13afcSAdam Balogh     }
828afb13afcSAdam Balogh   }
829afb13afcSAdam Balogh 
830afb13afcSAdam Balogh   return false;
831afb13afcSAdam Balogh }
832afb13afcSAdam Balogh 
findCallEnter(const ExplodedNode * Node,const Expr * Call)83360bad941SAdam Balogh const ExplodedNode *findCallEnter(const ExplodedNode *Node, const Expr *Call) {
83460bad941SAdam Balogh   while (Node) {
83560bad941SAdam Balogh     ProgramPoint PP = Node->getLocation();
83660bad941SAdam Balogh     if (auto Enter = PP.getAs<CallEnter>()) {
83760bad941SAdam Balogh       if (Enter->getCallExpr() == Call)
83860bad941SAdam Balogh         break;
83960bad941SAdam Balogh     }
84060bad941SAdam Balogh 
84160bad941SAdam Balogh     Node = Node->getFirstPred();
84260bad941SAdam Balogh   }
84360bad941SAdam Balogh 
84460bad941SAdam Balogh   return Node;
84560bad941SAdam Balogh }
84660bad941SAdam Balogh 
847afb13afcSAdam Balogh } // namespace
848afb13afcSAdam Balogh 
registerIteratorModeling(CheckerManager & mgr)849afb13afcSAdam Balogh void ento::registerIteratorModeling(CheckerManager &mgr) {
850afb13afcSAdam Balogh   mgr.registerChecker<IteratorModeling>();
851afb13afcSAdam Balogh }
852afb13afcSAdam Balogh 
shouldRegisterIteratorModeling(const CheckerManager & mgr)853bda3dd0dSKirstóf Umann bool ento::shouldRegisterIteratorModeling(const CheckerManager &mgr) {
854afb13afcSAdam Balogh   return true;
855afb13afcSAdam Balogh }
856