]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp
Merge llvm, clang, lld, lldb, compiler-rt and libc++ r304222, and update
[FreeBSD/FreeBSD.git] / contrib / llvm / tools / clang / lib / StaticAnalyzer / Checkers / IteratorChecker.cpp
1 //===-- IteratorChecker.cpp ---------------------------------------*- C++ -*--//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // Defines a checker for using iterators outside their range (past end). Usage
11 // means here dereferencing, incrementing etc.
12 //
13 //===----------------------------------------------------------------------===//
14 //
15 // In the code, iterator can be represented as a:
16 // * type-I: typedef-ed pointer. Operations over such iterator, such as
17 //           comparisons or increments, are modeled straightforwardly by the
18 //           analyzer.
19 // * type-II: structure with its method bodies available.  Operations over such
20 //            iterator are inlined by the analyzer, and results of modeling
21 //            these operations are exposing implementation details of the
22 //            iterators, which is not necessarily helping.
23 // * type-III: completely opaque structure. Operations over such iterator are
24 //             modeled conservatively, producing conjured symbols everywhere.
25 //
26 // To handle all these types in a common way we introduce a structure called
27 // IteratorPosition which is an abstraction of the position the iterator
28 // represents using symbolic expressions. The checker handles all the
29 // operations on this structure.
30 //
31 // Additionally, depending on the circumstances, operators of types II and III
32 // can be represented as:
33 // * type-IIa, type-IIIa: conjured structure symbols - when returned by value
34 //                        from conservatively evaluated methods such as
35 //                        `.begin()`.
36 // * type-IIb, type-IIIb: memory regions of iterator-typed objects, such as
37 //                        variables or temporaries, when the iterator object is
38 //                        currently treated as an lvalue.
39 // * type-IIc, type-IIIc: compound values of iterator-typed objects, when the
40 //                        iterator object is treated as an rvalue taken of a
41 //                        particular lvalue, eg. a copy of "type-a" iterator
42 //                        object, or an iterator that existed before the
43 //                        analysis has started.
44 //
45 // To handle any of these three different representations stored in an SVal we
46 // use setter and getters functions which separate the three cases. To store
47 // them we use a pointer union of symbol and memory region.
48 //
49 // The checker works the following way: We record the past-end iterator for
50 // all containers whenever their `.end()` is called. Since the Constraint
51 // Manager cannot handle SVals we need to take over its role. We post-check
52 // equality and non-equality comparisons and propagate the position of the
53 // iterator to the other side of the comparison if it is past-end and we are in
54 // the 'equal' branch (true-branch for `==` and false-branch for `!=`).
55 //
56 // In case of type-I or type-II iterators we get a concrete integer as a result
57 // of the comparison (1 or 0) but in case of type-III we only get a Symbol. In
58 // this latter case we record the symbol and reload it in evalAssume() and do
59 // the propagation there. We also handle (maybe double) negated comparisons
60 // which are represented in the form of (x == 0 or x !=0 ) where x is the
61 // comparison itself.
62
63 #include "ClangSACheckers.h"
64 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
65 #include "clang/StaticAnalyzer/Core/Checker.h"
66 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
67 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
68
69 using namespace clang;
70 using namespace ento;
71
72 namespace {
73
74 // Abstract position of an iterator. This helps to handle all three kinds
75 // of operators in a common way by using a symbolic position.
76 struct IteratorPosition {
77 private:
78
79   // Container the iterator belongs to
80   const MemRegion *Cont;
81
82   // Abstract offset
83   SymbolRef Offset;
84
85   IteratorPosition(const MemRegion *C, SymbolRef Of)
86       : Cont(C), Offset(Of) {}
87
88 public:
89   const MemRegion *getContainer() const { return Cont; }
90   SymbolRef getOffset() const { return Offset; }
91
92   static IteratorPosition getPosition(const MemRegion *C, SymbolRef Of) {
93     return IteratorPosition(C, Of);
94   }
95
96   IteratorPosition setTo(SymbolRef NewOf) const {
97     return IteratorPosition(Cont, NewOf);
98   }
99
100   bool operator==(const IteratorPosition &X) const {
101     return Cont == X.Cont && Offset == X.Offset;
102   }
103
104   bool operator!=(const IteratorPosition &X) const {
105     return Cont != X.Cont || Offset != X.Offset;
106   }
107
108   void Profile(llvm::FoldingSetNodeID &ID) const {
109     ID.AddPointer(Cont);
110     ID.Add(Offset);
111   }
112 };
113
114 typedef llvm::PointerUnion<const MemRegion *, SymbolRef> RegionOrSymbol;
115
116 // Structure to record the symbolic end position of a container
117 struct ContainerData {
118 private:
119   SymbolRef End;
120
121   ContainerData(SymbolRef E) : End(E) {}
122
123 public:
124   static ContainerData fromEnd(SymbolRef E) {
125     return ContainerData(E);
126   }
127
128   SymbolRef getEnd() const { return End; }
129
130   ContainerData newEnd(SymbolRef E) const { return ContainerData(E); }
131
132   bool operator==(const ContainerData &X) const {
133     return End == X.End;
134   }
135
136   bool operator!=(const ContainerData &X) const {
137     return End != X.End;
138   }
139
140   void Profile(llvm::FoldingSetNodeID &ID) const {
141     ID.Add(End);
142   }
143 };
144
145 // Structure fo recording iterator comparisons. We needed to retrieve the
146 // original comparison expression in assumptions.
147 struct IteratorComparison {
148 private:
149   RegionOrSymbol Left, Right;
150   bool Equality;
151
152 public:
153   IteratorComparison(RegionOrSymbol L, RegionOrSymbol R, bool Eq)
154       : Left(L), Right(R), Equality(Eq) {}
155
156   RegionOrSymbol getLeft() const { return Left; }
157   RegionOrSymbol getRight() const { return Right; }
158   bool isEquality() const { return Equality; }
159   bool operator==(const IteratorComparison &X) const {
160     return Left == X.Left && Right == X.Right && Equality == X.Equality;
161   }
162   bool operator!=(const IteratorComparison &X) const {
163     return Left != X.Left || Right != X.Right || Equality != X.Equality;
164   }
165   void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(Equality); }
166 };
167
168 class IteratorChecker
169     : public Checker<check::PreCall, check::PostCall,
170                      check::PostStmt<MaterializeTemporaryExpr>,
171                      check::DeadSymbols,
172                      eval::Assume> {
173
174   std::unique_ptr<BugType> OutOfRangeBugType;
175
176   void handleComparison(CheckerContext &C, const SVal &RetVal, const SVal &LVal,
177                         const SVal &RVal, OverloadedOperatorKind Op) const;
178   void verifyDereference(CheckerContext &C, const SVal &Val) const;
179   void handleEnd(CheckerContext &C, const Expr *CE, const SVal &RetVal,
180                  const SVal &Cont) const;
181   void assignToContainer(CheckerContext &C, const Expr *CE, const SVal &RetVal,
182                          const MemRegion *Cont) const;
183   void reportOutOfRangeBug(const StringRef &Message, const SVal &Val,
184                            CheckerContext &C, ExplodedNode *ErrNode) const;
185
186 public:
187   IteratorChecker();
188
189   enum CheckKind {
190     CK_IteratorRangeChecker,
191     CK_NumCheckKinds
192   };
193
194   DefaultBool ChecksEnabled[CK_NumCheckKinds];
195   CheckName CheckNames[CK_NumCheckKinds];
196
197   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
198   void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
199   void checkPostStmt(const MaterializeTemporaryExpr *MTE,
200                      CheckerContext &C) const;
201   void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
202   ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond,
203                              bool Assumption) const;
204 };
205 } // namespace
206
207 REGISTER_MAP_WITH_PROGRAMSTATE(IteratorSymbolMap, SymbolRef, IteratorPosition)
208 REGISTER_MAP_WITH_PROGRAMSTATE(IteratorRegionMap, const MemRegion *,
209                                IteratorPosition)
210
211 REGISTER_MAP_WITH_PROGRAMSTATE(ContainerMap, const MemRegion *, ContainerData)
212
213 REGISTER_MAP_WITH_PROGRAMSTATE(IteratorComparisonMap, const SymExpr *,
214                                IteratorComparison)
215
216 namespace {
217
218 bool isIteratorType(const QualType &Type);
219 bool isIterator(const CXXRecordDecl *CRD);
220 bool isEndCall(const FunctionDecl *Func);
221 bool isSimpleComparisonOperator(OverloadedOperatorKind OK);
222 bool isDereferenceOperator(OverloadedOperatorKind OK);
223 BinaryOperator::Opcode getOpcode(const SymExpr *SE);
224 const RegionOrSymbol getRegionOrSymbol(const SVal &Val);
225 const ProgramStateRef processComparison(ProgramStateRef State,
226                                         RegionOrSymbol LVal,
227                                         RegionOrSymbol RVal, bool Equal);
228 const ProgramStateRef saveComparison(ProgramStateRef State,
229                                      const SymExpr *Condition, const SVal &LVal,
230                                      const SVal &RVal, bool Eq);
231 const IteratorComparison *loadComparison(ProgramStateRef State,
232                                          const SymExpr *Condition);
233 SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont);
234 ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
235                                    const SymbolRef Sym);
236 const IteratorPosition *getIteratorPosition(ProgramStateRef State,
237                                             const SVal &Val);
238 const IteratorPosition *getIteratorPosition(ProgramStateRef State,
239                                             RegionOrSymbol RegOrSym);
240 ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
241                                     const IteratorPosition &Pos);
242 ProgramStateRef setIteratorPosition(ProgramStateRef State,
243                                     RegionOrSymbol RegOrSym,
244                                     const IteratorPosition &Pos);
245 ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val);
246 ProgramStateRef adjustIteratorPosition(ProgramStateRef State,
247                                        RegionOrSymbol RegOrSym,
248                                        const IteratorPosition &Pos, bool Equal);
249 ProgramStateRef relateIteratorPositions(ProgramStateRef State,
250                                         const IteratorPosition &Pos1,
251                                         const IteratorPosition &Pos2,
252                                         bool Equal);
253 const ContainerData *getContainerData(ProgramStateRef State,
254                                       const MemRegion *Cont);
255 ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
256                                  const ContainerData &CData);
257 bool isOutOfRange(ProgramStateRef State, const IteratorPosition &Pos);
258 } // namespace
259
260 IteratorChecker::IteratorChecker() {
261   OutOfRangeBugType.reset(
262       new BugType(this, "Iterator out of range", "Misuse of STL APIs"));
263   OutOfRangeBugType->setSuppressOnSink(true);
264 }
265
266 void IteratorChecker::checkPreCall(const CallEvent &Call,
267                                    CheckerContext &C) const {
268   // Check for out of range access
269   const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
270   if (!Func)
271     return;
272
273   if (Func->isOverloadedOperator()) {
274     if (ChecksEnabled[CK_IteratorRangeChecker] &&
275                isDereferenceOperator(Func->getOverloadedOperator())) {
276       // Check for dereference of out-of-range iterators
277       if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
278         verifyDereference(C, InstCall->getCXXThisVal());
279       } else {
280         verifyDereference(C, Call.getArgSVal(0));
281       }
282     }
283   }
284 }
285
286 void IteratorChecker::checkPostCall(const CallEvent &Call,
287                                     CheckerContext &C) const {
288   // Record new iterator positions and iterator position changes
289   const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
290   if (!Func)
291     return;
292
293   if (Func->isOverloadedOperator()) {
294     const auto Op = Func->getOverloadedOperator();
295     if (isSimpleComparisonOperator(Op)) {
296       if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
297         handleComparison(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
298                          Call.getArgSVal(0), Op);
299       } else {
300         handleComparison(C, Call.getReturnValue(), Call.getArgSVal(0),
301                          Call.getArgSVal(1), Op);
302       }
303     }
304   } else {
305     const auto *OrigExpr = Call.getOriginExpr();
306     if (!OrigExpr)
307       return;
308
309     if (!isIteratorType(Call.getResultType()))
310       return;
311
312     auto State = C.getState();
313     // Already bound to container?
314     if (getIteratorPosition(State, Call.getReturnValue()))
315       return;
316
317     if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
318       if (isEndCall(Func)) {
319         handleEnd(C, OrigExpr, Call.getReturnValue(),
320                   InstCall->getCXXThisVal());
321         return;
322       }
323     }
324
325     // Copy-like and move constructors
326     if (isa<CXXConstructorCall>(&Call) && Call.getNumArgs() == 1) {
327       if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(0))) {
328         State = setIteratorPosition(State, Call.getReturnValue(), *Pos);
329         if (cast<CXXConstructorDecl>(Func)->isMoveConstructor()) {
330           State = removeIteratorPosition(State, Call.getArgSVal(0));
331         }
332         C.addTransition(State);
333         return;
334       }
335     }
336
337     // Assumption: if return value is an iterator which is not yet bound to a
338     //             container, then look for the first iterator argument, and
339     //             bind the return value to the same container. This approach
340     //             works for STL algorithms.
341     // FIXME: Add a more conservative mode
342     for (unsigned i = 0; i < Call.getNumArgs(); ++i) {
343       if (isIteratorType(Call.getArgExpr(i)->getType())) {
344         if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(i))) {
345           assignToContainer(C, OrigExpr, Call.getReturnValue(),
346                             Pos->getContainer());
347           return;
348         }
349       }
350     }
351   }
352 }
353
354 void IteratorChecker::checkPostStmt(const MaterializeTemporaryExpr *MTE,
355                                     CheckerContext &C) const {
356   /* Transfer iterator state to temporary objects */
357   auto State = C.getState();
358   const auto *LCtx = C.getLocationContext();
359   const auto *Pos =
360       getIteratorPosition(State, State->getSVal(MTE->GetTemporaryExpr(), LCtx));
361   if (!Pos)
362     return;
363   State = setIteratorPosition(State, State->getSVal(MTE, LCtx), *Pos);
364   C.addTransition(State);
365 }
366
367 void IteratorChecker::checkDeadSymbols(SymbolReaper &SR,
368                                        CheckerContext &C) const {
369   // Cleanup
370   auto State = C.getState();
371
372   auto RegionMap = State->get<IteratorRegionMap>();
373   for (const auto Reg : RegionMap) {
374     if (!SR.isLiveRegion(Reg.first)) {
375       State = State->remove<IteratorRegionMap>(Reg.first);
376     }
377   }
378
379   auto SymbolMap = State->get<IteratorSymbolMap>();
380   for (const auto Sym : SymbolMap) {
381     if (!SR.isLive(Sym.first)) {
382       State = State->remove<IteratorSymbolMap>(Sym.first);
383     }
384   }
385
386   auto ContMap = State->get<ContainerMap>();
387   for (const auto Cont : ContMap) {
388     if (!SR.isLiveRegion(Cont.first)) {
389       State = State->remove<ContainerMap>(Cont.first);
390     }
391   }
392
393   auto ComparisonMap = State->get<IteratorComparisonMap>();
394   for (const auto Comp : ComparisonMap) {
395     if (!SR.isLive(Comp.first)) {
396       State = State->remove<IteratorComparisonMap>(Comp.first);
397     }
398   }
399 }
400
401 ProgramStateRef IteratorChecker::evalAssume(ProgramStateRef State, SVal Cond,
402                                             bool Assumption) const {
403   // Load recorded comparison and transfer iterator state between sides
404   // according to comparison operator and assumption
405   const auto *SE = Cond.getAsSymExpr();
406   if (!SE)
407     return State;
408
409   auto Opc = getOpcode(SE);
410   if (Opc != BO_EQ && Opc != BO_NE)
411     return State;
412
413   bool Negated = false;
414   const auto *Comp = loadComparison(State, SE);
415   if (!Comp) {
416     // Try negated comparison, which is a SymExpr to 0 integer comparison
417     const auto *SIE = dyn_cast<SymIntExpr>(SE);
418     if (!SIE)
419       return State;
420
421     if (SIE->getRHS() != 0)
422       return State;
423
424     SE = SIE->getLHS();
425     Negated = SIE->getOpcode() == BO_EQ; // Equal to zero means negation
426     Opc = getOpcode(SE);
427     if (Opc != BO_EQ && Opc != BO_NE)
428       return State;
429
430     Comp = loadComparison(State, SE);
431     if (!Comp)
432       return State;
433   }
434
435   return processComparison(State, Comp->getLeft(), Comp->getRight(),
436                            (Comp->isEquality() == Assumption) != Negated);
437 }
438
439 void IteratorChecker::handleComparison(CheckerContext &C, const SVal &RetVal,
440                                        const SVal &LVal, const SVal &RVal,
441                                        OverloadedOperatorKind Op) const {
442   // Record the operands and the operator of the comparison for the next
443   // evalAssume, if the result is a symbolic expression. If it is a concrete
444   // value (only one branch is possible), then transfer the state between
445   // the operands according to the operator and the result
446   auto State = C.getState();
447   if (const auto *Condition = RetVal.getAsSymbolicExpression()) {
448     const auto *LPos = getIteratorPosition(State, LVal);
449     const auto *RPos = getIteratorPosition(State, RVal);
450     if (!LPos && !RPos)
451       return;
452     State = saveComparison(State, Condition, LVal, RVal, Op == OO_EqualEqual);
453     C.addTransition(State);
454   } else if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) {
455     if ((State = processComparison(
456              State, getRegionOrSymbol(LVal), getRegionOrSymbol(RVal),
457              (Op == OO_EqualEqual) == (TruthVal->getValue() != 0)))) {
458       C.addTransition(State);
459     } else {
460       C.generateSink(State, C.getPredecessor());
461     }
462   }
463 }
464
465 void IteratorChecker::verifyDereference(CheckerContext &C,
466                                         const SVal &Val) const {
467   auto State = C.getState();
468   const auto *Pos = getIteratorPosition(State, Val);
469   if (Pos && isOutOfRange(State, *Pos)) {
470     // If I do not put a tag here, some range tests will fail
471     static CheckerProgramPointTag Tag("IteratorRangeChecker",
472                                       "IteratorOutOfRange");
473     auto *N = C.generateNonFatalErrorNode(State, &Tag);
474     if (!N) {
475       return;
476     }
477     reportOutOfRangeBug("Iterator accessed outside of its range.", Val, C, N);
478   }
479 }
480
481 void IteratorChecker::handleEnd(CheckerContext &C, const Expr *CE,
482                                 const SVal &RetVal, const SVal &Cont) const {
483   const auto *ContReg = Cont.getAsRegion();
484   if (!ContReg)
485     return;
486
487   while (const auto *CBOR = ContReg->getAs<CXXBaseObjectRegion>()) {
488     ContReg = CBOR->getSuperRegion();
489   }
490
491   // If the container already has an end symbol then use it. Otherwise first
492   // create a new one.
493   auto State = C.getState();
494   auto EndSym = getContainerEnd(State, ContReg);
495   if (!EndSym) {
496     auto &SymMgr = C.getSymbolManager();
497     EndSym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
498                                   C.getASTContext().LongTy, C.blockCount());
499     State = createContainerEnd(State, ContReg, EndSym);
500   }
501   State = setIteratorPosition(State, RetVal,
502                               IteratorPosition::getPosition(ContReg, EndSym));
503   C.addTransition(State);
504 }
505
506 void IteratorChecker::assignToContainer(CheckerContext &C, const Expr *CE,
507                                         const SVal &RetVal,
508                                         const MemRegion *Cont) const {
509   while (const auto *CBOR = Cont->getAs<CXXBaseObjectRegion>()) {
510     Cont = CBOR->getSuperRegion();
511   }
512
513   auto State = C.getState();
514   auto &SymMgr = C.getSymbolManager();
515   auto Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
516                                   C.getASTContext().LongTy, C.blockCount());
517   State = setIteratorPosition(State, RetVal,
518                               IteratorPosition::getPosition(Cont, Sym));
519   C.addTransition(State);
520 }
521
522 void IteratorChecker::reportOutOfRangeBug(const StringRef &Message,
523                                           const SVal &Val, CheckerContext &C,
524                                           ExplodedNode *ErrNode) const {
525   auto R = llvm::make_unique<BugReport>(*OutOfRangeBugType, Message, ErrNode);
526   R->markInteresting(Val);
527   C.emitReport(std::move(R));
528 }
529
530 namespace {
531
532 bool isGreaterOrEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
533 bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
534              BinaryOperator::Opcode Opc);
535
536 bool isIteratorType(const QualType &Type) {
537   if (Type->isPointerType())
538     return true;
539
540   const auto *CRD = Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
541   return isIterator(CRD);
542 }
543
544 bool isIterator(const CXXRecordDecl *CRD) {
545   if (!CRD)
546     return false;
547
548   const auto Name = CRD->getName();
549   if (!(Name.endswith_lower("iterator") || Name.endswith_lower("iter") ||
550         Name.endswith_lower("it")))
551     return false;
552
553   bool HasCopyCtor = false, HasCopyAssign = true, HasDtor = false,
554        HasPreIncrOp = false, HasPostIncrOp = false, HasDerefOp = false;
555   for (const auto *Method : CRD->methods()) {
556     if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(Method)) {
557       if (Ctor->isCopyConstructor()) {
558         HasCopyCtor = !Ctor->isDeleted() && Ctor->getAccess() == AS_public;
559       }
560       continue;
561     }
562     if (const auto *Dtor = dyn_cast<CXXDestructorDecl>(Method)) {
563       HasDtor = !Dtor->isDeleted() && Dtor->getAccess() == AS_public;
564       continue;
565     }
566     if (Method->isCopyAssignmentOperator()) {
567       HasCopyAssign = !Method->isDeleted() && Method->getAccess() == AS_public;
568       continue;
569     }
570     if (!Method->isOverloadedOperator())
571       continue;
572     const auto OPK = Method->getOverloadedOperator();
573     if (OPK == OO_PlusPlus) {
574       HasPreIncrOp = HasPreIncrOp || (Method->getNumParams() == 0);
575       HasPostIncrOp = HasPostIncrOp || (Method->getNumParams() == 1);
576       continue;
577     }
578     if (OPK == OO_Star) {
579       HasDerefOp = (Method->getNumParams() == 0);
580       continue;
581     }
582   }
583
584   return HasCopyCtor && HasCopyAssign && HasDtor && HasPreIncrOp &&
585          HasPostIncrOp && HasDerefOp;
586 }
587
588 bool isEndCall(const FunctionDecl *Func) {
589   const auto *IdInfo = Func->getIdentifier();
590   if (!IdInfo)
591     return false;
592   return IdInfo->getName().endswith_lower("end");
593 }
594
595 bool isSimpleComparisonOperator(OverloadedOperatorKind OK) {
596   return OK == OO_EqualEqual || OK == OO_ExclaimEqual;
597 }
598
599 bool isDereferenceOperator(OverloadedOperatorKind OK) {
600   return OK == OO_Star || OK == OO_Arrow || OK == OO_ArrowStar ||
601          OK == OO_Subscript;
602 }
603
604 BinaryOperator::Opcode getOpcode(const SymExpr *SE) {
605   if (const auto *BSE = dyn_cast<BinarySymExpr>(SE)) {
606     return BSE->getOpcode();
607   } else if (const auto *SC = dyn_cast<SymbolConjured>(SE)) {
608     const auto *COE = dyn_cast<CXXOperatorCallExpr>(SC->getStmt());
609     if (!COE)
610       return BO_Comma; // Extremal value, neither EQ nor NE
611     if (COE->getOperator() == OO_EqualEqual) {
612       return BO_EQ;
613     } else if (COE->getOperator() == OO_ExclaimEqual) {
614       return BO_NE;
615     }
616     return BO_Comma; // Extremal value, neither EQ nor NE
617   }
618   return BO_Comma; // Extremal value, neither EQ nor NE
619 }
620
621 const RegionOrSymbol getRegionOrSymbol(const SVal &Val) {
622   if (const auto Reg = Val.getAsRegion()) {
623     return Reg;
624   } else if (const auto Sym = Val.getAsSymbol()) {
625     return Sym;
626   } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
627     return LCVal->getRegion();
628   }
629   return RegionOrSymbol();
630 }
631
632 const ProgramStateRef processComparison(ProgramStateRef State,
633                                         RegionOrSymbol LVal,
634                                         RegionOrSymbol RVal, bool Equal) {
635   const auto *LPos = getIteratorPosition(State, LVal);
636   const auto *RPos = getIteratorPosition(State, RVal);
637   if (LPos && !RPos) {
638     State = adjustIteratorPosition(State, RVal, *LPos, Equal);
639   } else if (!LPos && RPos) {
640     State = adjustIteratorPosition(State, LVal, *RPos, Equal);
641   } else if (LPos && RPos) {
642     State = relateIteratorPositions(State, *LPos, *RPos, Equal);
643   }
644   return State;
645 }
646
647 const ProgramStateRef saveComparison(ProgramStateRef State,
648                                      const SymExpr *Condition, const SVal &LVal,
649                                      const SVal &RVal, bool Eq) {
650   const auto Left = getRegionOrSymbol(LVal);
651   const auto Right = getRegionOrSymbol(RVal);
652   if (!Left || !Right)
653     return State;
654   return State->set<IteratorComparisonMap>(Condition,
655                                            IteratorComparison(Left, Right, Eq));
656 }
657
658 const IteratorComparison *loadComparison(ProgramStateRef State,
659                                          const SymExpr *Condition) {
660   return State->get<IteratorComparisonMap>(Condition);
661 }
662
663 SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont) {
664   const auto *CDataPtr = getContainerData(State, Cont);
665   if (!CDataPtr)
666     return nullptr;
667
668   return CDataPtr->getEnd();
669 }
670
671 ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
672                                    const SymbolRef Sym) {
673   // Only create if it does not exist
674   const auto *CDataPtr = getContainerData(State, Cont);
675   if (CDataPtr) {
676     if (CDataPtr->getEnd()) {
677       return State;
678     } else {
679       const auto CData = CDataPtr->newEnd(Sym);
680       return setContainerData(State, Cont, CData);
681     }
682   } else {
683     const auto CData = ContainerData::fromEnd(Sym);
684     return setContainerData(State, Cont, CData);
685   }
686 }
687
688 const ContainerData *getContainerData(ProgramStateRef State,
689                                       const MemRegion *Cont) {
690   return State->get<ContainerMap>(Cont);
691 }
692
693 ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
694                                  const ContainerData &CData) {
695   return State->set<ContainerMap>(Cont, CData);
696 }
697
698 const IteratorPosition *getIteratorPosition(ProgramStateRef State,
699                                             const SVal &Val) {
700   if (const auto Reg = Val.getAsRegion()) {
701     return State->get<IteratorRegionMap>(Reg);
702   } else if (const auto Sym = Val.getAsSymbol()) {
703     return State->get<IteratorSymbolMap>(Sym);
704   } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
705     return State->get<IteratorRegionMap>(LCVal->getRegion());
706   }
707   return nullptr;
708 }
709
710 const IteratorPosition *getIteratorPosition(ProgramStateRef State,
711                                             RegionOrSymbol RegOrSym) {
712   if (RegOrSym.is<const MemRegion *>()) {
713     return State->get<IteratorRegionMap>(RegOrSym.get<const MemRegion *>());
714   } else if (RegOrSym.is<SymbolRef>()) {
715     return State->get<IteratorSymbolMap>(RegOrSym.get<SymbolRef>());
716   }
717   return nullptr;
718 }
719
720 ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
721                                     const IteratorPosition &Pos) {
722   if (const auto Reg = Val.getAsRegion()) {
723     return State->set<IteratorRegionMap>(Reg, Pos);
724   } else if (const auto Sym = Val.getAsSymbol()) {
725     return State->set<IteratorSymbolMap>(Sym, Pos);
726   } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
727     return State->set<IteratorRegionMap>(LCVal->getRegion(), Pos);
728   }
729   return nullptr;
730 }
731
732 ProgramStateRef setIteratorPosition(ProgramStateRef State,
733                                     RegionOrSymbol RegOrSym,
734                                     const IteratorPosition &Pos) {
735   if (RegOrSym.is<const MemRegion *>()) {
736     return State->set<IteratorRegionMap>(RegOrSym.get<const MemRegion *>(),
737                                          Pos);
738   } else if (RegOrSym.is<SymbolRef>()) {
739     return State->set<IteratorSymbolMap>(RegOrSym.get<SymbolRef>(), Pos);
740   }
741   return nullptr;
742 }
743
744 ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val) {
745   if (const auto Reg = Val.getAsRegion()) {
746     return State->remove<IteratorRegionMap>(Reg);
747   } else if (const auto Sym = Val.getAsSymbol()) {
748     return State->remove<IteratorSymbolMap>(Sym);
749   } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
750     return State->remove<IteratorRegionMap>(LCVal->getRegion());
751   }
752   return nullptr;
753 }
754
755 ProgramStateRef adjustIteratorPosition(ProgramStateRef State,
756                                        RegionOrSymbol RegOrSym,
757                                        const IteratorPosition &Pos,
758                                        bool Equal) {
759   if (Equal) {
760     return setIteratorPosition(State, RegOrSym, Pos);
761   } else {
762     return State;
763   }
764 }
765
766 ProgramStateRef relateIteratorPositions(ProgramStateRef State,
767                                         const IteratorPosition &Pos1,
768                                         const IteratorPosition &Pos2,
769                                         bool Equal) {
770   // Try to compare them and get a defined value
771   auto &SVB = State->getStateManager().getSValBuilder();
772   const auto comparison =
773       SVB.evalBinOp(State, BO_EQ, nonloc::SymbolVal(Pos1.getOffset()),
774                     nonloc::SymbolVal(Pos2.getOffset()), SVB.getConditionType())
775           .getAs<DefinedSVal>();
776   if (comparison) {
777     return State->assume(*comparison, Equal);
778   }
779
780   return State;
781 }
782
783 bool isOutOfRange(ProgramStateRef State, const IteratorPosition &Pos) {
784   const auto *Cont = Pos.getContainer();
785   const auto *CData = getContainerData(State, Cont);
786   if (!CData)
787     return false;
788
789   // Out of range means less than the begin symbol or greater or equal to the
790   // end symbol.
791
792   const auto End = CData->getEnd();
793   if (End) {
794     if (isGreaterOrEqual(State, Pos.getOffset(), End)) {
795       return true;
796     }
797   }
798
799   return false;
800 }
801
802 bool isGreaterOrEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
803   return compare(State, Sym1, Sym2, BO_GE);
804 }
805
806 bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
807              BinaryOperator::Opcode Opc) {
808   auto &SMgr = State->getStateManager();
809   auto &SVB = SMgr.getSValBuilder();
810
811   const auto comparison =
812       SVB.evalBinOp(State, Opc, nonloc::SymbolVal(Sym1),
813                     nonloc::SymbolVal(Sym2), SVB.getConditionType())
814           .getAs<DefinedSVal>();
815
816   if(comparison) {
817     return !!State->assume(*comparison, true);
818   }
819
820   return false;
821 }
822
823 } // namespace
824
825 #define REGISTER_CHECKER(name)                                                 \
826   void ento::register##name(CheckerManager &Mgr) {                             \
827     auto *checker = Mgr.registerChecker<IteratorChecker>();                    \
828     checker->ChecksEnabled[IteratorChecker::CK_##name] = true;                 \
829     checker->CheckNames[IteratorChecker::CK_##name] =                          \
830         Mgr.getCurrentCheckName();                                             \
831   }
832
833 REGISTER_CHECKER(IteratorRangeChecker)