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