xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp (revision afb13afcf2232c81fe8097832e5b6a2bde6bb3a5)
1 //===-- IteratorModeling.cpp --------------------------------------*- C++ -*--//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Defines a checker for using iterators outside their range (past end). Usage
10 // means here dereferencing, incrementing etc.
11 //
12 //===----------------------------------------------------------------------===//
13 //
14 // In the code, iterator can be represented as a:
15 // * type-I: typedef-ed pointer. Operations over such iterator, such as
16 //           comparisons or increments, are modeled straightforwardly by the
17 //           analyzer.
18 // * type-II: structure with its method bodies available.  Operations over such
19 //            iterator are inlined by the analyzer, and results of modeling
20 //            these operations are exposing implementation details of the
21 //            iterators, which is not necessarily helping.
22 // * type-III: completely opaque structure. Operations over such iterator are
23 //             modeled conservatively, producing conjured symbols everywhere.
24 //
25 // To handle all these types in a common way we introduce a structure called
26 // IteratorPosition which is an abstraction of the position the iterator
27 // represents using symbolic expressions. The checker handles all the
28 // operations on this structure.
29 //
30 // Additionally, depending on the circumstances, operators of types II and III
31 // can be represented as:
32 // * type-IIa, type-IIIa: conjured structure symbols - when returned by value
33 //                        from conservatively evaluated methods such as
34 //                        `.begin()`.
35 // * type-IIb, type-IIIb: memory regions of iterator-typed objects, such as
36 //                        variables or temporaries, when the iterator object is
37 //                        currently treated as an lvalue.
38 // * type-IIc, type-IIIc: compound values of iterator-typed objects, when the
39 //                        iterator object is treated as an rvalue taken of a
40 //                        particular lvalue, eg. a copy of "type-a" iterator
41 //                        object, or an iterator that existed before the
42 //                        analysis has started.
43 //
44 // To handle any of these three different representations stored in an SVal we
45 // use setter and getters functions which separate the three cases. To store
46 // them we use a pointer union of symbol and memory region.
47 //
48 // The checker works the following way: We record the begin and the
49 // past-end iterator for all containers whenever their `.begin()` and `.end()`
50 // are called. Since the Constraint Manager cannot handle such SVals we need
51 // to take over its role. We post-check equality and non-equality comparisons
52 // and record that the two sides are equal if we are in the 'equal' branch
53 // (true-branch for `==` and false-branch for `!=`).
54 //
55 // In case of type-I or type-II iterators we get a concrete integer as a result
56 // of the comparison (1 or 0) but in case of type-III we only get a Symbol. In
57 // this latter case we record the symbol and reload it in evalAssume() and do
58 // the propagation there. We also handle (maybe double) negated comparisons
59 // which are represented in the form of (x == 0 or x != 0) where x is the
60 // comparison itself.
61 //
62 // Since `SimpleConstraintManager` cannot handle complex symbolic expressions
63 // we only use expressions of the format S, S+n or S-n for iterator positions
64 // where S is a conjured symbol and n is an unsigned concrete integer. When
65 // making an assumption e.g. `S1 + n == S2 + m` we store `S1 - S2 == m - n` as
66 // a constraint which we later retrieve when doing an actual comparison.
67 
68 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
69 #include "clang/AST/DeclTemplate.h"
70 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
71 #include "clang/StaticAnalyzer/Core/Checker.h"
72 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
73 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
74 #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
75 
76 #include "Iterator.h"
77 
78 #include <utility>
79 
80 using namespace clang;
81 using namespace ento;
82 using namespace iterator;
83 
84 namespace {
85 
86 class IteratorModeling
87     : public Checker<check::PostCall, check::PostStmt<MaterializeTemporaryExpr>,
88                      check::Bind, check::LiveSymbols, check::DeadSymbols> {
89 
90   void handleComparison(CheckerContext &C, const Expr *CE, const SVal &RetVal,
91                         const SVal &LVal, const SVal &RVal,
92                         OverloadedOperatorKind Op) const;
93   void processComparison(CheckerContext &C, ProgramStateRef State,
94                          SymbolRef Sym1, SymbolRef Sym2, const SVal &RetVal,
95                          OverloadedOperatorKind Op) const;
96   void handleIncrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
97                        bool Postfix) const;
98   void handleDecrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
99                        bool Postfix) const;
100   void handleRandomIncrOrDecr(CheckerContext &C, const Expr *CE,
101                               OverloadedOperatorKind Op, const SVal &RetVal,
102                               const SVal &LHS, const SVal &RHS) const;
103   void handleBegin(CheckerContext &C, const Expr *CE, const SVal &RetVal,
104                    const SVal &Cont) const;
105   void handleEnd(CheckerContext &C, const Expr *CE, const SVal &RetVal,
106                  const SVal &Cont) const;
107   void assignToContainer(CheckerContext &C, const Expr *CE, const SVal &RetVal,
108                          const MemRegion *Cont) const;
109   void handleAssign(CheckerContext &C, const SVal &Cont,
110                     const Expr *CE = nullptr,
111                     const SVal &OldCont = UndefinedVal()) const;
112   void handleClear(CheckerContext &C, const SVal &Cont) const;
113   void handlePushBack(CheckerContext &C, const SVal &Cont) const;
114   void handlePopBack(CheckerContext &C, const SVal &Cont) const;
115   void handlePushFront(CheckerContext &C, const SVal &Cont) const;
116   void handlePopFront(CheckerContext &C, const SVal &Cont) const;
117   void handleInsert(CheckerContext &C, const SVal &Iter) const;
118   void handleErase(CheckerContext &C, const SVal &Iter) const;
119   void handleErase(CheckerContext &C, const SVal &Iter1,
120                    const SVal &Iter2) const;
121   void handleEraseAfter(CheckerContext &C, const SVal &Iter) const;
122   void handleEraseAfter(CheckerContext &C, const SVal &Iter1,
123                         const SVal &Iter2) const;
124 public:
125   IteratorModeling() {}
126 
127   void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
128   void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const;
129   void checkPostStmt(const CXXConstructExpr *CCE, CheckerContext &C) const;
130   void checkPostStmt(const DeclStmt *DS, CheckerContext &C) const;
131   void checkPostStmt(const MaterializeTemporaryExpr *MTE,
132                      CheckerContext &C) const;
133   void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
134   void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
135 };
136 
137 bool isBeginCall(const FunctionDecl *Func);
138 bool isEndCall(const FunctionDecl *Func);
139 bool isAssignCall(const FunctionDecl *Func);
140 bool isClearCall(const FunctionDecl *Func);
141 bool isPushBackCall(const FunctionDecl *Func);
142 bool isEmplaceBackCall(const FunctionDecl *Func);
143 bool isPopBackCall(const FunctionDecl *Func);
144 bool isPushFrontCall(const FunctionDecl *Func);
145 bool isEmplaceFrontCall(const FunctionDecl *Func);
146 bool isPopFrontCall(const FunctionDecl *Func);
147 bool isAssignmentOperator(OverloadedOperatorKind OK);
148 bool isSimpleComparisonOperator(OverloadedOperatorKind OK);
149 bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg);
150 bool frontModifiable(ProgramStateRef State, const MemRegion *Reg);
151 bool backModifiable(ProgramStateRef State, const MemRegion *Reg);
152 SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont);
153 SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont);
154 ProgramStateRef createContainerBegin(ProgramStateRef State,
155                                      const MemRegion *Cont, const Expr *E,
156                                      QualType T, const LocationContext *LCtx,
157                                      unsigned BlockCount);
158 ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
159                                    const Expr *E, QualType T,
160                                    const LocationContext *LCtx,
161                                    unsigned BlockCount);
162 ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
163                                  const ContainerData &CData);
164 ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val);
165 ProgramStateRef assumeNoOverflow(ProgramStateRef State, SymbolRef Sym,
166                                  long Scale);
167 ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State,
168                                                const MemRegion *Cont);
169 ProgramStateRef
170 invalidateAllIteratorPositionsExcept(ProgramStateRef State,
171                                      const MemRegion *Cont, SymbolRef Offset,
172                                      BinaryOperator::Opcode Opc);
173 ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
174                                             SymbolRef Offset,
175                                             BinaryOperator::Opcode Opc);
176 ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
177                                             SymbolRef Offset1,
178                                             BinaryOperator::Opcode Opc1,
179                                             SymbolRef Offset2,
180                                             BinaryOperator::Opcode Opc2);
181 ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State,
182                                              const MemRegion *Cont,
183                                              const MemRegion *NewCont);
184 ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State,
185                                                    const MemRegion *Cont,
186                                                    const MemRegion *NewCont,
187                                                    SymbolRef Offset,
188                                                    BinaryOperator::Opcode Opc);
189 ProgramStateRef rebaseSymbolInIteratorPositionsIf(
190     ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym,
191     SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc);
192 ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1,
193                               SymbolRef Sym2, bool Equal);
194 SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB, SymbolRef Expr,
195                         SymbolRef OldSym, SymbolRef NewSym);
196 bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont);
197 bool isBoundThroughLazyCompoundVal(const Environment &Env,
198                                    const MemRegion *Reg);
199 
200 } // namespace
201 
202 void IteratorModeling::checkPostCall(const CallEvent &Call,
203                                      CheckerContext &C) const {
204   // Record new iterator positions and iterator position changes
205   const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
206   if (!Func)
207     return;
208 
209   if (Func->isOverloadedOperator()) {
210     const auto Op = Func->getOverloadedOperator();
211     if (isAssignmentOperator(Op)) {
212       // Overloaded 'operator=' must be a non-static member function.
213       const auto *InstCall = cast<CXXInstanceCall>(&Call);
214       if (cast<CXXMethodDecl>(Func)->isMoveAssignmentOperator()) {
215         handleAssign(C, InstCall->getCXXThisVal(), Call.getOriginExpr(),
216                      Call.getArgSVal(0));
217         return;
218       }
219 
220       handleAssign(C, InstCall->getCXXThisVal());
221       return;
222     } else if (isSimpleComparisonOperator(Op)) {
223       const auto *OrigExpr = Call.getOriginExpr();
224       if (!OrigExpr)
225         return;
226 
227       if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
228         handleComparison(C, OrigExpr, Call.getReturnValue(),
229                          InstCall->getCXXThisVal(), Call.getArgSVal(0), Op);
230         return;
231       }
232 
233       handleComparison(C, OrigExpr, Call.getReturnValue(), Call.getArgSVal(0),
234                          Call.getArgSVal(1), Op);
235       return;
236     } else if (isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) {
237       const auto *OrigExpr = Call.getOriginExpr();
238       if (!OrigExpr)
239         return;
240 
241       if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
242         if (Call.getNumArgs() >= 1 &&
243               Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) {
244           handleRandomIncrOrDecr(C, OrigExpr, Func->getOverloadedOperator(),
245                                  Call.getReturnValue(),
246                                  InstCall->getCXXThisVal(), Call.getArgSVal(0));
247           return;
248         }
249       } else {
250         if (Call.getNumArgs() >= 2 &&
251               Call.getArgExpr(1)->getType()->isIntegralOrEnumerationType()) {
252           handleRandomIncrOrDecr(C, OrigExpr, Func->getOverloadedOperator(),
253                                  Call.getReturnValue(), Call.getArgSVal(0),
254                                  Call.getArgSVal(1));
255           return;
256         }
257       }
258     } else if (isIncrementOperator(Func->getOverloadedOperator())) {
259       if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
260         handleIncrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
261                         Call.getNumArgs());
262         return;
263       }
264 
265       handleIncrement(C, Call.getReturnValue(), Call.getArgSVal(0),
266                       Call.getNumArgs());
267       return;
268     } else if (isDecrementOperator(Func->getOverloadedOperator())) {
269       if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
270         handleDecrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
271                         Call.getNumArgs());
272         return;
273       }
274 
275       handleDecrement(C, Call.getReturnValue(), Call.getArgSVal(0),
276                         Call.getNumArgs());
277       return;
278     }
279   } else {
280     if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
281       if (isAssignCall(Func)) {
282         handleAssign(C, InstCall->getCXXThisVal());
283         return;
284       }
285 
286       if (isClearCall(Func)) {
287         handleClear(C, InstCall->getCXXThisVal());
288         return;
289       }
290 
291       if (isPushBackCall(Func) || isEmplaceBackCall(Func)) {
292         handlePushBack(C, InstCall->getCXXThisVal());
293         return;
294       }
295 
296       if (isPopBackCall(Func)) {
297         handlePopBack(C, InstCall->getCXXThisVal());
298         return;
299       }
300 
301       if (isPushFrontCall(Func) || isEmplaceFrontCall(Func)) {
302         handlePushFront(C, InstCall->getCXXThisVal());
303         return;
304       }
305 
306       if (isPopFrontCall(Func)) {
307         handlePopFront(C, InstCall->getCXXThisVal());
308         return;
309       }
310 
311       if (isInsertCall(Func) || isEmplaceCall(Func)) {
312         handleInsert(C, Call.getArgSVal(0));
313         return;
314       }
315 
316       if (isEraseCall(Func)) {
317         if (Call.getNumArgs() == 1) {
318           handleErase(C, Call.getArgSVal(0));
319           return;
320         }
321 
322         if (Call.getNumArgs() == 2) {
323           handleErase(C, Call.getArgSVal(0), Call.getArgSVal(1));
324           return;
325         }
326       }
327 
328       if (isEraseAfterCall(Func)) {
329         if (Call.getNumArgs() == 1) {
330           handleEraseAfter(C, Call.getArgSVal(0));
331           return;
332         }
333 
334         if (Call.getNumArgs() == 2) {
335           handleEraseAfter(C, Call.getArgSVal(0), Call.getArgSVal(1));
336           return;
337         }
338       }
339     }
340 
341     const auto *OrigExpr = Call.getOriginExpr();
342     if (!OrigExpr)
343       return;
344 
345     if (!isIteratorType(Call.getResultType()))
346       return;
347 
348     auto State = C.getState();
349 
350     if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
351       if (isBeginCall(Func)) {
352         handleBegin(C, OrigExpr, Call.getReturnValue(),
353                     InstCall->getCXXThisVal());
354         return;
355       }
356 
357       if (isEndCall(Func)) {
358         handleEnd(C, OrigExpr, Call.getReturnValue(),
359                   InstCall->getCXXThisVal());
360         return;
361       }
362     }
363 
364     // Already bound to container?
365     if (getIteratorPosition(State, Call.getReturnValue()))
366       return;
367 
368     // Copy-like and move constructors
369     if (isa<CXXConstructorCall>(&Call) && Call.getNumArgs() == 1) {
370       if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(0))) {
371         State = setIteratorPosition(State, Call.getReturnValue(), *Pos);
372         if (cast<CXXConstructorDecl>(Func)->isMoveConstructor()) {
373           State = removeIteratorPosition(State, Call.getArgSVal(0));
374         }
375         C.addTransition(State);
376         return;
377       }
378     }
379 
380     // Assumption: if return value is an iterator which is not yet bound to a
381     //             container, then look for the first iterator argument, and
382     //             bind the return value to the same container. This approach
383     //             works for STL algorithms.
384     // FIXME: Add a more conservative mode
385     for (unsigned i = 0; i < Call.getNumArgs(); ++i) {
386       if (isIteratorType(Call.getArgExpr(i)->getType())) {
387         if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(i))) {
388           assignToContainer(C, OrigExpr, Call.getReturnValue(),
389                             Pos->getContainer());
390           return;
391         }
392       }
393     }
394   }
395 }
396 
397 void IteratorModeling::checkBind(SVal Loc, SVal Val, const Stmt *S,
398                                  CheckerContext &C) const {
399   auto State = C.getState();
400   const auto *Pos = getIteratorPosition(State, Val);
401   if (Pos) {
402     State = setIteratorPosition(State, Loc, *Pos);
403     C.addTransition(State);
404   } else {
405     const auto *OldPos = getIteratorPosition(State, Loc);
406     if (OldPos) {
407       State = removeIteratorPosition(State, Loc);
408       C.addTransition(State);
409     }
410   }
411 }
412 
413 void IteratorModeling::checkPostStmt(const MaterializeTemporaryExpr *MTE,
414                                      CheckerContext &C) const {
415   /* Transfer iterator state to temporary objects */
416   auto State = C.getState();
417   const auto *Pos = getIteratorPosition(State, C.getSVal(MTE->getSubExpr()));
418   if (!Pos)
419     return;
420   State = setIteratorPosition(State, C.getSVal(MTE), *Pos);
421   C.addTransition(State);
422 }
423 
424 void IteratorModeling::checkLiveSymbols(ProgramStateRef State,
425                                         SymbolReaper &SR) const {
426   // Keep symbolic expressions of iterator positions, container begins and ends
427   // alive
428   auto RegionMap = State->get<IteratorRegionMap>();
429   for (const auto Reg : RegionMap) {
430     const auto Offset = Reg.second.getOffset();
431     for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
432       if (isa<SymbolData>(*i))
433         SR.markLive(*i);
434   }
435 
436   auto SymbolMap = State->get<IteratorSymbolMap>();
437   for (const auto Sym : SymbolMap) {
438     const auto Offset = Sym.second.getOffset();
439     for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
440       if (isa<SymbolData>(*i))
441         SR.markLive(*i);
442   }
443 
444   auto ContMap = State->get<ContainerMap>();
445   for (const auto Cont : ContMap) {
446     const auto CData = Cont.second;
447     if (CData.getBegin()) {
448       SR.markLive(CData.getBegin());
449       if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getBegin()))
450         SR.markLive(SIE->getLHS());
451     }
452     if (CData.getEnd()) {
453       SR.markLive(CData.getEnd());
454       if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getEnd()))
455         SR.markLive(SIE->getLHS());
456     }
457   }
458 }
459 
460 void IteratorModeling::checkDeadSymbols(SymbolReaper &SR,
461                                         CheckerContext &C) const {
462   // Cleanup
463   auto State = C.getState();
464 
465   auto RegionMap = State->get<IteratorRegionMap>();
466   for (const auto Reg : RegionMap) {
467     if (!SR.isLiveRegion(Reg.first)) {
468       // The region behind the `LazyCompoundVal` is often cleaned up before
469       // the `LazyCompoundVal` itself. If there are iterator positions keyed
470       // by these regions their cleanup must be deferred.
471       if (!isBoundThroughLazyCompoundVal(State->getEnvironment(), Reg.first)) {
472         State = State->remove<IteratorRegionMap>(Reg.first);
473       }
474     }
475   }
476 
477   auto SymbolMap = State->get<IteratorSymbolMap>();
478   for (const auto Sym : SymbolMap) {
479     if (!SR.isLive(Sym.first)) {
480       State = State->remove<IteratorSymbolMap>(Sym.first);
481     }
482   }
483 
484   auto ContMap = State->get<ContainerMap>();
485   for (const auto Cont : ContMap) {
486     if (!SR.isLiveRegion(Cont.first)) {
487       // We must keep the container data while it has live iterators to be able
488       // to compare them to the begin and the end of the container.
489       if (!hasLiveIterators(State, Cont.first)) {
490         State = State->remove<ContainerMap>(Cont.first);
491       }
492     }
493   }
494 
495   C.addTransition(State);
496 }
497 
498 void IteratorModeling::handleComparison(CheckerContext &C, const Expr *CE,
499                                         const SVal &RetVal, const SVal &LVal,
500                                         const SVal &RVal,
501                                         OverloadedOperatorKind Op) const {
502   // Record the operands and the operator of the comparison for the next
503   // evalAssume, if the result is a symbolic expression. If it is a concrete
504   // value (only one branch is possible), then transfer the state between
505   // the operands according to the operator and the result
506    auto State = C.getState();
507   const auto *LPos = getIteratorPosition(State, LVal);
508   const auto *RPos = getIteratorPosition(State, RVal);
509   const MemRegion *Cont = nullptr;
510   if (LPos) {
511     Cont = LPos->getContainer();
512   } else if (RPos) {
513     Cont = RPos->getContainer();
514   }
515   if (!Cont)
516     return;
517 
518   // At least one of the iterators have recorded positions. If one of them has
519   // not then create a new symbol for the offset.
520   SymbolRef Sym;
521   if (!LPos || !RPos) {
522     auto &SymMgr = C.getSymbolManager();
523     Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
524                                C.getASTContext().LongTy, C.blockCount());
525     State = assumeNoOverflow(State, Sym, 4);
526   }
527 
528   if (!LPos) {
529     State = setIteratorPosition(State, LVal,
530                                 IteratorPosition::getPosition(Cont, Sym));
531     LPos = getIteratorPosition(State, LVal);
532   } else if (!RPos) {
533     State = setIteratorPosition(State, RVal,
534                                 IteratorPosition::getPosition(Cont, Sym));
535     RPos = getIteratorPosition(State, RVal);
536   }
537 
538   processComparison(C, State, LPos->getOffset(), RPos->getOffset(), RetVal, Op);
539 }
540 
541 void IteratorModeling::processComparison(CheckerContext &C,
542                                          ProgramStateRef State, SymbolRef Sym1,
543                                          SymbolRef Sym2, const SVal &RetVal,
544                                          OverloadedOperatorKind Op) const {
545   if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) {
546     if ((State = relateSymbols(State, Sym1, Sym2,
547                               (Op == OO_EqualEqual) ==
548                                (TruthVal->getValue() != 0)))) {
549       C.addTransition(State);
550     } else {
551       C.generateSink(State, C.getPredecessor());
552     }
553     return;
554   }
555 
556   const auto ConditionVal = RetVal.getAs<DefinedSVal>();
557   if (!ConditionVal)
558     return;
559 
560   if (auto StateTrue = relateSymbols(State, Sym1, Sym2, Op == OO_EqualEqual)) {
561     StateTrue = StateTrue->assume(*ConditionVal, true);
562     C.addTransition(StateTrue);
563   }
564 
565   if (auto StateFalse = relateSymbols(State, Sym1, Sym2, Op != OO_EqualEqual)) {
566     StateFalse = StateFalse->assume(*ConditionVal, false);
567     C.addTransition(StateFalse);
568   }
569 }
570 
571 void IteratorModeling::handleIncrement(CheckerContext &C, const SVal &RetVal,
572                                        const SVal &Iter, bool Postfix) const {
573   // Increment the symbolic expressions which represents the position of the
574   // iterator
575   auto State = C.getState();
576   auto &BVF = C.getSymbolManager().getBasicVals();
577 
578   const auto *Pos = getIteratorPosition(State, Iter);
579   if (!Pos)
580     return;
581 
582   auto NewState =
583     advancePosition(State, Iter, OO_Plus,
584                     nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
585   assert(NewState &&
586          "Advancing position by concrete int should always be successful");
587 
588   const auto *NewPos = getIteratorPosition(NewState, Iter);
589   assert(NewPos &&
590          "Iterator should have position after successful advancement");
591 
592   State = setIteratorPosition(State, Iter, *NewPos);
593   State = setIteratorPosition(State, RetVal, Postfix ? *Pos : *NewPos);
594   C.addTransition(State);
595 }
596 
597 void IteratorModeling::handleDecrement(CheckerContext &C, const SVal &RetVal,
598                                        const SVal &Iter, bool Postfix) const {
599   // Decrement the symbolic expressions which represents the position of the
600   // iterator
601   auto State = C.getState();
602   auto &BVF = C.getSymbolManager().getBasicVals();
603 
604   const auto *Pos = getIteratorPosition(State, Iter);
605   if (!Pos)
606     return;
607 
608   auto NewState =
609     advancePosition(State, Iter, OO_Minus,
610                     nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
611   assert(NewState &&
612          "Advancing position by concrete int should always be successful");
613 
614   const auto *NewPos = getIteratorPosition(NewState, Iter);
615   assert(NewPos &&
616          "Iterator should have position after successful advancement");
617 
618   State = setIteratorPosition(State, Iter, *NewPos);
619   State = setIteratorPosition(State, RetVal, Postfix ? *Pos : *NewPos);
620   C.addTransition(State);
621 }
622 
623 void IteratorModeling::handleRandomIncrOrDecr(CheckerContext &C,
624                                               const Expr *CE,
625                                               OverloadedOperatorKind Op,
626                                               const SVal &RetVal,
627                                               const SVal &LHS,
628                                               const SVal &RHS) const {
629   // Increment or decrement the symbolic expressions which represents the
630   // position of the iterator
631   auto State = C.getState();
632 
633   const auto *Pos = getIteratorPosition(State, LHS);
634   if (!Pos)
635     return;
636 
637   const auto *value = &RHS;
638   if (auto loc = RHS.getAs<Loc>()) {
639     const auto val = State->getRawSVal(*loc);
640     value = &val;
641   }
642 
643   auto &TgtVal = (Op == OO_PlusEqual || Op == OO_MinusEqual) ? LHS : RetVal;
644 
645   auto NewState =
646     advancePosition(State, LHS, Op, *value);
647   if (NewState) {
648     const auto *NewPos = getIteratorPosition(NewState, LHS);
649     assert(NewPos &&
650            "Iterator should have position after successful advancement");
651 
652     State = setIteratorPosition(NewState, TgtVal, *NewPos);
653     C.addTransition(State);
654   } else {
655     assignToContainer(C, CE, TgtVal, Pos->getContainer());
656   }
657 }
658 
659 void IteratorModeling::handleBegin(CheckerContext &C, const Expr *CE,
660                                    const SVal &RetVal, const SVal &Cont) const {
661   const auto *ContReg = Cont.getAsRegion();
662   if (!ContReg)
663     return;
664 
665   ContReg = ContReg->getMostDerivedObjectRegion();
666 
667   // If the container already has a begin symbol then use it. Otherwise first
668   // create a new one.
669   auto State = C.getState();
670   auto BeginSym = getContainerBegin(State, ContReg);
671   if (!BeginSym) {
672     State = createContainerBegin(State, ContReg, CE, C.getASTContext().LongTy,
673                                  C.getLocationContext(), C.blockCount());
674     BeginSym = getContainerBegin(State, ContReg);
675   }
676   State = setIteratorPosition(State, RetVal,
677                               IteratorPosition::getPosition(ContReg, BeginSym));
678   C.addTransition(State);
679 }
680 
681 void IteratorModeling::handleEnd(CheckerContext &C, const Expr *CE,
682                                  const SVal &RetVal, const SVal &Cont) const {
683   const auto *ContReg = Cont.getAsRegion();
684   if (!ContReg)
685     return;
686 
687   ContReg = ContReg->getMostDerivedObjectRegion();
688 
689   // If the container already has an end symbol then use it. Otherwise first
690   // create a new one.
691   auto State = C.getState();
692   auto EndSym = getContainerEnd(State, ContReg);
693   if (!EndSym) {
694     State = createContainerEnd(State, ContReg, CE, C.getASTContext().LongTy,
695                                C.getLocationContext(), C.blockCount());
696     EndSym = getContainerEnd(State, ContReg);
697   }
698   State = setIteratorPosition(State, RetVal,
699                               IteratorPosition::getPosition(ContReg, EndSym));
700   C.addTransition(State);
701 }
702 
703 void IteratorModeling::assignToContainer(CheckerContext &C, const Expr *CE,
704                                          const SVal &RetVal,
705                                          const MemRegion *Cont) const {
706   Cont = Cont->getMostDerivedObjectRegion();
707 
708   auto State = C.getState();
709   auto &SymMgr = C.getSymbolManager();
710   auto Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
711                                   C.getASTContext().LongTy, C.blockCount());
712   State = assumeNoOverflow(State, Sym, 4);
713   State = setIteratorPosition(State, RetVal,
714                               IteratorPosition::getPosition(Cont, Sym));
715   C.addTransition(State);
716 }
717 
718 void IteratorModeling::handleAssign(CheckerContext &C, const SVal &Cont,
719                                     const Expr *CE, const SVal &OldCont) const {
720   const auto *ContReg = Cont.getAsRegion();
721   if (!ContReg)
722     return;
723 
724   ContReg = ContReg->getMostDerivedObjectRegion();
725 
726   // Assignment of a new value to a container always invalidates all its
727   // iterators
728   auto State = C.getState();
729   const auto CData = getContainerData(State, ContReg);
730   if (CData) {
731     State = invalidateAllIteratorPositions(State, ContReg);
732   }
733 
734   // In case of move, iterators of the old container (except the past-end
735   // iterators) remain valid but refer to the new container
736   if (!OldCont.isUndef()) {
737     const auto *OldContReg = OldCont.getAsRegion();
738     if (OldContReg) {
739       OldContReg = OldContReg->getMostDerivedObjectRegion();
740       const auto OldCData = getContainerData(State, OldContReg);
741       if (OldCData) {
742         if (const auto OldEndSym = OldCData->getEnd()) {
743           // If we already assigned an "end" symbol to the old container, then
744           // first reassign all iterator positions to the new container which
745           // are not past the container (thus not greater or equal to the
746           // current "end" symbol).
747           State = reassignAllIteratorPositionsUnless(State, OldContReg, ContReg,
748                                                      OldEndSym, BO_GE);
749           auto &SymMgr = C.getSymbolManager();
750           auto &SVB = C.getSValBuilder();
751           // Then generate and assign a new "end" symbol for the new container.
752           auto NewEndSym =
753               SymMgr.conjureSymbol(CE, C.getLocationContext(),
754                                    C.getASTContext().LongTy, C.blockCount());
755           State = assumeNoOverflow(State, NewEndSym, 4);
756           if (CData) {
757             State = setContainerData(State, ContReg, CData->newEnd(NewEndSym));
758           } else {
759             State = setContainerData(State, ContReg,
760                                      ContainerData::fromEnd(NewEndSym));
761           }
762           // Finally, replace the old "end" symbol in the already reassigned
763           // iterator positions with the new "end" symbol.
764           State = rebaseSymbolInIteratorPositionsIf(
765               State, SVB, OldEndSym, NewEndSym, OldEndSym, BO_LT);
766         } else {
767           // There was no "end" symbol assigned yet to the old container,
768           // so reassign all iterator positions to the new container.
769           State = reassignAllIteratorPositions(State, OldContReg, ContReg);
770         }
771         if (const auto OldBeginSym = OldCData->getBegin()) {
772           // If we already assigned a "begin" symbol to the old container, then
773           // assign it to the new container and remove it from the old one.
774           if (CData) {
775             State =
776                 setContainerData(State, ContReg, CData->newBegin(OldBeginSym));
777           } else {
778             State = setContainerData(State, ContReg,
779                                      ContainerData::fromBegin(OldBeginSym));
780           }
781           State =
782               setContainerData(State, OldContReg, OldCData->newEnd(nullptr));
783         }
784       } else {
785         // There was neither "begin" nor "end" symbol assigned yet to the old
786         // container, so reassign all iterator positions to the new container.
787         State = reassignAllIteratorPositions(State, OldContReg, ContReg);
788       }
789     }
790   }
791   C.addTransition(State);
792 }
793 
794 void IteratorModeling::handleClear(CheckerContext &C, const SVal &Cont) const {
795   const auto *ContReg = Cont.getAsRegion();
796   if (!ContReg)
797     return;
798 
799   ContReg = ContReg->getMostDerivedObjectRegion();
800 
801   // The clear() operation invalidates all the iterators, except the past-end
802   // iterators of list-like containers
803   auto State = C.getState();
804   if (!hasSubscriptOperator(State, ContReg) ||
805       !backModifiable(State, ContReg)) {
806     const auto CData = getContainerData(State, ContReg);
807     if (CData) {
808       if (const auto EndSym = CData->getEnd()) {
809         State =
810             invalidateAllIteratorPositionsExcept(State, ContReg, EndSym, BO_GE);
811         C.addTransition(State);
812         return;
813       }
814     }
815   }
816   State = invalidateAllIteratorPositions(State, ContReg);
817   C.addTransition(State);
818 }
819 
820 void IteratorModeling::handlePushBack(CheckerContext &C,
821                                       const SVal &Cont) const {
822   const auto *ContReg = Cont.getAsRegion();
823   if (!ContReg)
824     return;
825 
826   ContReg = ContReg->getMostDerivedObjectRegion();
827 
828   // For deque-like containers invalidate all iterator positions
829   auto State = C.getState();
830   if (hasSubscriptOperator(State, ContReg) && frontModifiable(State, ContReg)) {
831     State = invalidateAllIteratorPositions(State, ContReg);
832     C.addTransition(State);
833     return;
834   }
835 
836   const auto CData = getContainerData(State, ContReg);
837   if (!CData)
838     return;
839 
840   // For vector-like containers invalidate the past-end iterator positions
841   if (const auto EndSym = CData->getEnd()) {
842     if (hasSubscriptOperator(State, ContReg)) {
843       State = invalidateIteratorPositions(State, EndSym, BO_GE);
844     }
845     auto &SymMgr = C.getSymbolManager();
846     auto &BVF = SymMgr.getBasicVals();
847     auto &SVB = C.getSValBuilder();
848     const auto newEndSym =
849       SVB.evalBinOp(State, BO_Add,
850                     nonloc::SymbolVal(EndSym),
851                     nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
852                     SymMgr.getType(EndSym)).getAsSymbol();
853     State = setContainerData(State, ContReg, CData->newEnd(newEndSym));
854   }
855   C.addTransition(State);
856 }
857 
858 void IteratorModeling::handlePopBack(CheckerContext &C,
859                                      const SVal &Cont) const {
860   const auto *ContReg = Cont.getAsRegion();
861   if (!ContReg)
862     return;
863 
864   ContReg = ContReg->getMostDerivedObjectRegion();
865 
866   auto State = C.getState();
867   const auto CData = getContainerData(State, ContReg);
868   if (!CData)
869     return;
870 
871   if (const auto EndSym = CData->getEnd()) {
872     auto &SymMgr = C.getSymbolManager();
873     auto &BVF = SymMgr.getBasicVals();
874     auto &SVB = C.getSValBuilder();
875     const auto BackSym =
876       SVB.evalBinOp(State, BO_Sub,
877                     nonloc::SymbolVal(EndSym),
878                     nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
879                     SymMgr.getType(EndSym)).getAsSymbol();
880     // For vector-like and deque-like containers invalidate the last and the
881     // past-end iterator positions. For list-like containers only invalidate
882     // the last position
883     if (hasSubscriptOperator(State, ContReg) &&
884         backModifiable(State, ContReg)) {
885       State = invalidateIteratorPositions(State, BackSym, BO_GE);
886       State = setContainerData(State, ContReg, CData->newEnd(nullptr));
887     } else {
888       State = invalidateIteratorPositions(State, BackSym, BO_EQ);
889     }
890     auto newEndSym = BackSym;
891     State = setContainerData(State, ContReg, CData->newEnd(newEndSym));
892     C.addTransition(State);
893   }
894 }
895 
896 void IteratorModeling::handlePushFront(CheckerContext &C,
897                                        const SVal &Cont) const {
898   const auto *ContReg = Cont.getAsRegion();
899   if (!ContReg)
900     return;
901 
902   ContReg = ContReg->getMostDerivedObjectRegion();
903 
904   // For deque-like containers invalidate all iterator positions
905   auto State = C.getState();
906   if (hasSubscriptOperator(State, ContReg)) {
907     State = invalidateAllIteratorPositions(State, ContReg);
908     C.addTransition(State);
909   } else {
910     const auto CData = getContainerData(State, ContReg);
911     if (!CData)
912       return;
913 
914     if (const auto BeginSym = CData->getBegin()) {
915       auto &SymMgr = C.getSymbolManager();
916       auto &BVF = SymMgr.getBasicVals();
917       auto &SVB = C.getSValBuilder();
918       const auto newBeginSym =
919         SVB.evalBinOp(State, BO_Sub,
920                       nonloc::SymbolVal(BeginSym),
921                       nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
922                       SymMgr.getType(BeginSym)).getAsSymbol();
923       State = setContainerData(State, ContReg, CData->newBegin(newBeginSym));
924       C.addTransition(State);
925     }
926   }
927 }
928 
929 void IteratorModeling::handlePopFront(CheckerContext &C,
930                                       const SVal &Cont) const {
931   const auto *ContReg = Cont.getAsRegion();
932   if (!ContReg)
933     return;
934 
935   ContReg = ContReg->getMostDerivedObjectRegion();
936 
937   auto State = C.getState();
938   const auto CData = getContainerData(State, ContReg);
939   if (!CData)
940     return;
941 
942   // For deque-like containers invalidate all iterator positions. For list-like
943   // iterators only invalidate the first position
944   if (const auto BeginSym = CData->getBegin()) {
945     if (hasSubscriptOperator(State, ContReg)) {
946       State = invalidateIteratorPositions(State, BeginSym, BO_LE);
947     } else {
948       State = invalidateIteratorPositions(State, BeginSym, BO_EQ);
949     }
950     auto &SymMgr = C.getSymbolManager();
951     auto &BVF = SymMgr.getBasicVals();
952     auto &SVB = C.getSValBuilder();
953     const auto newBeginSym =
954       SVB.evalBinOp(State, BO_Add,
955                     nonloc::SymbolVal(BeginSym),
956                     nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
957                     SymMgr.getType(BeginSym)).getAsSymbol();
958     State = setContainerData(State, ContReg, CData->newBegin(newBeginSym));
959     C.addTransition(State);
960   }
961 }
962 
963 void IteratorModeling::handleInsert(CheckerContext &C, const SVal &Iter) const {
964   auto State = C.getState();
965   const auto *Pos = getIteratorPosition(State, Iter);
966   if (!Pos)
967     return;
968 
969   // For deque-like containers invalidate all iterator positions. For
970   // vector-like containers invalidate iterator positions after the insertion.
971   const auto *Cont = Pos->getContainer();
972   if (hasSubscriptOperator(State, Cont) && backModifiable(State, Cont)) {
973     if (frontModifiable(State, Cont)) {
974       State = invalidateAllIteratorPositions(State, Cont);
975     } else {
976       State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE);
977     }
978     if (const auto *CData = getContainerData(State, Cont)) {
979       if (const auto EndSym = CData->getEnd()) {
980         State = invalidateIteratorPositions(State, EndSym, BO_GE);
981         State = setContainerData(State, Cont, CData->newEnd(nullptr));
982       }
983     }
984     C.addTransition(State);
985   }
986 }
987 
988 void IteratorModeling::handleErase(CheckerContext &C, const SVal &Iter) const {
989   auto State = C.getState();
990   const auto *Pos = getIteratorPosition(State, Iter);
991   if (!Pos)
992     return;
993 
994   // For deque-like containers invalidate all iterator positions. For
995   // vector-like containers invalidate iterator positions at and after the
996   // deletion. For list-like containers only invalidate the deleted position.
997   const auto *Cont = Pos->getContainer();
998   if (hasSubscriptOperator(State, Cont) && backModifiable(State, Cont)) {
999     if (frontModifiable(State, Cont)) {
1000       State = invalidateAllIteratorPositions(State, Cont);
1001     } else {
1002       State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE);
1003     }
1004     if (const auto *CData = getContainerData(State, Cont)) {
1005       if (const auto EndSym = CData->getEnd()) {
1006         State = invalidateIteratorPositions(State, EndSym, BO_GE);
1007         State = setContainerData(State, Cont, CData->newEnd(nullptr));
1008       }
1009     }
1010   } else {
1011     State = invalidateIteratorPositions(State, Pos->getOffset(), BO_EQ);
1012   }
1013   C.addTransition(State);
1014 }
1015 
1016 void IteratorModeling::handleErase(CheckerContext &C, const SVal &Iter1,
1017                                    const SVal &Iter2) const {
1018   auto State = C.getState();
1019   const auto *Pos1 = getIteratorPosition(State, Iter1);
1020   const auto *Pos2 = getIteratorPosition(State, Iter2);
1021   if (!Pos1 || !Pos2)
1022     return;
1023 
1024   // For deque-like containers invalidate all iterator positions. For
1025   // vector-like containers invalidate iterator positions at and after the
1026   // deletion range. For list-like containers only invalidate the deleted
1027   // position range [first..last].
1028   const auto *Cont = Pos1->getContainer();
1029   if (hasSubscriptOperator(State, Cont) && backModifiable(State, Cont)) {
1030     if (frontModifiable(State, Cont)) {
1031       State = invalidateAllIteratorPositions(State, Cont);
1032     } else {
1033       State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE);
1034     }
1035     if (const auto *CData = getContainerData(State, Cont)) {
1036       if (const auto EndSym = CData->getEnd()) {
1037         State = invalidateIteratorPositions(State, EndSym, BO_GE);
1038         State = setContainerData(State, Cont, CData->newEnd(nullptr));
1039       }
1040     }
1041   } else {
1042     State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE,
1043                                         Pos2->getOffset(), BO_LT);
1044   }
1045   C.addTransition(State);
1046 }
1047 
1048 void IteratorModeling::handleEraseAfter(CheckerContext &C,
1049                                         const SVal &Iter) const {
1050   auto State = C.getState();
1051   const auto *Pos = getIteratorPosition(State, Iter);
1052   if (!Pos)
1053     return;
1054 
1055   // Invalidate the deleted iterator position, which is the position of the
1056   // parameter plus one.
1057   auto &SymMgr = C.getSymbolManager();
1058   auto &BVF = SymMgr.getBasicVals();
1059   auto &SVB = C.getSValBuilder();
1060   const auto NextSym =
1061     SVB.evalBinOp(State, BO_Add,
1062                   nonloc::SymbolVal(Pos->getOffset()),
1063                   nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
1064                   SymMgr.getType(Pos->getOffset())).getAsSymbol();
1065   State = invalidateIteratorPositions(State, NextSym, BO_EQ);
1066   C.addTransition(State);
1067 }
1068 
1069 void IteratorModeling::handleEraseAfter(CheckerContext &C, const SVal &Iter1,
1070                                         const SVal &Iter2) const {
1071   auto State = C.getState();
1072   const auto *Pos1 = getIteratorPosition(State, Iter1);
1073   const auto *Pos2 = getIteratorPosition(State, Iter2);
1074   if (!Pos1 || !Pos2)
1075     return;
1076 
1077   // Invalidate the deleted iterator position range (first..last)
1078   State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GT,
1079                                       Pos2->getOffset(), BO_LT);
1080   C.addTransition(State);
1081 }
1082 
1083 namespace {
1084 
1085 const CXXRecordDecl *getCXXRecordDecl(ProgramStateRef State,
1086                                       const MemRegion *Reg);
1087 
1088 bool isBeginCall(const FunctionDecl *Func) {
1089   const auto *IdInfo = Func->getIdentifier();
1090   if (!IdInfo)
1091     return false;
1092   return IdInfo->getName().endswith_lower("begin");
1093 }
1094 
1095 bool isEndCall(const FunctionDecl *Func) {
1096   const auto *IdInfo = Func->getIdentifier();
1097   if (!IdInfo)
1098     return false;
1099   return IdInfo->getName().endswith_lower("end");
1100 }
1101 
1102 bool isAssignCall(const FunctionDecl *Func) {
1103   const auto *IdInfo = Func->getIdentifier();
1104   if (!IdInfo)
1105     return false;
1106   if (Func->getNumParams() > 2)
1107     return false;
1108   return IdInfo->getName() == "assign";
1109 }
1110 
1111 bool isClearCall(const FunctionDecl *Func) {
1112   const auto *IdInfo = Func->getIdentifier();
1113   if (!IdInfo)
1114     return false;
1115   if (Func->getNumParams() > 0)
1116     return false;
1117   return IdInfo->getName() == "clear";
1118 }
1119 
1120 bool isPushBackCall(const FunctionDecl *Func) {
1121   const auto *IdInfo = Func->getIdentifier();
1122   if (!IdInfo)
1123     return false;
1124   if (Func->getNumParams() != 1)
1125     return false;
1126   return IdInfo->getName() == "push_back";
1127 }
1128 
1129 bool isEmplaceBackCall(const FunctionDecl *Func) {
1130   const auto *IdInfo = Func->getIdentifier();
1131   if (!IdInfo)
1132     return false;
1133   if (Func->getNumParams() < 1)
1134     return false;
1135   return IdInfo->getName() == "emplace_back";
1136 }
1137 
1138 bool isPopBackCall(const FunctionDecl *Func) {
1139   const auto *IdInfo = Func->getIdentifier();
1140   if (!IdInfo)
1141     return false;
1142   if (Func->getNumParams() > 0)
1143     return false;
1144   return IdInfo->getName() == "pop_back";
1145 }
1146 
1147 bool isPushFrontCall(const FunctionDecl *Func) {
1148   const auto *IdInfo = Func->getIdentifier();
1149   if (!IdInfo)
1150     return false;
1151   if (Func->getNumParams() != 1)
1152     return false;
1153   return IdInfo->getName() == "push_front";
1154 }
1155 
1156 bool isEmplaceFrontCall(const FunctionDecl *Func) {
1157   const auto *IdInfo = Func->getIdentifier();
1158   if (!IdInfo)
1159     return false;
1160   if (Func->getNumParams() < 1)
1161     return false;
1162   return IdInfo->getName() == "emplace_front";
1163 }
1164 
1165 bool isPopFrontCall(const FunctionDecl *Func) {
1166   const auto *IdInfo = Func->getIdentifier();
1167   if (!IdInfo)
1168     return false;
1169   if (Func->getNumParams() > 0)
1170     return false;
1171   return IdInfo->getName() == "pop_front";
1172 }
1173 
1174 bool isAssignmentOperator(OverloadedOperatorKind OK) { return OK == OO_Equal; }
1175 
1176 bool isSimpleComparisonOperator(OverloadedOperatorKind OK) {
1177   return OK == OO_EqualEqual || OK == OO_ExclaimEqual;
1178 }
1179 
1180 bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg) {
1181   const auto *CRD = getCXXRecordDecl(State, Reg);
1182   if (!CRD)
1183     return false;
1184 
1185   for (const auto *Method : CRD->methods()) {
1186     if (!Method->isOverloadedOperator())
1187       continue;
1188     const auto OPK = Method->getOverloadedOperator();
1189     if (OPK == OO_Subscript) {
1190       return true;
1191     }
1192   }
1193   return false;
1194 }
1195 
1196 bool frontModifiable(ProgramStateRef State, const MemRegion *Reg) {
1197   const auto *CRD = getCXXRecordDecl(State, Reg);
1198   if (!CRD)
1199     return false;
1200 
1201   for (const auto *Method : CRD->methods()) {
1202     if (!Method->getDeclName().isIdentifier())
1203       continue;
1204     if (Method->getName() == "push_front" || Method->getName() == "pop_front") {
1205       return true;
1206     }
1207   }
1208   return false;
1209 }
1210 
1211 bool backModifiable(ProgramStateRef State, const MemRegion *Reg) {
1212   const auto *CRD = getCXXRecordDecl(State, Reg);
1213   if (!CRD)
1214     return false;
1215 
1216   for (const auto *Method : CRD->methods()) {
1217     if (!Method->getDeclName().isIdentifier())
1218       continue;
1219     if (Method->getName() == "push_back" || Method->getName() == "pop_back") {
1220       return true;
1221     }
1222   }
1223   return false;
1224 }
1225 
1226 const CXXRecordDecl *getCXXRecordDecl(ProgramStateRef State,
1227                                       const MemRegion *Reg) {
1228   auto TI = getDynamicTypeInfo(State, Reg);
1229   if (!TI.isValid())
1230     return nullptr;
1231 
1232   auto Type = TI.getType();
1233   if (const auto *RefT = Type->getAs<ReferenceType>()) {
1234     Type = RefT->getPointeeType();
1235   }
1236 
1237   return Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
1238 }
1239 
1240 SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont) {
1241   const auto *CDataPtr = getContainerData(State, Cont);
1242   if (!CDataPtr)
1243     return nullptr;
1244 
1245   return CDataPtr->getBegin();
1246 }
1247 
1248 SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont) {
1249   const auto *CDataPtr = getContainerData(State, Cont);
1250   if (!CDataPtr)
1251     return nullptr;
1252 
1253   return CDataPtr->getEnd();
1254 }
1255 
1256 ProgramStateRef createContainerBegin(ProgramStateRef State,
1257                                      const MemRegion *Cont, const Expr *E,
1258                                      QualType T, const LocationContext *LCtx,
1259                                      unsigned BlockCount) {
1260   // Only create if it does not exist
1261   const auto *CDataPtr = getContainerData(State, Cont);
1262   if (CDataPtr && CDataPtr->getBegin())
1263     return State;
1264 
1265   auto &SymMgr = State->getSymbolManager();
1266   const SymbolConjured *Sym = SymMgr.conjureSymbol(E, LCtx, T, BlockCount,
1267                                                    "begin");
1268   State = assumeNoOverflow(State, Sym, 4);
1269 
1270   if (CDataPtr) {
1271     const auto CData = CDataPtr->newBegin(Sym);
1272     return setContainerData(State, Cont, CData);
1273   }
1274 
1275   const auto CData = ContainerData::fromBegin(Sym);
1276   return setContainerData(State, Cont, CData);
1277 }
1278 
1279 ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
1280                                    const Expr *E, QualType T,
1281                                    const LocationContext *LCtx,
1282                                    unsigned BlockCount) {
1283   // Only create if it does not exist
1284   const auto *CDataPtr = getContainerData(State, Cont);
1285   if (CDataPtr && CDataPtr->getEnd())
1286     return State;
1287 
1288   auto &SymMgr = State->getSymbolManager();
1289   const SymbolConjured *Sym = SymMgr.conjureSymbol(E, LCtx, T, BlockCount,
1290                                                   "end");
1291   State = assumeNoOverflow(State, Sym, 4);
1292 
1293   if (CDataPtr) {
1294     const auto CData = CDataPtr->newEnd(Sym);
1295     return setContainerData(State, Cont, CData);
1296   }
1297 
1298   const auto CData = ContainerData::fromEnd(Sym);
1299   return setContainerData(State, Cont, CData);
1300 }
1301 
1302 ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
1303                                  const ContainerData &CData) {
1304   return State->set<ContainerMap>(Cont, CData);
1305 }
1306 
1307 ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val) {
1308   if (auto Reg = Val.getAsRegion()) {
1309     Reg = Reg->getMostDerivedObjectRegion();
1310     return State->remove<IteratorRegionMap>(Reg);
1311   } else if (const auto Sym = Val.getAsSymbol()) {
1312     return State->remove<IteratorSymbolMap>(Sym);
1313   } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
1314     return State->remove<IteratorRegionMap>(LCVal->getRegion());
1315   }
1316   return nullptr;
1317 }
1318 
1319 // This function tells the analyzer's engine that symbols produced by our
1320 // checker, most notably iterator positions, are relatively small.
1321 // A distance between items in the container should not be very large.
1322 // By assuming that it is within around 1/8 of the address space,
1323 // we can help the analyzer perform operations on these symbols
1324 // without being afraid of integer overflows.
1325 // FIXME: Should we provide it as an API, so that all checkers could use it?
1326 ProgramStateRef assumeNoOverflow(ProgramStateRef State, SymbolRef Sym,
1327                                  long Scale) {
1328   SValBuilder &SVB = State->getStateManager().getSValBuilder();
1329   BasicValueFactory &BV = SVB.getBasicValueFactory();
1330 
1331   QualType T = Sym->getType();
1332   assert(T->isSignedIntegerOrEnumerationType());
1333   APSIntType AT = BV.getAPSIntType(T);
1334 
1335   ProgramStateRef NewState = State;
1336 
1337   llvm::APSInt Max = AT.getMaxValue() / AT.getValue(Scale);
1338   SVal IsCappedFromAbove =
1339       SVB.evalBinOpNN(State, BO_LE, nonloc::SymbolVal(Sym),
1340                       nonloc::ConcreteInt(Max), SVB.getConditionType());
1341   if (auto DV = IsCappedFromAbove.getAs<DefinedSVal>()) {
1342     NewState = NewState->assume(*DV, true);
1343     if (!NewState)
1344       return State;
1345   }
1346 
1347   llvm::APSInt Min = -Max;
1348   SVal IsCappedFromBelow =
1349       SVB.evalBinOpNN(State, BO_GE, nonloc::SymbolVal(Sym),
1350                       nonloc::ConcreteInt(Min), SVB.getConditionType());
1351   if (auto DV = IsCappedFromBelow.getAs<DefinedSVal>()) {
1352     NewState = NewState->assume(*DV, true);
1353     if (!NewState)
1354       return State;
1355   }
1356 
1357   return NewState;
1358 }
1359 
1360 ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1,
1361                               SymbolRef Sym2, bool Equal) {
1362   auto &SVB = State->getStateManager().getSValBuilder();
1363 
1364   // FIXME: This code should be reworked as follows:
1365   // 1. Subtract the operands using evalBinOp().
1366   // 2. Assume that the result doesn't overflow.
1367   // 3. Compare the result to 0.
1368   // 4. Assume the result of the comparison.
1369   const auto comparison =
1370     SVB.evalBinOp(State, BO_EQ, nonloc::SymbolVal(Sym1),
1371                   nonloc::SymbolVal(Sym2), SVB.getConditionType());
1372 
1373   assert(comparison.getAs<DefinedSVal>() &&
1374     "Symbol comparison must be a `DefinedSVal`");
1375 
1376   auto NewState = State->assume(comparison.castAs<DefinedSVal>(), Equal);
1377   if (!NewState)
1378     return nullptr;
1379 
1380   if (const auto CompSym = comparison.getAsSymbol()) {
1381     assert(isa<SymIntExpr>(CompSym) &&
1382            "Symbol comparison must be a `SymIntExpr`");
1383     assert(BinaryOperator::isComparisonOp(
1384                cast<SymIntExpr>(CompSym)->getOpcode()) &&
1385            "Symbol comparison must be a comparison");
1386     return assumeNoOverflow(NewState, cast<SymIntExpr>(CompSym)->getLHS(), 2);
1387   }
1388 
1389   return NewState;
1390 }
1391 
1392 bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont) {
1393   auto RegionMap = State->get<IteratorRegionMap>();
1394   for (const auto Reg : RegionMap) {
1395     if (Reg.second.getContainer() == Cont)
1396       return true;
1397   }
1398 
1399   auto SymbolMap = State->get<IteratorSymbolMap>();
1400   for (const auto Sym : SymbolMap) {
1401     if (Sym.second.getContainer() == Cont)
1402       return true;
1403   }
1404 
1405   return false;
1406 }
1407 
1408 bool isBoundThroughLazyCompoundVal(const Environment &Env,
1409                                    const MemRegion *Reg) {
1410   for (const auto Binding: Env) {
1411     if (const auto LCVal = Binding.second.getAs<nonloc::LazyCompoundVal>()) {
1412       if (LCVal->getRegion() == Reg)
1413         return true;
1414     }
1415   }
1416 
1417   return false;
1418 }
1419 
1420 template <typename Condition, typename Process>
1421 ProgramStateRef processIteratorPositions(ProgramStateRef State, Condition Cond,
1422                                          Process Proc) {
1423   auto &RegionMapFactory = State->get_context<IteratorRegionMap>();
1424   auto RegionMap = State->get<IteratorRegionMap>();
1425   bool Changed = false;
1426   for (const auto Reg : RegionMap) {
1427     if (Cond(Reg.second)) {
1428       RegionMap = RegionMapFactory.add(RegionMap, Reg.first, Proc(Reg.second));
1429       Changed = true;
1430     }
1431   }
1432 
1433   if (Changed)
1434     State = State->set<IteratorRegionMap>(RegionMap);
1435 
1436   auto &SymbolMapFactory = State->get_context<IteratorSymbolMap>();
1437   auto SymbolMap = State->get<IteratorSymbolMap>();
1438   Changed = false;
1439   for (const auto Sym : SymbolMap) {
1440     if (Cond(Sym.second)) {
1441       SymbolMap = SymbolMapFactory.add(SymbolMap, Sym.first, Proc(Sym.second));
1442       Changed = true;
1443     }
1444   }
1445 
1446   if (Changed)
1447     State = State->set<IteratorSymbolMap>(SymbolMap);
1448 
1449   return State;
1450 }
1451 
1452 ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State,
1453                                                const MemRegion *Cont) {
1454   auto MatchCont = [&](const IteratorPosition &Pos) {
1455     return Pos.getContainer() == Cont;
1456   };
1457   auto Invalidate = [&](const IteratorPosition &Pos) {
1458     return Pos.invalidate();
1459   };
1460   return processIteratorPositions(State, MatchCont, Invalidate);
1461 }
1462 
1463 ProgramStateRef
1464 invalidateAllIteratorPositionsExcept(ProgramStateRef State,
1465                                      const MemRegion *Cont, SymbolRef Offset,
1466                                      BinaryOperator::Opcode Opc) {
1467   auto MatchContAndCompare = [&](const IteratorPosition &Pos) {
1468     return Pos.getContainer() == Cont &&
1469            !compare(State, Pos.getOffset(), Offset, Opc);
1470   };
1471   auto Invalidate = [&](const IteratorPosition &Pos) {
1472     return Pos.invalidate();
1473   };
1474   return processIteratorPositions(State, MatchContAndCompare, Invalidate);
1475 }
1476 
1477 ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
1478                                             SymbolRef Offset,
1479                                             BinaryOperator::Opcode Opc) {
1480   auto Compare = [&](const IteratorPosition &Pos) {
1481     return compare(State, Pos.getOffset(), Offset, Opc);
1482   };
1483   auto Invalidate = [&](const IteratorPosition &Pos) {
1484     return Pos.invalidate();
1485   };
1486   return processIteratorPositions(State, Compare, Invalidate);
1487 }
1488 
1489 ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
1490                                             SymbolRef Offset1,
1491                                             BinaryOperator::Opcode Opc1,
1492                                             SymbolRef Offset2,
1493                                             BinaryOperator::Opcode Opc2) {
1494   auto Compare = [&](const IteratorPosition &Pos) {
1495     return compare(State, Pos.getOffset(), Offset1, Opc1) &&
1496            compare(State, Pos.getOffset(), Offset2, Opc2);
1497   };
1498   auto Invalidate = [&](const IteratorPosition &Pos) {
1499     return Pos.invalidate();
1500   };
1501   return processIteratorPositions(State, Compare, Invalidate);
1502 }
1503 
1504 ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State,
1505                                              const MemRegion *Cont,
1506                                              const MemRegion *NewCont) {
1507   auto MatchCont = [&](const IteratorPosition &Pos) {
1508     return Pos.getContainer() == Cont;
1509   };
1510   auto ReAssign = [&](const IteratorPosition &Pos) {
1511     return Pos.reAssign(NewCont);
1512   };
1513   return processIteratorPositions(State, MatchCont, ReAssign);
1514 }
1515 
1516 ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State,
1517                                                    const MemRegion *Cont,
1518                                                    const MemRegion *NewCont,
1519                                                    SymbolRef Offset,
1520                                                    BinaryOperator::Opcode Opc) {
1521   auto MatchContAndCompare = [&](const IteratorPosition &Pos) {
1522     return Pos.getContainer() == Cont &&
1523     !compare(State, Pos.getOffset(), Offset, Opc);
1524   };
1525   auto ReAssign = [&](const IteratorPosition &Pos) {
1526     return Pos.reAssign(NewCont);
1527   };
1528   return processIteratorPositions(State, MatchContAndCompare, ReAssign);
1529 }
1530 
1531 // This function rebases symbolic expression `OldSym + Int` to `NewSym + Int`,
1532 // `OldSym - Int` to `NewSym - Int` and  `OldSym` to `NewSym` in any iterator
1533 // position offsets where `CondSym` is true.
1534 ProgramStateRef rebaseSymbolInIteratorPositionsIf(
1535     ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym,
1536     SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc) {
1537   auto LessThanEnd = [&](const IteratorPosition &Pos) {
1538     return compare(State, Pos.getOffset(), CondSym, Opc);
1539   };
1540   auto RebaseSymbol = [&](const IteratorPosition &Pos) {
1541     return Pos.setTo(rebaseSymbol(State, SVB, Pos.getOffset(), OldSym,
1542                                    NewSym));
1543   };
1544   return processIteratorPositions(State, LessThanEnd, RebaseSymbol);
1545 }
1546 
1547 // This function rebases symbolic expression `OldExpr + Int` to `NewExpr + Int`,
1548 // `OldExpr - Int` to `NewExpr - Int` and  `OldExpr` to `NewExpr` in expression
1549 // `OrigExpr`.
1550 SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB,
1551                        SymbolRef OrigExpr, SymbolRef OldExpr,
1552                        SymbolRef NewSym) {
1553   auto &SymMgr = SVB.getSymbolManager();
1554   auto Diff = SVB.evalBinOpNN(State, BO_Sub, nonloc::SymbolVal(OrigExpr),
1555                               nonloc::SymbolVal(OldExpr),
1556                               SymMgr.getType(OrigExpr));
1557 
1558   const auto DiffInt = Diff.getAs<nonloc::ConcreteInt>();
1559   if (!DiffInt)
1560     return OrigExpr;
1561 
1562   return SVB.evalBinOpNN(State, BO_Add, *DiffInt, nonloc::SymbolVal(NewSym),
1563                          SymMgr.getType(OrigExpr)).getAsSymbol();
1564 }
1565 
1566 } // namespace
1567 
1568 void ento::registerIteratorModeling(CheckerManager &mgr) {
1569   mgr.registerChecker<IteratorModeling>();
1570 }
1571 
1572 bool ento::shouldRegisterIteratorModeling(const LangOptions &LO) {
1573   return true;
1574 }
1575