| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 1 | //===-- IteratorChecker.cpp ---------------------------------------*- C++ -*--// | 
|  | 2 | // | 
| Chandler Carruth | 2946cd7 | 2019-01-19 08:50:56 +0000 | [diff] [blame] | 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 | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 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 | // | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 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 `!=`). | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 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 | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 59 | // which are represented in the form of (x == 0 or x != 0) where x is the | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 60 | // comparison itself. | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 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. | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 67 |  | 
| Kristof Umann | 76a2150 | 2018-12-15 16:23:51 +0000 | [diff] [blame] | 68 | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" | 
| Adam Balogh | 2cfbe93 | 2018-08-28 08:41:15 +0000 | [diff] [blame] | 69 | #include "clang/AST/DeclTemplate.h" | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 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" | 
| Csaba Dabis | e4bf456 | 2019-08-22 00:36:42 +0000 | [diff] [blame] | 74 | #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 75 |  | 
| Adam Balogh | 2cfbe93 | 2018-08-28 08:41:15 +0000 | [diff] [blame] | 76 | #include <utility> | 
|  | 77 |  | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 78 | using namespace clang; | 
|  | 79 | using namespace ento; | 
|  | 80 |  | 
|  | 81 | namespace { | 
|  | 82 |  | 
|  | 83 | // Abstract position of an iterator. This helps to handle all three kinds | 
|  | 84 | // of operators in a common way by using a symbolic position. | 
|  | 85 | struct IteratorPosition { | 
|  | 86 | private: | 
|  | 87 |  | 
|  | 88 | // Container the iterator belongs to | 
|  | 89 | const MemRegion *Cont; | 
|  | 90 |  | 
| Adam Balogh | 2cfbe93 | 2018-08-28 08:41:15 +0000 | [diff] [blame] | 91 | // Whether iterator is valid | 
|  | 92 | const bool Valid; | 
|  | 93 |  | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 94 | // Abstract offset | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 95 | const SymbolRef Offset; | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 96 |  | 
| Adam Balogh | 2cfbe93 | 2018-08-28 08:41:15 +0000 | [diff] [blame] | 97 | IteratorPosition(const MemRegion *C, bool V, SymbolRef Of) | 
|  | 98 | : Cont(C), Valid(V), Offset(Of) {} | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 99 |  | 
|  | 100 | public: | 
|  | 101 | const MemRegion *getContainer() const { return Cont; } | 
| Adam Balogh | 2cfbe93 | 2018-08-28 08:41:15 +0000 | [diff] [blame] | 102 | bool isValid() const { return Valid; } | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 103 | SymbolRef getOffset() const { return Offset; } | 
|  | 104 |  | 
| Adam Balogh | 2cfbe93 | 2018-08-28 08:41:15 +0000 | [diff] [blame] | 105 | IteratorPosition invalidate() const { | 
|  | 106 | return IteratorPosition(Cont, false, Offset); | 
|  | 107 | } | 
|  | 108 |  | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 109 | static IteratorPosition getPosition(const MemRegion *C, SymbolRef Of) { | 
| Adam Balogh | 2cfbe93 | 2018-08-28 08:41:15 +0000 | [diff] [blame] | 110 | return IteratorPosition(C, true, Of); | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 111 | } | 
|  | 112 |  | 
|  | 113 | IteratorPosition setTo(SymbolRef NewOf) const { | 
| Adam Balogh | 2cfbe93 | 2018-08-28 08:41:15 +0000 | [diff] [blame] | 114 | return IteratorPosition(Cont, Valid, NewOf); | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 115 | } | 
|  | 116 |  | 
| Adam Balogh | 6b23b1a | 2018-09-10 09:04:27 +0000 | [diff] [blame] | 117 | IteratorPosition reAssign(const MemRegion *NewCont) const { | 
|  | 118 | return IteratorPosition(NewCont, Valid, Offset); | 
|  | 119 | } | 
|  | 120 |  | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 121 | bool operator==(const IteratorPosition &X) const { | 
| Adam Balogh | 2cfbe93 | 2018-08-28 08:41:15 +0000 | [diff] [blame] | 122 | return Cont == X.Cont && Valid == X.Valid && Offset == X.Offset; | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 123 | } | 
|  | 124 |  | 
|  | 125 | bool operator!=(const IteratorPosition &X) const { | 
| Adam Balogh | 2cfbe93 | 2018-08-28 08:41:15 +0000 | [diff] [blame] | 126 | return Cont != X.Cont || Valid != X.Valid || Offset != X.Offset; | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 127 | } | 
|  | 128 |  | 
|  | 129 | void Profile(llvm::FoldingSetNodeID &ID) const { | 
|  | 130 | ID.AddPointer(Cont); | 
| Adam Balogh | 2cfbe93 | 2018-08-28 08:41:15 +0000 | [diff] [blame] | 131 | ID.AddInteger(Valid); | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 132 | ID.Add(Offset); | 
|  | 133 | } | 
|  | 134 | }; | 
|  | 135 |  | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 136 | // Structure to record the symbolic begin and end position of a container | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 137 | struct ContainerData { | 
|  | 138 | private: | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 139 | const SymbolRef Begin, End; | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 140 |  | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 141 | ContainerData(SymbolRef B, SymbolRef E) : Begin(B), End(E) {} | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 142 |  | 
|  | 143 | public: | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 144 | static ContainerData fromBegin(SymbolRef B) { | 
|  | 145 | return ContainerData(B, nullptr); | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 146 | } | 
|  | 147 |  | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 148 | static ContainerData fromEnd(SymbolRef E) { | 
|  | 149 | return ContainerData(nullptr, E); | 
|  | 150 | } | 
|  | 151 |  | 
|  | 152 | SymbolRef getBegin() const { return Begin; } | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 153 | SymbolRef getEnd() const { return End; } | 
|  | 154 |  | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 155 | ContainerData newBegin(SymbolRef B) const { return ContainerData(B, End); } | 
|  | 156 |  | 
|  | 157 | ContainerData newEnd(SymbolRef E) const { return ContainerData(Begin, E); } | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 158 |  | 
|  | 159 | bool operator==(const ContainerData &X) const { | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 160 | return Begin == X.Begin && End == X.End; | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 161 | } | 
|  | 162 |  | 
|  | 163 | bool operator!=(const ContainerData &X) const { | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 164 | return Begin != X.Begin || End != X.End; | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 165 | } | 
|  | 166 |  | 
|  | 167 | void Profile(llvm::FoldingSetNodeID &ID) const { | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 168 | ID.Add(Begin); | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 169 | ID.Add(End); | 
|  | 170 | } | 
|  | 171 | }; | 
|  | 172 |  | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 173 | class IteratorChecker | 
|  | 174 | : public Checker<check::PreCall, check::PostCall, | 
| Adam Balogh | 2e7cb34 | 2018-09-10 09:07:47 +0000 | [diff] [blame] | 175 | check::PostStmt<MaterializeTemporaryExpr>, check::Bind, | 
| Adam Balogh | 54976e7 | 2019-04-23 07:15:55 +0000 | [diff] [blame] | 176 | check::LiveSymbols, check::DeadSymbols> { | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 177 |  | 
|  | 178 | std::unique_ptr<BugType> OutOfRangeBugType; | 
| Adam Balogh | 21583b7 | 2018-09-10 09:03:22 +0000 | [diff] [blame] | 179 | std::unique_ptr<BugType> MismatchedBugType; | 
| Adam Balogh | 2cfbe93 | 2018-08-28 08:41:15 +0000 | [diff] [blame] | 180 | std::unique_ptr<BugType> InvalidatedBugType; | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 181 |  | 
| Adam Balogh | 54976e7 | 2019-04-23 07:15:55 +0000 | [diff] [blame] | 182 | void handleComparison(CheckerContext &C, const Expr *CE, const SVal &RetVal, | 
|  | 183 | const SVal &LVal, const SVal &RVal, | 
|  | 184 | OverloadedOperatorKind Op) const; | 
|  | 185 | void processComparison(CheckerContext &C, ProgramStateRef State, | 
|  | 186 | SymbolRef Sym1, SymbolRef Sym2, const SVal &RetVal, | 
|  | 187 | OverloadedOperatorKind Op) const; | 
| Adam Balogh | 2cfbe93 | 2018-08-28 08:41:15 +0000 | [diff] [blame] | 188 | void verifyAccess(CheckerContext &C, const SVal &Val) const; | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 189 | void verifyDereference(CheckerContext &C, const SVal &Val) const; | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 190 | void handleIncrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter, | 
|  | 191 | bool Postfix) const; | 
|  | 192 | void handleDecrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter, | 
|  | 193 | bool Postfix) const; | 
|  | 194 | void handleRandomIncrOrDecr(CheckerContext &C, OverloadedOperatorKind Op, | 
|  | 195 | const SVal &RetVal, const SVal &LHS, | 
|  | 196 | const SVal &RHS) const; | 
|  | 197 | void handleBegin(CheckerContext &C, const Expr *CE, const SVal &RetVal, | 
|  | 198 | const SVal &Cont) const; | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 199 | void handleEnd(CheckerContext &C, const Expr *CE, const SVal &RetVal, | 
|  | 200 | const SVal &Cont) const; | 
|  | 201 | void assignToContainer(CheckerContext &C, const Expr *CE, const SVal &RetVal, | 
|  | 202 | const MemRegion *Cont) const; | 
| Adam Balogh | 6b23b1a | 2018-09-10 09:04:27 +0000 | [diff] [blame] | 203 | void handleAssign(CheckerContext &C, const SVal &Cont, | 
|  | 204 | const Expr *CE = nullptr, | 
|  | 205 | const SVal &OldCont = UndefinedVal()) const; | 
| Adam Balogh | 2e7cb34 | 2018-09-10 09:07:47 +0000 | [diff] [blame] | 206 | void handleClear(CheckerContext &C, const SVal &Cont) const; | 
| Adam Balogh | 9a48ba6 | 2018-09-10 09:06:31 +0000 | [diff] [blame] | 207 | void handlePushBack(CheckerContext &C, const SVal &Cont) const; | 
|  | 208 | void handlePopBack(CheckerContext &C, const SVal &Cont) const; | 
|  | 209 | void handlePushFront(CheckerContext &C, const SVal &Cont) const; | 
|  | 210 | void handlePopFront(CheckerContext &C, const SVal &Cont) const; | 
| Adam Balogh | 2e7cb34 | 2018-09-10 09:07:47 +0000 | [diff] [blame] | 211 | void handleInsert(CheckerContext &C, const SVal &Iter) const; | 
|  | 212 | void handleErase(CheckerContext &C, const SVal &Iter) const; | 
|  | 213 | void handleErase(CheckerContext &C, const SVal &Iter1, | 
|  | 214 | const SVal &Iter2) const; | 
|  | 215 | void handleEraseAfter(CheckerContext &C, const SVal &Iter) const; | 
|  | 216 | void handleEraseAfter(CheckerContext &C, const SVal &Iter1, | 
|  | 217 | const SVal &Iter2) const; | 
| Adam Balogh | d5bd3f6 | 2018-12-04 10:27:27 +0000 | [diff] [blame] | 218 | void verifyIncrement(CheckerContext &C, const SVal &Iter) const; | 
|  | 219 | void verifyDecrement(CheckerContext &C, const SVal &Iter) const; | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 220 | void verifyRandomIncrOrDecr(CheckerContext &C, OverloadedOperatorKind Op, | 
| Adam Balogh | d5bd3f6 | 2018-12-04 10:27:27 +0000 | [diff] [blame] | 221 | const SVal &LHS, const SVal &RHS) const; | 
| Adam Balogh | 2e7cb34 | 2018-09-10 09:07:47 +0000 | [diff] [blame] | 222 | void verifyMatch(CheckerContext &C, const SVal &Iter, | 
|  | 223 | const MemRegion *Cont) const; | 
| Adam Balogh | 21583b7 | 2018-09-10 09:03:22 +0000 | [diff] [blame] | 224 | void verifyMatch(CheckerContext &C, const SVal &Iter1, | 
|  | 225 | const SVal &Iter2) const; | 
| Adam Balogh | d5bd3f6 | 2018-12-04 10:27:27 +0000 | [diff] [blame] | 226 | IteratorPosition advancePosition(CheckerContext &C, OverloadedOperatorKind Op, | 
|  | 227 | const IteratorPosition &Pos, | 
|  | 228 | const SVal &Distance) const; | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 229 | void reportOutOfRangeBug(const StringRef &Message, const SVal &Val, | 
|  | 230 | CheckerContext &C, ExplodedNode *ErrNode) const; | 
| Adam Balogh | 21583b7 | 2018-09-10 09:03:22 +0000 | [diff] [blame] | 231 | void reportMismatchedBug(const StringRef &Message, const SVal &Val1, | 
|  | 232 | const SVal &Val2, CheckerContext &C, | 
|  | 233 | ExplodedNode *ErrNode) const; | 
| Adam Balogh | 3659f7a | 2018-09-10 09:05:31 +0000 | [diff] [blame] | 234 | void reportMismatchedBug(const StringRef &Message, const SVal &Val, | 
|  | 235 | const MemRegion *Reg, CheckerContext &C, | 
|  | 236 | ExplodedNode *ErrNode) const; | 
| Adam Balogh | 2cfbe93 | 2018-08-28 08:41:15 +0000 | [diff] [blame] | 237 | void reportInvalidatedBug(const StringRef &Message, const SVal &Val, | 
|  | 238 | CheckerContext &C, ExplodedNode *ErrNode) const; | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 239 |  | 
|  | 240 | public: | 
|  | 241 | IteratorChecker(); | 
|  | 242 |  | 
|  | 243 | enum CheckKind { | 
|  | 244 | CK_IteratorRangeChecker, | 
| Adam Balogh | 21583b7 | 2018-09-10 09:03:22 +0000 | [diff] [blame] | 245 | CK_MismatchedIteratorChecker, | 
| Adam Balogh | 2cfbe93 | 2018-08-28 08:41:15 +0000 | [diff] [blame] | 246 | CK_InvalidatedIteratorChecker, | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 247 | CK_NumCheckKinds | 
|  | 248 | }; | 
|  | 249 |  | 
|  | 250 | DefaultBool ChecksEnabled[CK_NumCheckKinds]; | 
|  | 251 | CheckName CheckNames[CK_NumCheckKinds]; | 
|  | 252 |  | 
|  | 253 | void checkPreCall(const CallEvent &Call, CheckerContext &C) const; | 
|  | 254 | void checkPostCall(const CallEvent &Call, CheckerContext &C) const; | 
| Adam Balogh | 2e7cb34 | 2018-09-10 09:07:47 +0000 | [diff] [blame] | 255 | void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const; | 
|  | 256 | void checkPostStmt(const CXXConstructExpr *CCE, CheckerContext &C) const; | 
|  | 257 | void checkPostStmt(const DeclStmt *DS, CheckerContext &C) const; | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 258 | void checkPostStmt(const MaterializeTemporaryExpr *MTE, | 
|  | 259 | CheckerContext &C) const; | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 260 | void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const; | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 261 | void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 262 | }; | 
|  | 263 | } // namespace | 
|  | 264 |  | 
|  | 265 | REGISTER_MAP_WITH_PROGRAMSTATE(IteratorSymbolMap, SymbolRef, IteratorPosition) | 
|  | 266 | REGISTER_MAP_WITH_PROGRAMSTATE(IteratorRegionMap, const MemRegion *, | 
|  | 267 | IteratorPosition) | 
|  | 268 |  | 
|  | 269 | REGISTER_MAP_WITH_PROGRAMSTATE(ContainerMap, const MemRegion *, ContainerData) | 
|  | 270 |  | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 271 | namespace { | 
|  | 272 |  | 
|  | 273 | bool isIteratorType(const QualType &Type); | 
|  | 274 | bool isIterator(const CXXRecordDecl *CRD); | 
| Adam Balogh | 3659f7a | 2018-09-10 09:05:31 +0000 | [diff] [blame] | 275 | bool isComparisonOperator(OverloadedOperatorKind OK); | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 276 | bool isBeginCall(const FunctionDecl *Func); | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 277 | bool isEndCall(const FunctionDecl *Func); | 
| Adam Balogh | 2e7cb34 | 2018-09-10 09:07:47 +0000 | [diff] [blame] | 278 | bool isAssignCall(const FunctionDecl *Func); | 
|  | 279 | bool isClearCall(const FunctionDecl *Func); | 
| Adam Balogh | 9a48ba6 | 2018-09-10 09:06:31 +0000 | [diff] [blame] | 280 | bool isPushBackCall(const FunctionDecl *Func); | 
|  | 281 | bool isEmplaceBackCall(const FunctionDecl *Func); | 
|  | 282 | bool isPopBackCall(const FunctionDecl *Func); | 
|  | 283 | bool isPushFrontCall(const FunctionDecl *Func); | 
|  | 284 | bool isEmplaceFrontCall(const FunctionDecl *Func); | 
|  | 285 | bool isPopFrontCall(const FunctionDecl *Func); | 
| Adam Balogh | 2e7cb34 | 2018-09-10 09:07:47 +0000 | [diff] [blame] | 286 | bool isInsertCall(const FunctionDecl *Func); | 
|  | 287 | bool isEraseCall(const FunctionDecl *Func); | 
|  | 288 | bool isEraseAfterCall(const FunctionDecl *Func); | 
|  | 289 | bool isEmplaceCall(const FunctionDecl *Func); | 
| Adam Balogh | 2cfbe93 | 2018-08-28 08:41:15 +0000 | [diff] [blame] | 290 | bool isAssignmentOperator(OverloadedOperatorKind OK); | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 291 | bool isSimpleComparisonOperator(OverloadedOperatorKind OK); | 
| Adam Balogh | 2cfbe93 | 2018-08-28 08:41:15 +0000 | [diff] [blame] | 292 | bool isAccessOperator(OverloadedOperatorKind OK); | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 293 | bool isDereferenceOperator(OverloadedOperatorKind OK); | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 294 | bool isIncrementOperator(OverloadedOperatorKind OK); | 
|  | 295 | bool isDecrementOperator(OverloadedOperatorKind OK); | 
|  | 296 | bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK); | 
| Adam Balogh | 9a48ba6 | 2018-09-10 09:06:31 +0000 | [diff] [blame] | 297 | bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg); | 
|  | 298 | bool frontModifiable(ProgramStateRef State, const MemRegion *Reg); | 
|  | 299 | bool backModifiable(ProgramStateRef State, const MemRegion *Reg); | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 300 | SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont); | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 301 | SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont); | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 302 | ProgramStateRef createContainerBegin(ProgramStateRef State, | 
| Adam Balogh | 33160c4 | 2019-05-20 11:04:27 +0000 | [diff] [blame] | 303 | const MemRegion *Cont, const Expr *E, | 
|  | 304 | QualType T, const LocationContext *LCtx, | 
|  | 305 | unsigned BlockCount); | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 306 | ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont, | 
| Adam Balogh | 33160c4 | 2019-05-20 11:04:27 +0000 | [diff] [blame] | 307 | const Expr *E, QualType T, | 
|  | 308 | const LocationContext *LCtx, | 
|  | 309 | unsigned BlockCount); | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 310 | const IteratorPosition *getIteratorPosition(ProgramStateRef State, | 
|  | 311 | const SVal &Val); | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 312 | ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val, | 
|  | 313 | const IteratorPosition &Pos); | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 314 | ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val); | 
| Adam Balogh | 54976e7 | 2019-04-23 07:15:55 +0000 | [diff] [blame] | 315 | ProgramStateRef assumeNoOverflow(ProgramStateRef State, SymbolRef Sym, | 
|  | 316 | long Scale); | 
| Adam Balogh | 2cfbe93 | 2018-08-28 08:41:15 +0000 | [diff] [blame] | 317 | ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State, | 
|  | 318 | const MemRegion *Cont); | 
| Adam Balogh | 2e7cb34 | 2018-09-10 09:07:47 +0000 | [diff] [blame] | 319 | ProgramStateRef | 
|  | 320 | invalidateAllIteratorPositionsExcept(ProgramStateRef State, | 
|  | 321 | const MemRegion *Cont, SymbolRef Offset, | 
|  | 322 | BinaryOperator::Opcode Opc); | 
| Adam Balogh | 9a48ba6 | 2018-09-10 09:06:31 +0000 | [diff] [blame] | 323 | ProgramStateRef invalidateIteratorPositions(ProgramStateRef State, | 
|  | 324 | SymbolRef Offset, | 
|  | 325 | BinaryOperator::Opcode Opc); | 
| Adam Balogh | 2e7cb34 | 2018-09-10 09:07:47 +0000 | [diff] [blame] | 326 | ProgramStateRef invalidateIteratorPositions(ProgramStateRef State, | 
|  | 327 | SymbolRef Offset1, | 
|  | 328 | BinaryOperator::Opcode Opc1, | 
|  | 329 | SymbolRef Offset2, | 
|  | 330 | BinaryOperator::Opcode Opc2); | 
| Adam Balogh | 6b23b1a | 2018-09-10 09:04:27 +0000 | [diff] [blame] | 331 | ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State, | 
|  | 332 | const MemRegion *Cont, | 
|  | 333 | const MemRegion *NewCont); | 
|  | 334 | ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State, | 
|  | 335 | const MemRegion *Cont, | 
|  | 336 | const MemRegion *NewCont, | 
|  | 337 | SymbolRef Offset, | 
|  | 338 | BinaryOperator::Opcode Opc); | 
|  | 339 | ProgramStateRef rebaseSymbolInIteratorPositionsIf( | 
|  | 340 | ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym, | 
|  | 341 | SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc); | 
| Adam Balogh | 54976e7 | 2019-04-23 07:15:55 +0000 | [diff] [blame] | 342 | ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1, | 
|  | 343 | SymbolRef Sym2, bool Equal); | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 344 | const ContainerData *getContainerData(ProgramStateRef State, | 
|  | 345 | const MemRegion *Cont); | 
|  | 346 | ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont, | 
|  | 347 | const ContainerData &CData); | 
| Adam Balogh | a692120 | 2018-07-30 08:52:21 +0000 | [diff] [blame] | 348 | bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont); | 
| Adam Balogh | 21583b7 | 2018-09-10 09:03:22 +0000 | [diff] [blame] | 349 | bool isBoundThroughLazyCompoundVal(const Environment &Env, | 
|  | 350 | const MemRegion *Reg); | 
| Adam Balogh | d5bd3f6 | 2018-12-04 10:27:27 +0000 | [diff] [blame] | 351 | bool isPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos); | 
|  | 352 | bool isAheadOfRange(ProgramStateRef State, const IteratorPosition &Pos); | 
|  | 353 | bool isBehindPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos); | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 354 | bool isZero(ProgramStateRef State, const NonLoc &Val); | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 355 | } // namespace | 
|  | 356 |  | 
|  | 357 | IteratorChecker::IteratorChecker() { | 
|  | 358 | OutOfRangeBugType.reset( | 
| Adam Balogh | 12f5c7f | 2019-08-29 09:35:47 +0000 | [diff] [blame] | 359 | new BugType(this, "Iterator out of range", "Misuse of STL APIs")); | 
| Adam Balogh | 21583b7 | 2018-09-10 09:03:22 +0000 | [diff] [blame] | 360 | MismatchedBugType.reset( | 
| George Karpenkov | 2c2d0b6 | 2019-01-18 19:24:55 +0000 | [diff] [blame] | 361 | new BugType(this, "Iterator(s) mismatched", "Misuse of STL APIs", | 
|  | 362 | /*SuppressOnSink=*/true)); | 
| Adam Balogh | 2cfbe93 | 2018-08-28 08:41:15 +0000 | [diff] [blame] | 363 | InvalidatedBugType.reset( | 
| Adam Balogh | 12f5c7f | 2019-08-29 09:35:47 +0000 | [diff] [blame] | 364 | new BugType(this, "Iterator invalidated", "Misuse of STL APIs")); | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 365 | } | 
|  | 366 |  | 
|  | 367 | void IteratorChecker::checkPreCall(const CallEvent &Call, | 
|  | 368 | CheckerContext &C) const { | 
| Adam Balogh | 2cfbe93 | 2018-08-28 08:41:15 +0000 | [diff] [blame] | 369 | // Check for out of range access or access of invalidated position and | 
|  | 370 | // iterator mismatches | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 371 | const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); | 
|  | 372 | if (!Func) | 
|  | 373 | return; | 
|  | 374 |  | 
|  | 375 | if (Func->isOverloadedOperator()) { | 
| Adam Balogh | 2cfbe93 | 2018-08-28 08:41:15 +0000 | [diff] [blame] | 376 | if (ChecksEnabled[CK_InvalidatedIteratorChecker] && | 
|  | 377 | isAccessOperator(Func->getOverloadedOperator())) { | 
|  | 378 | // Check for any kind of access of invalidated iterator positions | 
|  | 379 | if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { | 
|  | 380 | verifyAccess(C, InstCall->getCXXThisVal()); | 
|  | 381 | } else { | 
|  | 382 | verifyAccess(C, Call.getArgSVal(0)); | 
|  | 383 | } | 
|  | 384 | } | 
| Adam Balogh | d5bd3f6 | 2018-12-04 10:27:27 +0000 | [diff] [blame] | 385 | if (ChecksEnabled[CK_IteratorRangeChecker]) { | 
|  | 386 | if (isIncrementOperator(Func->getOverloadedOperator())) { | 
|  | 387 | // Check for out-of-range incrementions | 
|  | 388 | if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { | 
|  | 389 | verifyIncrement(C, InstCall->getCXXThisVal()); | 
|  | 390 | } else { | 
|  | 391 | if (Call.getNumArgs() >= 1) { | 
|  | 392 | verifyIncrement(C, Call.getArgSVal(0)); | 
|  | 393 | } | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 394 | } | 
| Adam Balogh | d5bd3f6 | 2018-12-04 10:27:27 +0000 | [diff] [blame] | 395 | } else if (isDecrementOperator(Func->getOverloadedOperator())) { | 
|  | 396 | // Check for out-of-range decrementions | 
|  | 397 | if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { | 
|  | 398 | verifyDecrement(C, InstCall->getCXXThisVal()); | 
|  | 399 | } else { | 
|  | 400 | if (Call.getNumArgs() >= 1) { | 
|  | 401 | verifyDecrement(C, Call.getArgSVal(0)); | 
|  | 402 | } | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 403 | } | 
| Adam Balogh | d5bd3f6 | 2018-12-04 10:27:27 +0000 | [diff] [blame] | 404 | } else if (isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) { | 
|  | 405 | if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { | 
|  | 406 | // Check for out-of-range incrementions and decrementions | 
| Adam Balogh | 8557f17 | 2019-08-05 06:45:41 +0000 | [diff] [blame] | 407 | if (Call.getNumArgs() >= 1 && | 
|  | 408 | Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) { | 
| Adam Balogh | d5bd3f6 | 2018-12-04 10:27:27 +0000 | [diff] [blame] | 409 | verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(), | 
|  | 410 | InstCall->getCXXThisVal(), | 
|  | 411 | Call.getArgSVal(0)); | 
|  | 412 | } | 
|  | 413 | } else { | 
| Adam Balogh | 8557f17 | 2019-08-05 06:45:41 +0000 | [diff] [blame] | 414 | if (Call.getNumArgs() >= 2 && | 
|  | 415 | Call.getArgExpr(1)->getType()->isIntegralOrEnumerationType()) { | 
| Adam Balogh | d5bd3f6 | 2018-12-04 10:27:27 +0000 | [diff] [blame] | 416 | verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(), | 
|  | 417 | Call.getArgSVal(0), Call.getArgSVal(1)); | 
|  | 418 | } | 
|  | 419 | } | 
|  | 420 | } else if (isDereferenceOperator(Func->getOverloadedOperator())) { | 
|  | 421 | // Check for dereference of out-of-range iterators | 
|  | 422 | if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { | 
|  | 423 | verifyDereference(C, InstCall->getCXXThisVal()); | 
|  | 424 | } else { | 
|  | 425 | verifyDereference(C, Call.getArgSVal(0)); | 
|  | 426 | } | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 427 | } | 
| Adam Balogh | 3659f7a | 2018-09-10 09:05:31 +0000 | [diff] [blame] | 428 | } else if (ChecksEnabled[CK_MismatchedIteratorChecker] && | 
|  | 429 | isComparisonOperator(Func->getOverloadedOperator())) { | 
|  | 430 | // Check for comparisons of iterators of different containers | 
|  | 431 | if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { | 
|  | 432 | if (Call.getNumArgs() < 1) | 
|  | 433 | return; | 
|  | 434 |  | 
|  | 435 | if (!isIteratorType(InstCall->getCXXThisExpr()->getType()) || | 
|  | 436 | !isIteratorType(Call.getArgExpr(0)->getType())) | 
|  | 437 | return; | 
|  | 438 |  | 
|  | 439 | verifyMatch(C, InstCall->getCXXThisVal(), Call.getArgSVal(0)); | 
|  | 440 | } else { | 
|  | 441 | if (Call.getNumArgs() < 2) | 
|  | 442 | return; | 
|  | 443 |  | 
|  | 444 | if (!isIteratorType(Call.getArgExpr(0)->getType()) || | 
|  | 445 | !isIteratorType(Call.getArgExpr(1)->getType())) | 
|  | 446 | return; | 
|  | 447 |  | 
|  | 448 | verifyMatch(C, Call.getArgSVal(0), Call.getArgSVal(1)); | 
|  | 449 | } | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 450 | } | 
| Adam Balogh | 2e7cb34 | 2018-09-10 09:07:47 +0000 | [diff] [blame] | 451 | } else if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { | 
|  | 452 | if (!ChecksEnabled[CK_MismatchedIteratorChecker]) | 
|  | 453 | return; | 
|  | 454 |  | 
|  | 455 | const auto *ContReg = InstCall->getCXXThisVal().getAsRegion(); | 
|  | 456 | if (!ContReg) | 
|  | 457 | return; | 
|  | 458 | // Check for erase, insert and emplace using iterator of another container | 
|  | 459 | if (isEraseCall(Func) || isEraseAfterCall(Func)) { | 
|  | 460 | verifyMatch(C, Call.getArgSVal(0), | 
|  | 461 | InstCall->getCXXThisVal().getAsRegion()); | 
|  | 462 | if (Call.getNumArgs() == 2) { | 
|  | 463 | verifyMatch(C, Call.getArgSVal(1), | 
|  | 464 | InstCall->getCXXThisVal().getAsRegion()); | 
|  | 465 | } | 
|  | 466 | } else if (isInsertCall(Func)) { | 
|  | 467 | verifyMatch(C, Call.getArgSVal(0), | 
|  | 468 | InstCall->getCXXThisVal().getAsRegion()); | 
|  | 469 | if (Call.getNumArgs() == 3 && | 
|  | 470 | isIteratorType(Call.getArgExpr(1)->getType()) && | 
|  | 471 | isIteratorType(Call.getArgExpr(2)->getType())) { | 
|  | 472 | verifyMatch(C, Call.getArgSVal(1), Call.getArgSVal(2)); | 
|  | 473 | } | 
|  | 474 | } else if (isEmplaceCall(Func)) { | 
|  | 475 | verifyMatch(C, Call.getArgSVal(0), | 
|  | 476 | InstCall->getCXXThisVal().getAsRegion()); | 
|  | 477 | } | 
| Adam Balogh | 3659f7a | 2018-09-10 09:05:31 +0000 | [diff] [blame] | 478 | } else if (isa<CXXConstructorCall>(&Call)) { | 
|  | 479 | // Check match of first-last iterator pair in a constructor of a container | 
|  | 480 | if (Call.getNumArgs() < 2) | 
|  | 481 | return; | 
|  | 482 |  | 
|  | 483 | const auto *Ctr = cast<CXXConstructorDecl>(Call.getDecl()); | 
|  | 484 | if (Ctr->getNumParams() < 2) | 
|  | 485 | return; | 
|  | 486 |  | 
|  | 487 | if (Ctr->getParamDecl(0)->getName() != "first" || | 
|  | 488 | Ctr->getParamDecl(1)->getName() != "last") | 
|  | 489 | return; | 
|  | 490 |  | 
|  | 491 | if (!isIteratorType(Call.getArgExpr(0)->getType()) || | 
|  | 492 | !isIteratorType(Call.getArgExpr(1)->getType())) | 
|  | 493 | return; | 
|  | 494 |  | 
|  | 495 | verifyMatch(C, Call.getArgSVal(0), Call.getArgSVal(1)); | 
|  | 496 | } else { | 
| Adam Balogh | 21583b7 | 2018-09-10 09:03:22 +0000 | [diff] [blame] | 497 | // The main purpose of iterators is to abstract away from different | 
|  | 498 | // containers and provide a (maybe limited) uniform access to them. | 
|  | 499 | // This implies that any correctly written template function that | 
|  | 500 | // works on multiple containers using iterators takes different | 
|  | 501 | // template parameters for different containers. So we can safely | 
|  | 502 | // assume that passing iterators of different containers as arguments | 
|  | 503 | // whose type replaces the same template parameter is a bug. | 
|  | 504 | // | 
|  | 505 | // Example: | 
|  | 506 | // template<typename I1, typename I2> | 
|  | 507 | // void f(I1 first1, I1 last1, I2 first2, I2 last2); | 
|  | 508 | // | 
|  | 509 | // In this case the first two arguments to f() must be iterators must belong | 
|  | 510 | // to the same container and the last to also to the same container but | 
| Raphael Isemann | b23ccec | 2018-12-10 12:37:46 +0000 | [diff] [blame] | 511 | // not necessarily to the same as the first two. | 
| Adam Balogh | 21583b7 | 2018-09-10 09:03:22 +0000 | [diff] [blame] | 512 |  | 
|  | 513 | if (!ChecksEnabled[CK_MismatchedIteratorChecker]) | 
|  | 514 | return; | 
|  | 515 |  | 
|  | 516 | const auto *Templ = Func->getPrimaryTemplate(); | 
|  | 517 | if (!Templ) | 
|  | 518 | return; | 
|  | 519 |  | 
|  | 520 | const auto *TParams = Templ->getTemplateParameters(); | 
|  | 521 | const auto *TArgs = Func->getTemplateSpecializationArgs(); | 
|  | 522 |  | 
|  | 523 | // Iterate over all the template parameters | 
|  | 524 | for (size_t I = 0; I < TParams->size(); ++I) { | 
|  | 525 | const auto *TPDecl = dyn_cast<TemplateTypeParmDecl>(TParams->getParam(I)); | 
|  | 526 | if (!TPDecl) | 
|  | 527 | continue; | 
|  | 528 |  | 
|  | 529 | if (TPDecl->isParameterPack()) | 
|  | 530 | continue; | 
|  | 531 |  | 
|  | 532 | const auto TAType = TArgs->get(I).getAsType(); | 
|  | 533 | if (!isIteratorType(TAType)) | 
|  | 534 | continue; | 
|  | 535 |  | 
|  | 536 | SVal LHS = UndefinedVal(); | 
|  | 537 |  | 
|  | 538 | // For every template parameter which is an iterator type in the | 
|  | 539 | // instantiation look for all functions' parameters' type by it and | 
|  | 540 | // check whether they belong to the same container | 
|  | 541 | for (auto J = 0U; J < Func->getNumParams(); ++J) { | 
|  | 542 | const auto *Param = Func->getParamDecl(J); | 
|  | 543 | const auto *ParamType = | 
|  | 544 | Param->getType()->getAs<SubstTemplateTypeParmType>(); | 
|  | 545 | if (!ParamType || | 
|  | 546 | ParamType->getReplacedParameter()->getDecl() != TPDecl) | 
|  | 547 | continue; | 
|  | 548 | if (LHS.isUndef()) { | 
|  | 549 | LHS = Call.getArgSVal(J); | 
|  | 550 | } else { | 
|  | 551 | verifyMatch(C, LHS, Call.getArgSVal(J)); | 
|  | 552 | } | 
|  | 553 | } | 
|  | 554 | } | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 555 | } | 
|  | 556 | } | 
|  | 557 |  | 
|  | 558 | void IteratorChecker::checkPostCall(const CallEvent &Call, | 
|  | 559 | CheckerContext &C) const { | 
|  | 560 | // Record new iterator positions and iterator position changes | 
|  | 561 | const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); | 
|  | 562 | if (!Func) | 
|  | 563 | return; | 
|  | 564 |  | 
|  | 565 | if (Func->isOverloadedOperator()) { | 
|  | 566 | const auto Op = Func->getOverloadedOperator(); | 
| Adam Balogh | 2cfbe93 | 2018-08-28 08:41:15 +0000 | [diff] [blame] | 567 | if (isAssignmentOperator(Op)) { | 
| Artem Dergachev | 630f7da | 2019-08-28 18:44:38 +0000 | [diff] [blame] | 568 | // Overloaded 'operator=' must be a non-static member function. | 
|  | 569 | const auto *InstCall = cast<CXXInstanceCall>(&Call); | 
| Adam Balogh | d538b70 | 2019-04-26 07:30:07 +0000 | [diff] [blame] | 570 | if (cast<CXXMethodDecl>(Func)->isMoveAssignmentOperator()) { | 
| Adam Balogh | 6b23b1a | 2018-09-10 09:04:27 +0000 | [diff] [blame] | 571 | handleAssign(C, InstCall->getCXXThisVal(), Call.getOriginExpr(), | 
|  | 572 | Call.getArgSVal(0)); | 
| Adam Balogh | d538b70 | 2019-04-26 07:30:07 +0000 | [diff] [blame] | 573 | return; | 
| Adam Balogh | 6b23b1a | 2018-09-10 09:04:27 +0000 | [diff] [blame] | 574 | } | 
| Adam Balogh | d538b70 | 2019-04-26 07:30:07 +0000 | [diff] [blame] | 575 |  | 
|  | 576 | handleAssign(C, InstCall->getCXXThisVal()); | 
|  | 577 | return; | 
| Adam Balogh | 2cfbe93 | 2018-08-28 08:41:15 +0000 | [diff] [blame] | 578 | } else if (isSimpleComparisonOperator(Op)) { | 
| Adam Balogh | 54976e7 | 2019-04-23 07:15:55 +0000 | [diff] [blame] | 579 | const auto *OrigExpr = Call.getOriginExpr(); | 
|  | 580 | if (!OrigExpr) | 
|  | 581 | return; | 
|  | 582 |  | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 583 | if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { | 
| Adam Balogh | 54976e7 | 2019-04-23 07:15:55 +0000 | [diff] [blame] | 584 | handleComparison(C, OrigExpr, Call.getReturnValue(), | 
|  | 585 | InstCall->getCXXThisVal(), Call.getArgSVal(0), Op); | 
| Adam Balogh | d538b70 | 2019-04-26 07:30:07 +0000 | [diff] [blame] | 586 | return; | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 587 | } | 
| Adam Balogh | d538b70 | 2019-04-26 07:30:07 +0000 | [diff] [blame] | 588 |  | 
|  | 589 | handleComparison(C, OrigExpr, Call.getReturnValue(), Call.getArgSVal(0), | 
|  | 590 | Call.getArgSVal(1), Op); | 
|  | 591 | return; | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 592 | } else if (isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) { | 
|  | 593 | if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { | 
| Adam Balogh | 8557f17 | 2019-08-05 06:45:41 +0000 | [diff] [blame] | 594 | if (Call.getNumArgs() >= 1 && | 
|  | 595 | Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) { | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 596 | handleRandomIncrOrDecr(C, Func->getOverloadedOperator(), | 
|  | 597 | Call.getReturnValue(), | 
|  | 598 | InstCall->getCXXThisVal(), Call.getArgSVal(0)); | 
| Adam Balogh | d538b70 | 2019-04-26 07:30:07 +0000 | [diff] [blame] | 599 | return; | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 600 | } | 
|  | 601 | } else { | 
| Adam Balogh | 8557f17 | 2019-08-05 06:45:41 +0000 | [diff] [blame] | 602 | if (Call.getNumArgs() >= 2 && | 
|  | 603 | Call.getArgExpr(1)->getType()->isIntegralOrEnumerationType()) { | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 604 | handleRandomIncrOrDecr(C, Func->getOverloadedOperator(), | 
|  | 605 | Call.getReturnValue(), Call.getArgSVal(0), | 
|  | 606 | Call.getArgSVal(1)); | 
| Adam Balogh | d538b70 | 2019-04-26 07:30:07 +0000 | [diff] [blame] | 607 | return; | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 608 | } | 
|  | 609 | } | 
|  | 610 | } else if (isIncrementOperator(Func->getOverloadedOperator())) { | 
|  | 611 | if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { | 
|  | 612 | handleIncrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(), | 
|  | 613 | Call.getNumArgs()); | 
| Adam Balogh | d538b70 | 2019-04-26 07:30:07 +0000 | [diff] [blame] | 614 | return; | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 615 | } | 
| Adam Balogh | d538b70 | 2019-04-26 07:30:07 +0000 | [diff] [blame] | 616 |  | 
|  | 617 | handleIncrement(C, Call.getReturnValue(), Call.getArgSVal(0), | 
|  | 618 | Call.getNumArgs()); | 
|  | 619 | return; | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 620 | } else if (isDecrementOperator(Func->getOverloadedOperator())) { | 
|  | 621 | if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { | 
|  | 622 | handleDecrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(), | 
|  | 623 | Call.getNumArgs()); | 
| Adam Balogh | d538b70 | 2019-04-26 07:30:07 +0000 | [diff] [blame] | 624 | return; | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 625 | } | 
| Adam Balogh | d538b70 | 2019-04-26 07:30:07 +0000 | [diff] [blame] | 626 |  | 
|  | 627 | handleDecrement(C, Call.getReturnValue(), Call.getArgSVal(0), | 
|  | 628 | Call.getNumArgs()); | 
|  | 629 | return; | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 630 | } | 
|  | 631 | } else { | 
| Adam Balogh | 9a48ba6 | 2018-09-10 09:06:31 +0000 | [diff] [blame] | 632 | if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { | 
| Adam Balogh | 2e7cb34 | 2018-09-10 09:07:47 +0000 | [diff] [blame] | 633 | if (isAssignCall(Func)) { | 
|  | 634 | handleAssign(C, InstCall->getCXXThisVal()); | 
| Adam Balogh | d538b70 | 2019-04-26 07:30:07 +0000 | [diff] [blame] | 635 | return; | 
|  | 636 | } | 
|  | 637 |  | 
|  | 638 | if (isClearCall(Func)) { | 
| Adam Balogh | 2e7cb34 | 2018-09-10 09:07:47 +0000 | [diff] [blame] | 639 | handleClear(C, InstCall->getCXXThisVal()); | 
| Adam Balogh | d538b70 | 2019-04-26 07:30:07 +0000 | [diff] [blame] | 640 | return; | 
|  | 641 | } | 
|  | 642 |  | 
|  | 643 | if (isPushBackCall(Func) || isEmplaceBackCall(Func)) { | 
| Adam Balogh | 9a48ba6 | 2018-09-10 09:06:31 +0000 | [diff] [blame] | 644 | handlePushBack(C, InstCall->getCXXThisVal()); | 
| Adam Balogh | d538b70 | 2019-04-26 07:30:07 +0000 | [diff] [blame] | 645 | return; | 
|  | 646 | } | 
|  | 647 |  | 
|  | 648 | if (isPopBackCall(Func)) { | 
| Adam Balogh | 9a48ba6 | 2018-09-10 09:06:31 +0000 | [diff] [blame] | 649 | handlePopBack(C, InstCall->getCXXThisVal()); | 
| Adam Balogh | d538b70 | 2019-04-26 07:30:07 +0000 | [diff] [blame] | 650 | return; | 
|  | 651 | } | 
|  | 652 |  | 
|  | 653 | if (isPushFrontCall(Func) || isEmplaceFrontCall(Func)) { | 
| Adam Balogh | 9a48ba6 | 2018-09-10 09:06:31 +0000 | [diff] [blame] | 654 | handlePushFront(C, InstCall->getCXXThisVal()); | 
| Adam Balogh | d538b70 | 2019-04-26 07:30:07 +0000 | [diff] [blame] | 655 | return; | 
|  | 656 | } | 
|  | 657 |  | 
|  | 658 | if (isPopFrontCall(Func)) { | 
| Adam Balogh | 9a48ba6 | 2018-09-10 09:06:31 +0000 | [diff] [blame] | 659 | handlePopFront(C, InstCall->getCXXThisVal()); | 
| Adam Balogh | d538b70 | 2019-04-26 07:30:07 +0000 | [diff] [blame] | 660 | return; | 
|  | 661 | } | 
|  | 662 |  | 
|  | 663 | if (isInsertCall(Func) || isEmplaceCall(Func)) { | 
| Adam Balogh | 2e7cb34 | 2018-09-10 09:07:47 +0000 | [diff] [blame] | 664 | handleInsert(C, Call.getArgSVal(0)); | 
| Adam Balogh | d538b70 | 2019-04-26 07:30:07 +0000 | [diff] [blame] | 665 | return; | 
|  | 666 | } | 
|  | 667 |  | 
|  | 668 | if (isEraseCall(Func)) { | 
| Adam Balogh | 2e7cb34 | 2018-09-10 09:07:47 +0000 | [diff] [blame] | 669 | if (Call.getNumArgs() == 1) { | 
|  | 670 | handleErase(C, Call.getArgSVal(0)); | 
| Adam Balogh | d538b70 | 2019-04-26 07:30:07 +0000 | [diff] [blame] | 671 | return; | 
| Adam Balogh | 2e7cb34 | 2018-09-10 09:07:47 +0000 | [diff] [blame] | 672 | } | 
| Adam Balogh | d538b70 | 2019-04-26 07:30:07 +0000 | [diff] [blame] | 673 |  | 
|  | 674 | if (Call.getNumArgs() == 2) { | 
|  | 675 | handleErase(C, Call.getArgSVal(0), Call.getArgSVal(1)); | 
|  | 676 | return; | 
|  | 677 | } | 
|  | 678 | } | 
|  | 679 |  | 
|  | 680 | if (isEraseAfterCall(Func)) { | 
| Adam Balogh | 2e7cb34 | 2018-09-10 09:07:47 +0000 | [diff] [blame] | 681 | if (Call.getNumArgs() == 1) { | 
|  | 682 | handleEraseAfter(C, Call.getArgSVal(0)); | 
| Adam Balogh | d538b70 | 2019-04-26 07:30:07 +0000 | [diff] [blame] | 683 | return; | 
|  | 684 | } | 
|  | 685 |  | 
|  | 686 | if (Call.getNumArgs() == 2) { | 
| Adam Balogh | 2e7cb34 | 2018-09-10 09:07:47 +0000 | [diff] [blame] | 687 | handleEraseAfter(C, Call.getArgSVal(0), Call.getArgSVal(1)); | 
| Adam Balogh | d538b70 | 2019-04-26 07:30:07 +0000 | [diff] [blame] | 688 | return; | 
| Adam Balogh | 2e7cb34 | 2018-09-10 09:07:47 +0000 | [diff] [blame] | 689 | } | 
| Adam Balogh | 9a48ba6 | 2018-09-10 09:06:31 +0000 | [diff] [blame] | 690 | } | 
|  | 691 | } | 
|  | 692 |  | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 693 | const auto *OrigExpr = Call.getOriginExpr(); | 
|  | 694 | if (!OrigExpr) | 
|  | 695 | return; | 
|  | 696 |  | 
|  | 697 | if (!isIteratorType(Call.getResultType())) | 
|  | 698 | return; | 
|  | 699 |  | 
|  | 700 | auto State = C.getState(); | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 701 |  | 
|  | 702 | if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 703 | if (isBeginCall(Func)) { | 
|  | 704 | handleBegin(C, OrigExpr, Call.getReturnValue(), | 
|  | 705 | InstCall->getCXXThisVal()); | 
|  | 706 | return; | 
|  | 707 | } | 
| Adam Balogh | d538b70 | 2019-04-26 07:30:07 +0000 | [diff] [blame] | 708 |  | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 709 | if (isEndCall(Func)) { | 
|  | 710 | handleEnd(C, OrigExpr, Call.getReturnValue(), | 
|  | 711 | InstCall->getCXXThisVal()); | 
|  | 712 | return; | 
|  | 713 | } | 
|  | 714 | } | 
|  | 715 |  | 
| Adam Balogh | 6b23b1a | 2018-09-10 09:04:27 +0000 | [diff] [blame] | 716 | // Already bound to container? | 
|  | 717 | if (getIteratorPosition(State, Call.getReturnValue())) | 
|  | 718 | return; | 
|  | 719 |  | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 720 | // Copy-like and move constructors | 
|  | 721 | if (isa<CXXConstructorCall>(&Call) && Call.getNumArgs() == 1) { | 
|  | 722 | if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(0))) { | 
|  | 723 | State = setIteratorPosition(State, Call.getReturnValue(), *Pos); | 
|  | 724 | if (cast<CXXConstructorDecl>(Func)->isMoveConstructor()) { | 
|  | 725 | State = removeIteratorPosition(State, Call.getArgSVal(0)); | 
|  | 726 | } | 
|  | 727 | C.addTransition(State); | 
|  | 728 | return; | 
|  | 729 | } | 
|  | 730 | } | 
|  | 731 |  | 
|  | 732 | // Assumption: if return value is an iterator which is not yet bound to a | 
|  | 733 | //             container, then look for the first iterator argument, and | 
|  | 734 | //             bind the return value to the same container. This approach | 
|  | 735 | //             works for STL algorithms. | 
|  | 736 | // FIXME: Add a more conservative mode | 
|  | 737 | for (unsigned i = 0; i < Call.getNumArgs(); ++i) { | 
|  | 738 | if (isIteratorType(Call.getArgExpr(i)->getType())) { | 
|  | 739 | if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(i))) { | 
|  | 740 | assignToContainer(C, OrigExpr, Call.getReturnValue(), | 
|  | 741 | Pos->getContainer()); | 
|  | 742 | return; | 
|  | 743 | } | 
|  | 744 | } | 
|  | 745 | } | 
|  | 746 | } | 
|  | 747 | } | 
|  | 748 |  | 
| Adam Balogh | 2e7cb34 | 2018-09-10 09:07:47 +0000 | [diff] [blame] | 749 | void IteratorChecker::checkBind(SVal Loc, SVal Val, const Stmt *S, | 
|  | 750 | CheckerContext &C) const { | 
|  | 751 | auto State = C.getState(); | 
|  | 752 | const auto *Pos = getIteratorPosition(State, Val); | 
|  | 753 | if (Pos) { | 
|  | 754 | State = setIteratorPosition(State, Loc, *Pos); | 
|  | 755 | C.addTransition(State); | 
|  | 756 | } else { | 
|  | 757 | const auto *OldPos = getIteratorPosition(State, Loc); | 
|  | 758 | if (OldPos) { | 
|  | 759 | State = removeIteratorPosition(State, Loc); | 
|  | 760 | C.addTransition(State); | 
|  | 761 | } | 
|  | 762 | } | 
|  | 763 | } | 
|  | 764 |  | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 765 | void IteratorChecker::checkPostStmt(const MaterializeTemporaryExpr *MTE, | 
|  | 766 | CheckerContext &C) const { | 
|  | 767 | /* Transfer iterator state to temporary objects */ | 
|  | 768 | auto State = C.getState(); | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 769 | const auto *Pos = | 
| George Karpenkov | d703ec9 | 2018-01-17 20:27:29 +0000 | [diff] [blame] | 770 | getIteratorPosition(State, C.getSVal(MTE->GetTemporaryExpr())); | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 771 | if (!Pos) | 
|  | 772 | return; | 
| George Karpenkov | d703ec9 | 2018-01-17 20:27:29 +0000 | [diff] [blame] | 773 | State = setIteratorPosition(State, C.getSVal(MTE), *Pos); | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 774 | C.addTransition(State); | 
|  | 775 | } | 
|  | 776 |  | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 777 | void IteratorChecker::checkLiveSymbols(ProgramStateRef State, | 
|  | 778 | SymbolReaper &SR) const { | 
|  | 779 | // Keep symbolic expressions of iterator positions, container begins and ends | 
|  | 780 | // alive | 
|  | 781 | auto RegionMap = State->get<IteratorRegionMap>(); | 
|  | 782 | for (const auto Reg : RegionMap) { | 
| Adam Balogh | 0a7592b | 2018-07-16 09:27:27 +0000 | [diff] [blame] | 783 | const auto Offset = Reg.second.getOffset(); | 
|  | 784 | for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i) | 
|  | 785 | if (isa<SymbolData>(*i)) | 
|  | 786 | SR.markLive(*i); | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 787 | } | 
|  | 788 |  | 
|  | 789 | auto SymbolMap = State->get<IteratorSymbolMap>(); | 
|  | 790 | for (const auto Sym : SymbolMap) { | 
| Adam Balogh | 0a7592b | 2018-07-16 09:27:27 +0000 | [diff] [blame] | 791 | const auto Offset = Sym.second.getOffset(); | 
|  | 792 | for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i) | 
|  | 793 | if (isa<SymbolData>(*i)) | 
|  | 794 | SR.markLive(*i); | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 795 | } | 
|  | 796 |  | 
|  | 797 | auto ContMap = State->get<ContainerMap>(); | 
|  | 798 | for (const auto Cont : ContMap) { | 
|  | 799 | const auto CData = Cont.second; | 
|  | 800 | if (CData.getBegin()) { | 
|  | 801 | SR.markLive(CData.getBegin()); | 
| Adam Balogh | 9a48ba6 | 2018-09-10 09:06:31 +0000 | [diff] [blame] | 802 | if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getBegin())) | 
|  | 803 | SR.markLive(SIE->getLHS()); | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 804 | } | 
|  | 805 | if (CData.getEnd()) { | 
|  | 806 | SR.markLive(CData.getEnd()); | 
| Adam Balogh | 9a48ba6 | 2018-09-10 09:06:31 +0000 | [diff] [blame] | 807 | if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getEnd())) | 
|  | 808 | SR.markLive(SIE->getLHS()); | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 809 | } | 
|  | 810 | } | 
|  | 811 | } | 
|  | 812 |  | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 813 | void IteratorChecker::checkDeadSymbols(SymbolReaper &SR, | 
|  | 814 | CheckerContext &C) const { | 
|  | 815 | // Cleanup | 
|  | 816 | auto State = C.getState(); | 
|  | 817 |  | 
|  | 818 | auto RegionMap = State->get<IteratorRegionMap>(); | 
|  | 819 | for (const auto Reg : RegionMap) { | 
|  | 820 | if (!SR.isLiveRegion(Reg.first)) { | 
| Adam Balogh | 21583b7 | 2018-09-10 09:03:22 +0000 | [diff] [blame] | 821 | // The region behind the `LazyCompoundVal` is often cleaned up before | 
|  | 822 | // the `LazyCompoundVal` itself. If there are iterator positions keyed | 
|  | 823 | // by these regions their cleanup must be deferred. | 
|  | 824 | if (!isBoundThroughLazyCompoundVal(State->getEnvironment(), Reg.first)) { | 
|  | 825 | State = State->remove<IteratorRegionMap>(Reg.first); | 
|  | 826 | } | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 827 | } | 
|  | 828 | } | 
|  | 829 |  | 
|  | 830 | auto SymbolMap = State->get<IteratorSymbolMap>(); | 
|  | 831 | for (const auto Sym : SymbolMap) { | 
|  | 832 | if (!SR.isLive(Sym.first)) { | 
|  | 833 | State = State->remove<IteratorSymbolMap>(Sym.first); | 
|  | 834 | } | 
|  | 835 | } | 
|  | 836 |  | 
|  | 837 | auto ContMap = State->get<ContainerMap>(); | 
|  | 838 | for (const auto Cont : ContMap) { | 
|  | 839 | if (!SR.isLiveRegion(Cont.first)) { | 
| Adam Balogh | a692120 | 2018-07-30 08:52:21 +0000 | [diff] [blame] | 840 | // We must keep the container data while it has live iterators to be able | 
|  | 841 | // to compare them to the begin and the end of the container. | 
|  | 842 | if (!hasLiveIterators(State, Cont.first)) { | 
|  | 843 | State = State->remove<ContainerMap>(Cont.first); | 
|  | 844 | } | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 845 | } | 
|  | 846 | } | 
|  | 847 |  | 
| Reka Kovacs | e48ea89 | 2018-07-30 16:14:59 +0000 | [diff] [blame] | 848 | C.addTransition(State); | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 849 | } | 
|  | 850 |  | 
| Adam Balogh | 54976e7 | 2019-04-23 07:15:55 +0000 | [diff] [blame] | 851 | void IteratorChecker::handleComparison(CheckerContext &C, const Expr *CE, | 
|  | 852 | const SVal &RetVal, const SVal &LVal, | 
|  | 853 | const SVal &RVal, | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 854 | OverloadedOperatorKind Op) const { | 
|  | 855 | // Record the operands and the operator of the comparison for the next | 
|  | 856 | // evalAssume, if the result is a symbolic expression. If it is a concrete | 
|  | 857 | // value (only one branch is possible), then transfer the state between | 
|  | 858 | // the operands according to the operator and the result | 
| Adam Balogh | 54976e7 | 2019-04-23 07:15:55 +0000 | [diff] [blame] | 859 | auto State = C.getState(); | 
|  | 860 | const auto *LPos = getIteratorPosition(State, LVal); | 
|  | 861 | const auto *RPos = getIteratorPosition(State, RVal); | 
|  | 862 | const MemRegion *Cont = nullptr; | 
|  | 863 | if (LPos) { | 
|  | 864 | Cont = LPos->getContainer(); | 
|  | 865 | } else if (RPos) { | 
|  | 866 | Cont = RPos->getContainer(); | 
|  | 867 | } | 
|  | 868 | if (!Cont) | 
|  | 869 | return; | 
|  | 870 |  | 
|  | 871 | // At least one of the iterators have recorded positions. If one of them has | 
|  | 872 | // not then create a new symbol for the offset. | 
|  | 873 | SymbolRef Sym; | 
|  | 874 | if (!LPos || !RPos) { | 
|  | 875 | auto &SymMgr = C.getSymbolManager(); | 
| Adam Balogh | d2e2e20 | 2019-04-23 11:18:50 +0000 | [diff] [blame] | 876 | Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(), | 
| Adam Balogh | 54976e7 | 2019-04-23 07:15:55 +0000 | [diff] [blame] | 877 | C.getASTContext().LongTy, C.blockCount()); | 
|  | 878 | State = assumeNoOverflow(State, Sym, 4); | 
|  | 879 | } | 
|  | 880 |  | 
|  | 881 | if (!LPos) { | 
|  | 882 | State = setIteratorPosition(State, LVal, | 
|  | 883 | IteratorPosition::getPosition(Cont, Sym)); | 
|  | 884 | LPos = getIteratorPosition(State, LVal); | 
|  | 885 | } else if (!RPos) { | 
|  | 886 | State = setIteratorPosition(State, RVal, | 
|  | 887 | IteratorPosition::getPosition(Cont, Sym)); | 
|  | 888 | RPos = getIteratorPosition(State, RVal); | 
|  | 889 | } | 
|  | 890 |  | 
|  | 891 | processComparison(C, State, LPos->getOffset(), RPos->getOffset(), RetVal, Op); | 
|  | 892 | } | 
|  | 893 |  | 
|  | 894 | void IteratorChecker::processComparison(CheckerContext &C, | 
|  | 895 | ProgramStateRef State, SymbolRef Sym1, | 
|  | 896 | SymbolRef Sym2, const SVal &RetVal, | 
|  | 897 | OverloadedOperatorKind Op) const { | 
|  | 898 | if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) { | 
| Adam Balogh | 8f88270 | 2019-04-23 07:45:10 +0000 | [diff] [blame] | 899 | if ((State = relateSymbols(State, Sym1, Sym2, | 
| Adam Balogh | 54976e7 | 2019-04-23 07:15:55 +0000 | [diff] [blame] | 900 | (Op == OO_EqualEqual) == | 
| Adam Balogh | 8f88270 | 2019-04-23 07:45:10 +0000 | [diff] [blame] | 901 | (TruthVal->getValue() != 0)))) { | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 902 | C.addTransition(State); | 
|  | 903 | } else { | 
|  | 904 | C.generateSink(State, C.getPredecessor()); | 
|  | 905 | } | 
| Adam Balogh | 54976e7 | 2019-04-23 07:15:55 +0000 | [diff] [blame] | 906 | return; | 
|  | 907 | } | 
|  | 908 |  | 
|  | 909 | const auto ConditionVal = RetVal.getAs<DefinedSVal>(); | 
|  | 910 | if (!ConditionVal) | 
|  | 911 | return; | 
|  | 912 |  | 
|  | 913 | if (auto StateTrue = relateSymbols(State, Sym1, Sym2, Op == OO_EqualEqual)) { | 
|  | 914 | StateTrue = StateTrue->assume(*ConditionVal, true); | 
|  | 915 | C.addTransition(StateTrue); | 
|  | 916 | } | 
|  | 917 |  | 
|  | 918 | if (auto StateFalse = relateSymbols(State, Sym1, Sym2, Op != OO_EqualEqual)) { | 
|  | 919 | StateFalse = StateFalse->assume(*ConditionVal, false); | 
|  | 920 | C.addTransition(StateFalse); | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 921 | } | 
|  | 922 | } | 
|  | 923 |  | 
|  | 924 | void IteratorChecker::verifyDereference(CheckerContext &C, | 
|  | 925 | const SVal &Val) const { | 
|  | 926 | auto State = C.getState(); | 
|  | 927 | const auto *Pos = getIteratorPosition(State, Val); | 
| Adam Balogh | d5bd3f6 | 2018-12-04 10:27:27 +0000 | [diff] [blame] | 928 | if (Pos && isPastTheEnd(State, *Pos)) { | 
| Adam Balogh | 12f5c7f | 2019-08-29 09:35:47 +0000 | [diff] [blame] | 929 | auto *N = C.generateErrorNode(State); | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 930 | if (!N) | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 931 | return; | 
| Adam Balogh | d5bd3f6 | 2018-12-04 10:27:27 +0000 | [diff] [blame] | 932 | reportOutOfRangeBug("Past-the-end iterator dereferenced.", Val, C, N); | 
|  | 933 | return; | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 934 | } | 
|  | 935 | } | 
|  | 936 |  | 
| Adam Balogh | 2cfbe93 | 2018-08-28 08:41:15 +0000 | [diff] [blame] | 937 | void IteratorChecker::verifyAccess(CheckerContext &C, const SVal &Val) const { | 
|  | 938 | auto State = C.getState(); | 
|  | 939 | const auto *Pos = getIteratorPosition(State, Val); | 
|  | 940 | if (Pos && !Pos->isValid()) { | 
| Adam Balogh | 12f5c7f | 2019-08-29 09:35:47 +0000 | [diff] [blame] | 941 | auto *N = C.generateErrorNode(State); | 
| Adam Balogh | 2cfbe93 | 2018-08-28 08:41:15 +0000 | [diff] [blame] | 942 | if (!N) { | 
|  | 943 | return; | 
|  | 944 | } | 
|  | 945 | reportInvalidatedBug("Invalidated iterator accessed.", Val, C, N); | 
|  | 946 | } | 
|  | 947 | } | 
|  | 948 |  | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 949 | void IteratorChecker::handleIncrement(CheckerContext &C, const SVal &RetVal, | 
|  | 950 | const SVal &Iter, bool Postfix) const { | 
|  | 951 | // Increment the symbolic expressions which represents the position of the | 
|  | 952 | // iterator | 
|  | 953 | auto State = C.getState(); | 
|  | 954 | const auto *Pos = getIteratorPosition(State, Iter); | 
|  | 955 | if (Pos) { | 
|  | 956 | auto &SymMgr = C.getSymbolManager(); | 
|  | 957 | auto &BVF = SymMgr.getBasicVals(); | 
| Adam Balogh | d5bd3f6 | 2018-12-04 10:27:27 +0000 | [diff] [blame] | 958 | const auto NewPos = | 
|  | 959 | advancePosition(C, OO_Plus, *Pos, | 
|  | 960 | nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1)))); | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 961 | State = setIteratorPosition(State, Iter, NewPos); | 
|  | 962 | State = setIteratorPosition(State, RetVal, Postfix ? *Pos : NewPos); | 
|  | 963 | C.addTransition(State); | 
|  | 964 | } | 
|  | 965 | } | 
|  | 966 |  | 
|  | 967 | void IteratorChecker::handleDecrement(CheckerContext &C, const SVal &RetVal, | 
|  | 968 | const SVal &Iter, bool Postfix) const { | 
|  | 969 | // Decrement the symbolic expressions which represents the position of the | 
|  | 970 | // iterator | 
|  | 971 | auto State = C.getState(); | 
|  | 972 | const auto *Pos = getIteratorPosition(State, Iter); | 
|  | 973 | if (Pos) { | 
|  | 974 | auto &SymMgr = C.getSymbolManager(); | 
|  | 975 | auto &BVF = SymMgr.getBasicVals(); | 
| Adam Balogh | d5bd3f6 | 2018-12-04 10:27:27 +0000 | [diff] [blame] | 976 | const auto NewPos = | 
|  | 977 | advancePosition(C, OO_Minus, *Pos, | 
|  | 978 | nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1)))); | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 979 | State = setIteratorPosition(State, Iter, NewPos); | 
|  | 980 | State = setIteratorPosition(State, RetVal, Postfix ? *Pos : NewPos); | 
|  | 981 | C.addTransition(State); | 
|  | 982 | } | 
|  | 983 | } | 
|  | 984 |  | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 985 | void IteratorChecker::handleRandomIncrOrDecr(CheckerContext &C, | 
|  | 986 | OverloadedOperatorKind Op, | 
|  | 987 | const SVal &RetVal, | 
|  | 988 | const SVal &LHS, | 
|  | 989 | const SVal &RHS) const { | 
|  | 990 | // Increment or decrement the symbolic expressions which represents the | 
|  | 991 | // position of the iterator | 
|  | 992 | auto State = C.getState(); | 
|  | 993 | const auto *Pos = getIteratorPosition(State, LHS); | 
|  | 994 | if (!Pos) | 
|  | 995 | return; | 
|  | 996 |  | 
|  | 997 | const auto *value = &RHS; | 
|  | 998 | if (auto loc = RHS.getAs<Loc>()) { | 
|  | 999 | const auto val = State->getRawSVal(*loc); | 
|  | 1000 | value = &val; | 
|  | 1001 | } | 
|  | 1002 |  | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 1003 | auto &TgtVal = (Op == OO_PlusEqual || Op == OO_MinusEqual) ? LHS : RetVal; | 
| Adam Balogh | d5bd3f6 | 2018-12-04 10:27:27 +0000 | [diff] [blame] | 1004 | State = | 
|  | 1005 | setIteratorPosition(State, TgtVal, advancePosition(C, Op, *Pos, *value)); | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 1006 | C.addTransition(State); | 
|  | 1007 | } | 
|  | 1008 |  | 
| Adam Balogh | d5bd3f6 | 2018-12-04 10:27:27 +0000 | [diff] [blame] | 1009 | void IteratorChecker::verifyIncrement(CheckerContext &C, | 
|  | 1010 | const SVal &Iter) const { | 
|  | 1011 | auto &BVF = C.getSValBuilder().getBasicValueFactory(); | 
|  | 1012 | verifyRandomIncrOrDecr(C, OO_Plus, Iter, | 
|  | 1013 | nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1)))); | 
|  | 1014 | } | 
|  | 1015 |  | 
|  | 1016 | void IteratorChecker::verifyDecrement(CheckerContext &C, | 
|  | 1017 | const SVal &Iter) const { | 
|  | 1018 | auto &BVF = C.getSValBuilder().getBasicValueFactory(); | 
|  | 1019 | verifyRandomIncrOrDecr(C, OO_Minus, Iter, | 
|  | 1020 | nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1)))); | 
|  | 1021 | } | 
|  | 1022 |  | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 1023 | void IteratorChecker::verifyRandomIncrOrDecr(CheckerContext &C, | 
|  | 1024 | OverloadedOperatorKind Op, | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 1025 | const SVal &LHS, | 
|  | 1026 | const SVal &RHS) const { | 
|  | 1027 | auto State = C.getState(); | 
|  | 1028 |  | 
|  | 1029 | // If the iterator is initially inside its range, then the operation is valid | 
|  | 1030 | const auto *Pos = getIteratorPosition(State, LHS); | 
| Adam Balogh | d5bd3f6 | 2018-12-04 10:27:27 +0000 | [diff] [blame] | 1031 | if (!Pos) | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 1032 | return; | 
|  | 1033 |  | 
| Adam Balogh | d5bd3f6 | 2018-12-04 10:27:27 +0000 | [diff] [blame] | 1034 | auto Value = RHS; | 
|  | 1035 | if (auto ValAsLoc = RHS.getAs<Loc>()) { | 
|  | 1036 | Value = State->getRawSVal(*ValAsLoc); | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 1037 | } | 
|  | 1038 |  | 
| Adam Balogh | d5bd3f6 | 2018-12-04 10:27:27 +0000 | [diff] [blame] | 1039 | if (Value.isUnknown()) | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 1040 | return; | 
|  | 1041 |  | 
| Adam Balogh | d5bd3f6 | 2018-12-04 10:27:27 +0000 | [diff] [blame] | 1042 | // Incremention or decremention by 0 is never a bug. | 
|  | 1043 | if (isZero(State, Value.castAs<NonLoc>())) | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 1044 | return; | 
|  | 1045 |  | 
| Adam Balogh | d5bd3f6 | 2018-12-04 10:27:27 +0000 | [diff] [blame] | 1046 | // The result may be the past-end iterator of the container, but any other | 
|  | 1047 | // out of range position is undefined behaviour | 
|  | 1048 | if (isAheadOfRange(State, advancePosition(C, Op, *Pos, Value))) { | 
| Adam Balogh | 12f5c7f | 2019-08-29 09:35:47 +0000 | [diff] [blame] | 1049 | auto *N = C.generateErrorNode(State); | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 1050 | if (!N) | 
|  | 1051 | return; | 
| Adam Balogh | d5bd3f6 | 2018-12-04 10:27:27 +0000 | [diff] [blame] | 1052 | reportOutOfRangeBug("Iterator decremented ahead of its valid range.", LHS, | 
|  | 1053 | C, N); | 
|  | 1054 | } | 
|  | 1055 | if (isBehindPastTheEnd(State, advancePosition(C, Op, *Pos, Value))) { | 
| Adam Balogh | 12f5c7f | 2019-08-29 09:35:47 +0000 | [diff] [blame] | 1056 | auto *N = C.generateErrorNode(State); | 
| Adam Balogh | d5bd3f6 | 2018-12-04 10:27:27 +0000 | [diff] [blame] | 1057 | if (!N) | 
|  | 1058 | return; | 
|  | 1059 | reportOutOfRangeBug("Iterator incremented behind the past-the-end " | 
|  | 1060 | "iterator.", LHS, C, N); | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 1061 | } | 
|  | 1062 | } | 
|  | 1063 |  | 
| Adam Balogh | 2e7cb34 | 2018-09-10 09:07:47 +0000 | [diff] [blame] | 1064 | void IteratorChecker::verifyMatch(CheckerContext &C, const SVal &Iter, | 
|  | 1065 | const MemRegion *Cont) const { | 
|  | 1066 | // Verify match between a container and the container of an iterator | 
| Adam Balogh | 42d241f | 2018-12-04 10:22:28 +0000 | [diff] [blame] | 1067 | Cont = Cont->getMostDerivedObjectRegion(); | 
| Adam Balogh | 2e7cb34 | 2018-09-10 09:07:47 +0000 | [diff] [blame] | 1068 |  | 
| Adam Balogh | d703305 | 2019-03-13 13:55:11 +0000 | [diff] [blame] | 1069 | if (const auto *ContSym = Cont->getSymbolicBase()) { | 
|  | 1070 | if (isa<SymbolConjured>(ContSym->getSymbol())) | 
|  | 1071 | return; | 
|  | 1072 | } | 
|  | 1073 |  | 
| Adam Balogh | 2e7cb34 | 2018-09-10 09:07:47 +0000 | [diff] [blame] | 1074 | auto State = C.getState(); | 
|  | 1075 | const auto *Pos = getIteratorPosition(State, Iter); | 
| Adam Balogh | d703305 | 2019-03-13 13:55:11 +0000 | [diff] [blame] | 1076 | if (!Pos) | 
|  | 1077 | return; | 
|  | 1078 |  | 
|  | 1079 | const auto *IterCont = Pos->getContainer(); | 
|  | 1080 |  | 
|  | 1081 | // Skip symbolic regions based on conjured symbols. Two conjured symbols | 
|  | 1082 | // may or may not be the same. For example, the same function can return | 
|  | 1083 | // the same or a different container but we get different conjured symbols | 
|  | 1084 | // for each call. This may cause false positives so omit them from the check. | 
|  | 1085 | if (const auto *ContSym = IterCont->getSymbolicBase()) { | 
|  | 1086 | if (isa<SymbolConjured>(ContSym->getSymbol())) | 
|  | 1087 | return; | 
|  | 1088 | } | 
|  | 1089 |  | 
|  | 1090 | if (IterCont != Cont) { | 
| Adam Balogh | 2e7cb34 | 2018-09-10 09:07:47 +0000 | [diff] [blame] | 1091 | auto *N = C.generateNonFatalErrorNode(State); | 
|  | 1092 | if (!N) { | 
|  | 1093 | return; | 
|  | 1094 | } | 
| Adam Balogh | d703305 | 2019-03-13 13:55:11 +0000 | [diff] [blame] | 1095 | reportMismatchedBug("Container accessed using foreign iterator argument.", | 
|  | 1096 | Iter, Cont, C, N); | 
| Adam Balogh | 2e7cb34 | 2018-09-10 09:07:47 +0000 | [diff] [blame] | 1097 | } | 
|  | 1098 | } | 
|  | 1099 |  | 
| Adam Balogh | 21583b7 | 2018-09-10 09:03:22 +0000 | [diff] [blame] | 1100 | void IteratorChecker::verifyMatch(CheckerContext &C, const SVal &Iter1, | 
|  | 1101 | const SVal &Iter2) const { | 
|  | 1102 | // Verify match between the containers of two iterators | 
|  | 1103 | auto State = C.getState(); | 
|  | 1104 | const auto *Pos1 = getIteratorPosition(State, Iter1); | 
| Adam Balogh | d703305 | 2019-03-13 13:55:11 +0000 | [diff] [blame] | 1105 | if (!Pos1) | 
|  | 1106 | return; | 
|  | 1107 |  | 
|  | 1108 | const auto *IterCont1 = Pos1->getContainer(); | 
|  | 1109 |  | 
|  | 1110 | // Skip symbolic regions based on conjured symbols. Two conjured symbols | 
|  | 1111 | // may or may not be the same. For example, the same function can return | 
|  | 1112 | // the same or a different container but we get different conjured symbols | 
|  | 1113 | // for each call. This may cause false positives so omit them from the check. | 
|  | 1114 | if (const auto *ContSym = IterCont1->getSymbolicBase()) { | 
|  | 1115 | if (isa<SymbolConjured>(ContSym->getSymbol())) | 
|  | 1116 | return; | 
|  | 1117 | } | 
|  | 1118 |  | 
| Adam Balogh | 21583b7 | 2018-09-10 09:03:22 +0000 | [diff] [blame] | 1119 | const auto *Pos2 = getIteratorPosition(State, Iter2); | 
| Adam Balogh | d703305 | 2019-03-13 13:55:11 +0000 | [diff] [blame] | 1120 | if (!Pos2) | 
|  | 1121 | return; | 
|  | 1122 |  | 
|  | 1123 | const auto *IterCont2 = Pos2->getContainer(); | 
|  | 1124 | if (const auto *ContSym = IterCont2->getSymbolicBase()) { | 
|  | 1125 | if (isa<SymbolConjured>(ContSym->getSymbol())) | 
|  | 1126 | return; | 
|  | 1127 | } | 
|  | 1128 |  | 
|  | 1129 | if (IterCont1 != IterCont2) { | 
| Adam Balogh | 21583b7 | 2018-09-10 09:03:22 +0000 | [diff] [blame] | 1130 | auto *N = C.generateNonFatalErrorNode(State); | 
|  | 1131 | if (!N) | 
|  | 1132 | return; | 
|  | 1133 | reportMismatchedBug("Iterators of different containers used where the " | 
|  | 1134 | "same container is expected.", Iter1, Iter2, C, N); | 
|  | 1135 | } | 
|  | 1136 | } | 
|  | 1137 |  | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 1138 | void IteratorChecker::handleBegin(CheckerContext &C, const Expr *CE, | 
|  | 1139 | const SVal &RetVal, const SVal &Cont) const { | 
|  | 1140 | const auto *ContReg = Cont.getAsRegion(); | 
|  | 1141 | if (!ContReg) | 
|  | 1142 | return; | 
|  | 1143 |  | 
| Adam Balogh | 42d241f | 2018-12-04 10:22:28 +0000 | [diff] [blame] | 1144 | ContReg = ContReg->getMostDerivedObjectRegion(); | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 1145 |  | 
|  | 1146 | // If the container already has a begin symbol then use it. Otherwise first | 
|  | 1147 | // create a new one. | 
|  | 1148 | auto State = C.getState(); | 
|  | 1149 | auto BeginSym = getContainerBegin(State, ContReg); | 
|  | 1150 | if (!BeginSym) { | 
| Adam Balogh | 33160c4 | 2019-05-20 11:04:27 +0000 | [diff] [blame] | 1151 | State = createContainerBegin(State, ContReg, CE, C.getASTContext().LongTy, | 
|  | 1152 | C.getLocationContext(), C.blockCount()); | 
|  | 1153 | BeginSym = getContainerBegin(State, ContReg); | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 1154 | } | 
|  | 1155 | State = setIteratorPosition(State, RetVal, | 
|  | 1156 | IteratorPosition::getPosition(ContReg, BeginSym)); | 
|  | 1157 | C.addTransition(State); | 
|  | 1158 | } | 
|  | 1159 |  | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 1160 | void IteratorChecker::handleEnd(CheckerContext &C, const Expr *CE, | 
|  | 1161 | const SVal &RetVal, const SVal &Cont) const { | 
|  | 1162 | const auto *ContReg = Cont.getAsRegion(); | 
|  | 1163 | if (!ContReg) | 
|  | 1164 | return; | 
|  | 1165 |  | 
| Adam Balogh | 42d241f | 2018-12-04 10:22:28 +0000 | [diff] [blame] | 1166 | ContReg = ContReg->getMostDerivedObjectRegion(); | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 1167 |  | 
|  | 1168 | // If the container already has an end symbol then use it. Otherwise first | 
|  | 1169 | // create a new one. | 
|  | 1170 | auto State = C.getState(); | 
|  | 1171 | auto EndSym = getContainerEnd(State, ContReg); | 
|  | 1172 | if (!EndSym) { | 
| Adam Balogh | 33160c4 | 2019-05-20 11:04:27 +0000 | [diff] [blame] | 1173 | State = createContainerEnd(State, ContReg, CE, C.getASTContext().LongTy, | 
|  | 1174 | C.getLocationContext(), C.blockCount()); | 
|  | 1175 | EndSym = getContainerEnd(State, ContReg); | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 1176 | } | 
|  | 1177 | State = setIteratorPosition(State, RetVal, | 
|  | 1178 | IteratorPosition::getPosition(ContReg, EndSym)); | 
|  | 1179 | C.addTransition(State); | 
|  | 1180 | } | 
|  | 1181 |  | 
|  | 1182 | void IteratorChecker::assignToContainer(CheckerContext &C, const Expr *CE, | 
|  | 1183 | const SVal &RetVal, | 
|  | 1184 | const MemRegion *Cont) const { | 
| Adam Balogh | 42d241f | 2018-12-04 10:22:28 +0000 | [diff] [blame] | 1185 | Cont = Cont->getMostDerivedObjectRegion(); | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 1186 |  | 
|  | 1187 | auto State = C.getState(); | 
|  | 1188 | auto &SymMgr = C.getSymbolManager(); | 
|  | 1189 | auto Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(), | 
|  | 1190 | C.getASTContext().LongTy, C.blockCount()); | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 1191 | State = assumeNoOverflow(State, Sym, 4); | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 1192 | State = setIteratorPosition(State, RetVal, | 
|  | 1193 | IteratorPosition::getPosition(Cont, Sym)); | 
|  | 1194 | C.addTransition(State); | 
|  | 1195 | } | 
|  | 1196 |  | 
| Adam Balogh | 6b23b1a | 2018-09-10 09:04:27 +0000 | [diff] [blame] | 1197 | void IteratorChecker::handleAssign(CheckerContext &C, const SVal &Cont, | 
|  | 1198 | const Expr *CE, const SVal &OldCont) const { | 
| Adam Balogh | 2cfbe93 | 2018-08-28 08:41:15 +0000 | [diff] [blame] | 1199 | const auto *ContReg = Cont.getAsRegion(); | 
|  | 1200 | if (!ContReg) | 
|  | 1201 | return; | 
|  | 1202 |  | 
| Adam Balogh | 42d241f | 2018-12-04 10:22:28 +0000 | [diff] [blame] | 1203 | ContReg = ContReg->getMostDerivedObjectRegion(); | 
| Adam Balogh | 2cfbe93 | 2018-08-28 08:41:15 +0000 | [diff] [blame] | 1204 |  | 
|  | 1205 | // Assignment of a new value to a container always invalidates all its | 
|  | 1206 | // iterators | 
|  | 1207 | auto State = C.getState(); | 
|  | 1208 | const auto CData = getContainerData(State, ContReg); | 
|  | 1209 | if (CData) { | 
|  | 1210 | State = invalidateAllIteratorPositions(State, ContReg); | 
|  | 1211 | } | 
|  | 1212 |  | 
| Adam Balogh | 6b23b1a | 2018-09-10 09:04:27 +0000 | [diff] [blame] | 1213 | // In case of move, iterators of the old container (except the past-end | 
|  | 1214 | // iterators) remain valid but refer to the new container | 
|  | 1215 | if (!OldCont.isUndef()) { | 
|  | 1216 | const auto *OldContReg = OldCont.getAsRegion(); | 
|  | 1217 | if (OldContReg) { | 
| Adam Balogh | 42d241f | 2018-12-04 10:22:28 +0000 | [diff] [blame] | 1218 | OldContReg = OldContReg->getMostDerivedObjectRegion(); | 
| Adam Balogh | 6b23b1a | 2018-09-10 09:04:27 +0000 | [diff] [blame] | 1219 | const auto OldCData = getContainerData(State, OldContReg); | 
|  | 1220 | if (OldCData) { | 
|  | 1221 | if (const auto OldEndSym = OldCData->getEnd()) { | 
| Raphael Isemann | b23ccec | 2018-12-10 12:37:46 +0000 | [diff] [blame] | 1222 | // If we already assigned an "end" symbol to the old container, then | 
| Adam Balogh | 6b23b1a | 2018-09-10 09:04:27 +0000 | [diff] [blame] | 1223 | // first reassign all iterator positions to the new container which | 
|  | 1224 | // are not past the container (thus not greater or equal to the | 
|  | 1225 | // current "end" symbol). | 
|  | 1226 | State = reassignAllIteratorPositionsUnless(State, OldContReg, ContReg, | 
|  | 1227 | OldEndSym, BO_GE); | 
|  | 1228 | auto &SymMgr = C.getSymbolManager(); | 
|  | 1229 | auto &SVB = C.getSValBuilder(); | 
|  | 1230 | // Then generate and assign a new "end" symbol for the new container. | 
|  | 1231 | auto NewEndSym = | 
|  | 1232 | SymMgr.conjureSymbol(CE, C.getLocationContext(), | 
|  | 1233 | C.getASTContext().LongTy, C.blockCount()); | 
|  | 1234 | State = assumeNoOverflow(State, NewEndSym, 4); | 
|  | 1235 | if (CData) { | 
|  | 1236 | State = setContainerData(State, ContReg, CData->newEnd(NewEndSym)); | 
|  | 1237 | } else { | 
|  | 1238 | State = setContainerData(State, ContReg, | 
|  | 1239 | ContainerData::fromEnd(NewEndSym)); | 
|  | 1240 | } | 
|  | 1241 | // Finally, replace the old "end" symbol in the already reassigned | 
|  | 1242 | // iterator positions with the new "end" symbol. | 
|  | 1243 | State = rebaseSymbolInIteratorPositionsIf( | 
|  | 1244 | State, SVB, OldEndSym, NewEndSym, OldEndSym, BO_LT); | 
|  | 1245 | } else { | 
|  | 1246 | // There was no "end" symbol assigned yet to the old container, | 
|  | 1247 | // so reassign all iterator positions to the new container. | 
|  | 1248 | State = reassignAllIteratorPositions(State, OldContReg, ContReg); | 
|  | 1249 | } | 
|  | 1250 | if (const auto OldBeginSym = OldCData->getBegin()) { | 
|  | 1251 | // If we already assigned a "begin" symbol to the old container, then | 
|  | 1252 | // assign it to the new container and remove it from the old one. | 
|  | 1253 | if (CData) { | 
|  | 1254 | State = | 
|  | 1255 | setContainerData(State, ContReg, CData->newBegin(OldBeginSym)); | 
|  | 1256 | } else { | 
|  | 1257 | State = setContainerData(State, ContReg, | 
|  | 1258 | ContainerData::fromBegin(OldBeginSym)); | 
|  | 1259 | } | 
|  | 1260 | State = | 
|  | 1261 | setContainerData(State, OldContReg, OldCData->newEnd(nullptr)); | 
|  | 1262 | } | 
|  | 1263 | } else { | 
|  | 1264 | // There was neither "begin" nor "end" symbol assigned yet to the old | 
|  | 1265 | // container, so reassign all iterator positions to the new container. | 
|  | 1266 | State = reassignAllIteratorPositions(State, OldContReg, ContReg); | 
|  | 1267 | } | 
|  | 1268 | } | 
|  | 1269 | } | 
| Adam Balogh | 2cfbe93 | 2018-08-28 08:41:15 +0000 | [diff] [blame] | 1270 | C.addTransition(State); | 
|  | 1271 | } | 
|  | 1272 |  | 
| Adam Balogh | 2e7cb34 | 2018-09-10 09:07:47 +0000 | [diff] [blame] | 1273 | void IteratorChecker::handleClear(CheckerContext &C, const SVal &Cont) const { | 
|  | 1274 | const auto *ContReg = Cont.getAsRegion(); | 
|  | 1275 | if (!ContReg) | 
|  | 1276 | return; | 
|  | 1277 |  | 
| Adam Balogh | 42d241f | 2018-12-04 10:22:28 +0000 | [diff] [blame] | 1278 | ContReg = ContReg->getMostDerivedObjectRegion(); | 
| Adam Balogh | 2e7cb34 | 2018-09-10 09:07:47 +0000 | [diff] [blame] | 1279 |  | 
|  | 1280 | // The clear() operation invalidates all the iterators, except the past-end | 
|  | 1281 | // iterators of list-like containers | 
|  | 1282 | auto State = C.getState(); | 
|  | 1283 | if (!hasSubscriptOperator(State, ContReg) || | 
|  | 1284 | !backModifiable(State, ContReg)) { | 
|  | 1285 | const auto CData = getContainerData(State, ContReg); | 
|  | 1286 | if (CData) { | 
|  | 1287 | if (const auto EndSym = CData->getEnd()) { | 
|  | 1288 | State = | 
|  | 1289 | invalidateAllIteratorPositionsExcept(State, ContReg, EndSym, BO_GE); | 
|  | 1290 | C.addTransition(State); | 
|  | 1291 | return; | 
|  | 1292 | } | 
|  | 1293 | } | 
|  | 1294 | } | 
|  | 1295 | State = invalidateAllIteratorPositions(State, ContReg); | 
|  | 1296 | C.addTransition(State); | 
|  | 1297 | } | 
|  | 1298 |  | 
| Adam Balogh | 9a48ba6 | 2018-09-10 09:06:31 +0000 | [diff] [blame] | 1299 | void IteratorChecker::handlePushBack(CheckerContext &C, | 
|  | 1300 | const SVal &Cont) const { | 
|  | 1301 | const auto *ContReg = Cont.getAsRegion(); | 
|  | 1302 | if (!ContReg) | 
|  | 1303 | return; | 
|  | 1304 |  | 
| Adam Balogh | 42d241f | 2018-12-04 10:22:28 +0000 | [diff] [blame] | 1305 | ContReg = ContReg->getMostDerivedObjectRegion(); | 
| Adam Balogh | 9a48ba6 | 2018-09-10 09:06:31 +0000 | [diff] [blame] | 1306 |  | 
|  | 1307 | // For deque-like containers invalidate all iterator positions | 
|  | 1308 | auto State = C.getState(); | 
|  | 1309 | if (hasSubscriptOperator(State, ContReg) && frontModifiable(State, ContReg)) { | 
|  | 1310 | State = invalidateAllIteratorPositions(State, ContReg); | 
|  | 1311 | C.addTransition(State); | 
|  | 1312 | return; | 
|  | 1313 | } | 
|  | 1314 |  | 
|  | 1315 | const auto CData = getContainerData(State, ContReg); | 
|  | 1316 | if (!CData) | 
|  | 1317 | return; | 
|  | 1318 |  | 
|  | 1319 | // For vector-like containers invalidate the past-end iterator positions | 
|  | 1320 | if (const auto EndSym = CData->getEnd()) { | 
|  | 1321 | if (hasSubscriptOperator(State, ContReg)) { | 
|  | 1322 | State = invalidateIteratorPositions(State, EndSym, BO_GE); | 
|  | 1323 | } | 
|  | 1324 | auto &SymMgr = C.getSymbolManager(); | 
|  | 1325 | auto &BVF = SymMgr.getBasicVals(); | 
|  | 1326 | auto &SVB = C.getSValBuilder(); | 
|  | 1327 | const auto newEndSym = | 
|  | 1328 | SVB.evalBinOp(State, BO_Add, | 
|  | 1329 | nonloc::SymbolVal(EndSym), | 
|  | 1330 | nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))), | 
|  | 1331 | SymMgr.getType(EndSym)).getAsSymbol(); | 
|  | 1332 | State = setContainerData(State, ContReg, CData->newEnd(newEndSym)); | 
|  | 1333 | } | 
|  | 1334 | C.addTransition(State); | 
|  | 1335 | } | 
|  | 1336 |  | 
|  | 1337 | void IteratorChecker::handlePopBack(CheckerContext &C, const SVal &Cont) const { | 
|  | 1338 | const auto *ContReg = Cont.getAsRegion(); | 
|  | 1339 | if (!ContReg) | 
|  | 1340 | return; | 
|  | 1341 |  | 
| Adam Balogh | 42d241f | 2018-12-04 10:22:28 +0000 | [diff] [blame] | 1342 | ContReg = ContReg->getMostDerivedObjectRegion(); | 
| Adam Balogh | 9a48ba6 | 2018-09-10 09:06:31 +0000 | [diff] [blame] | 1343 |  | 
|  | 1344 | auto State = C.getState(); | 
|  | 1345 | const auto CData = getContainerData(State, ContReg); | 
|  | 1346 | if (!CData) | 
|  | 1347 | return; | 
|  | 1348 |  | 
|  | 1349 | if (const auto EndSym = CData->getEnd()) { | 
|  | 1350 | auto &SymMgr = C.getSymbolManager(); | 
|  | 1351 | auto &BVF = SymMgr.getBasicVals(); | 
|  | 1352 | auto &SVB = C.getSValBuilder(); | 
|  | 1353 | const auto BackSym = | 
|  | 1354 | SVB.evalBinOp(State, BO_Sub, | 
|  | 1355 | nonloc::SymbolVal(EndSym), | 
|  | 1356 | nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))), | 
|  | 1357 | SymMgr.getType(EndSym)).getAsSymbol(); | 
|  | 1358 | // For vector-like and deque-like containers invalidate the last and the | 
|  | 1359 | // past-end iterator positions. For list-like containers only invalidate | 
|  | 1360 | // the last position | 
|  | 1361 | if (hasSubscriptOperator(State, ContReg) && | 
|  | 1362 | backModifiable(State, ContReg)) { | 
|  | 1363 | State = invalidateIteratorPositions(State, BackSym, BO_GE); | 
|  | 1364 | State = setContainerData(State, ContReg, CData->newEnd(nullptr)); | 
|  | 1365 | } else { | 
|  | 1366 | State = invalidateIteratorPositions(State, BackSym, BO_EQ); | 
|  | 1367 | } | 
|  | 1368 | auto newEndSym = BackSym; | 
|  | 1369 | State = setContainerData(State, ContReg, CData->newEnd(newEndSym)); | 
|  | 1370 | C.addTransition(State); | 
|  | 1371 | } | 
|  | 1372 | } | 
|  | 1373 |  | 
|  | 1374 | void IteratorChecker::handlePushFront(CheckerContext &C, | 
|  | 1375 | const SVal &Cont) const { | 
|  | 1376 | const auto *ContReg = Cont.getAsRegion(); | 
|  | 1377 | if (!ContReg) | 
|  | 1378 | return; | 
|  | 1379 |  | 
| Adam Balogh | 42d241f | 2018-12-04 10:22:28 +0000 | [diff] [blame] | 1380 | ContReg = ContReg->getMostDerivedObjectRegion(); | 
| Adam Balogh | 9a48ba6 | 2018-09-10 09:06:31 +0000 | [diff] [blame] | 1381 |  | 
|  | 1382 | // For deque-like containers invalidate all iterator positions | 
|  | 1383 | auto State = C.getState(); | 
|  | 1384 | if (hasSubscriptOperator(State, ContReg)) { | 
|  | 1385 | State = invalidateAllIteratorPositions(State, ContReg); | 
|  | 1386 | C.addTransition(State); | 
|  | 1387 | } else { | 
|  | 1388 | const auto CData = getContainerData(State, ContReg); | 
|  | 1389 | if (!CData) | 
|  | 1390 | return; | 
|  | 1391 |  | 
|  | 1392 | if (const auto BeginSym = CData->getBegin()) { | 
|  | 1393 | auto &SymMgr = C.getSymbolManager(); | 
|  | 1394 | auto &BVF = SymMgr.getBasicVals(); | 
|  | 1395 | auto &SVB = C.getSValBuilder(); | 
|  | 1396 | const auto newBeginSym = | 
|  | 1397 | SVB.evalBinOp(State, BO_Sub, | 
|  | 1398 | nonloc::SymbolVal(BeginSym), | 
|  | 1399 | nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))), | 
|  | 1400 | SymMgr.getType(BeginSym)).getAsSymbol(); | 
|  | 1401 | State = setContainerData(State, ContReg, CData->newBegin(newBeginSym)); | 
|  | 1402 | C.addTransition(State); | 
|  | 1403 | } | 
|  | 1404 | } | 
|  | 1405 | } | 
|  | 1406 |  | 
|  | 1407 | void IteratorChecker::handlePopFront(CheckerContext &C, | 
|  | 1408 | const SVal &Cont) const { | 
|  | 1409 | const auto *ContReg = Cont.getAsRegion(); | 
|  | 1410 | if (!ContReg) | 
|  | 1411 | return; | 
|  | 1412 |  | 
| Adam Balogh | 42d241f | 2018-12-04 10:22:28 +0000 | [diff] [blame] | 1413 | ContReg = ContReg->getMostDerivedObjectRegion(); | 
| Adam Balogh | 9a48ba6 | 2018-09-10 09:06:31 +0000 | [diff] [blame] | 1414 |  | 
|  | 1415 | auto State = C.getState(); | 
|  | 1416 | const auto CData = getContainerData(State, ContReg); | 
|  | 1417 | if (!CData) | 
|  | 1418 | return; | 
|  | 1419 |  | 
|  | 1420 | // For deque-like containers invalidate all iterator positions. For list-like | 
|  | 1421 | // iterators only invalidate the first position | 
|  | 1422 | if (const auto BeginSym = CData->getBegin()) { | 
|  | 1423 | if (hasSubscriptOperator(State, ContReg)) { | 
|  | 1424 | State = invalidateIteratorPositions(State, BeginSym, BO_LE); | 
|  | 1425 | } else { | 
|  | 1426 | State = invalidateIteratorPositions(State, BeginSym, BO_EQ); | 
|  | 1427 | } | 
|  | 1428 | auto &SymMgr = C.getSymbolManager(); | 
|  | 1429 | auto &BVF = SymMgr.getBasicVals(); | 
|  | 1430 | auto &SVB = C.getSValBuilder(); | 
|  | 1431 | const auto newBeginSym = | 
|  | 1432 | SVB.evalBinOp(State, BO_Add, | 
|  | 1433 | nonloc::SymbolVal(BeginSym), | 
|  | 1434 | nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))), | 
|  | 1435 | SymMgr.getType(BeginSym)).getAsSymbol(); | 
|  | 1436 | State = setContainerData(State, ContReg, CData->newBegin(newBeginSym)); | 
|  | 1437 | C.addTransition(State); | 
|  | 1438 | } | 
|  | 1439 | } | 
|  | 1440 |  | 
| Adam Balogh | 2e7cb34 | 2018-09-10 09:07:47 +0000 | [diff] [blame] | 1441 | void IteratorChecker::handleInsert(CheckerContext &C, const SVal &Iter) const { | 
|  | 1442 | auto State = C.getState(); | 
|  | 1443 | const auto *Pos = getIteratorPosition(State, Iter); | 
|  | 1444 | if (!Pos) | 
|  | 1445 | return; | 
|  | 1446 |  | 
|  | 1447 | // For deque-like containers invalidate all iterator positions. For | 
|  | 1448 | // vector-like containers invalidate iterator positions after the insertion. | 
|  | 1449 | const auto *Cont = Pos->getContainer(); | 
|  | 1450 | if (hasSubscriptOperator(State, Cont) && backModifiable(State, Cont)) { | 
|  | 1451 | if (frontModifiable(State, Cont)) { | 
|  | 1452 | State = invalidateAllIteratorPositions(State, Cont); | 
|  | 1453 | } else { | 
|  | 1454 | State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE); | 
|  | 1455 | } | 
|  | 1456 | if (const auto *CData = getContainerData(State, Cont)) { | 
|  | 1457 | if (const auto EndSym = CData->getEnd()) { | 
|  | 1458 | State = invalidateIteratorPositions(State, EndSym, BO_GE); | 
|  | 1459 | State = setContainerData(State, Cont, CData->newEnd(nullptr)); | 
|  | 1460 | } | 
|  | 1461 | } | 
|  | 1462 | C.addTransition(State); | 
|  | 1463 | } | 
|  | 1464 | } | 
|  | 1465 |  | 
|  | 1466 | void IteratorChecker::handleErase(CheckerContext &C, const SVal &Iter) const { | 
|  | 1467 | auto State = C.getState(); | 
|  | 1468 | const auto *Pos = getIteratorPosition(State, Iter); | 
|  | 1469 | if (!Pos) | 
|  | 1470 | return; | 
|  | 1471 |  | 
|  | 1472 | // For deque-like containers invalidate all iterator positions. For | 
|  | 1473 | // vector-like containers invalidate iterator positions at and after the | 
|  | 1474 | // deletion. For list-like containers only invalidate the deleted position. | 
|  | 1475 | const auto *Cont = Pos->getContainer(); | 
|  | 1476 | if (hasSubscriptOperator(State, Cont) && backModifiable(State, Cont)) { | 
|  | 1477 | if (frontModifiable(State, Cont)) { | 
|  | 1478 | State = invalidateAllIteratorPositions(State, Cont); | 
|  | 1479 | } else { | 
|  | 1480 | State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE); | 
|  | 1481 | } | 
|  | 1482 | if (const auto *CData = getContainerData(State, Cont)) { | 
|  | 1483 | if (const auto EndSym = CData->getEnd()) { | 
|  | 1484 | State = invalidateIteratorPositions(State, EndSym, BO_GE); | 
|  | 1485 | State = setContainerData(State, Cont, CData->newEnd(nullptr)); | 
|  | 1486 | } | 
|  | 1487 | } | 
|  | 1488 | } else { | 
|  | 1489 | State = invalidateIteratorPositions(State, Pos->getOffset(), BO_EQ); | 
|  | 1490 | } | 
|  | 1491 | C.addTransition(State); | 
|  | 1492 | } | 
|  | 1493 |  | 
|  | 1494 | void IteratorChecker::handleErase(CheckerContext &C, const SVal &Iter1, | 
|  | 1495 | const SVal &Iter2) const { | 
|  | 1496 | auto State = C.getState(); | 
|  | 1497 | const auto *Pos1 = getIteratorPosition(State, Iter1); | 
|  | 1498 | const auto *Pos2 = getIteratorPosition(State, Iter2); | 
|  | 1499 | if (!Pos1 || !Pos2) | 
|  | 1500 | return; | 
|  | 1501 |  | 
|  | 1502 | // For deque-like containers invalidate all iterator positions. For | 
|  | 1503 | // vector-like containers invalidate iterator positions at and after the | 
|  | 1504 | // deletion range. For list-like containers only invalidate the deleted | 
|  | 1505 | // position range [first..last]. | 
|  | 1506 | const auto *Cont = Pos1->getContainer(); | 
|  | 1507 | if (hasSubscriptOperator(State, Cont) && backModifiable(State, Cont)) { | 
|  | 1508 | if (frontModifiable(State, Cont)) { | 
|  | 1509 | State = invalidateAllIteratorPositions(State, Cont); | 
|  | 1510 | } else { | 
|  | 1511 | State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE); | 
|  | 1512 | } | 
|  | 1513 | if (const auto *CData = getContainerData(State, Cont)) { | 
|  | 1514 | if (const auto EndSym = CData->getEnd()) { | 
|  | 1515 | State = invalidateIteratorPositions(State, EndSym, BO_GE); | 
|  | 1516 | State = setContainerData(State, Cont, CData->newEnd(nullptr)); | 
|  | 1517 | } | 
|  | 1518 | } | 
|  | 1519 | } else { | 
|  | 1520 | State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE, | 
|  | 1521 | Pos2->getOffset(), BO_LT); | 
|  | 1522 | } | 
|  | 1523 | C.addTransition(State); | 
|  | 1524 | } | 
|  | 1525 |  | 
|  | 1526 | void IteratorChecker::handleEraseAfter(CheckerContext &C, | 
|  | 1527 | const SVal &Iter) const { | 
|  | 1528 | auto State = C.getState(); | 
|  | 1529 | const auto *Pos = getIteratorPosition(State, Iter); | 
|  | 1530 | if (!Pos) | 
|  | 1531 | return; | 
|  | 1532 |  | 
|  | 1533 | // Invalidate the deleted iterator position, which is the position of the | 
|  | 1534 | // parameter plus one. | 
|  | 1535 | auto &SymMgr = C.getSymbolManager(); | 
|  | 1536 | auto &BVF = SymMgr.getBasicVals(); | 
|  | 1537 | auto &SVB = C.getSValBuilder(); | 
|  | 1538 | const auto NextSym = | 
|  | 1539 | SVB.evalBinOp(State, BO_Add, | 
|  | 1540 | nonloc::SymbolVal(Pos->getOffset()), | 
|  | 1541 | nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))), | 
|  | 1542 | SymMgr.getType(Pos->getOffset())).getAsSymbol(); | 
|  | 1543 | State = invalidateIteratorPositions(State, NextSym, BO_EQ); | 
|  | 1544 | C.addTransition(State); | 
|  | 1545 | } | 
|  | 1546 |  | 
|  | 1547 | void IteratorChecker::handleEraseAfter(CheckerContext &C, const SVal &Iter1, | 
|  | 1548 | const SVal &Iter2) const { | 
|  | 1549 | auto State = C.getState(); | 
|  | 1550 | const auto *Pos1 = getIteratorPosition(State, Iter1); | 
|  | 1551 | const auto *Pos2 = getIteratorPosition(State, Iter2); | 
|  | 1552 | if (!Pos1 || !Pos2) | 
|  | 1553 | return; | 
|  | 1554 |  | 
|  | 1555 | // Invalidate the deleted iterator position range (first..last) | 
|  | 1556 | State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GT, | 
|  | 1557 | Pos2->getOffset(), BO_LT); | 
|  | 1558 | C.addTransition(State); | 
|  | 1559 | } | 
|  | 1560 |  | 
| Adam Balogh | d5bd3f6 | 2018-12-04 10:27:27 +0000 | [diff] [blame] | 1561 | IteratorPosition IteratorChecker::advancePosition(CheckerContext &C, | 
|  | 1562 | OverloadedOperatorKind Op, | 
|  | 1563 | const IteratorPosition &Pos, | 
|  | 1564 | const SVal &Distance) const { | 
|  | 1565 | auto State = C.getState(); | 
|  | 1566 | auto &SymMgr = C.getSymbolManager(); | 
|  | 1567 | auto &SVB = C.getSValBuilder(); | 
|  | 1568 |  | 
|  | 1569 | assert ((Op == OO_Plus || Op == OO_PlusEqual || | 
|  | 1570 | Op == OO_Minus || Op == OO_MinusEqual) && | 
|  | 1571 | "Advance operator must be one of +, -, += and -=."); | 
|  | 1572 | auto BinOp = (Op == OO_Plus || Op == OO_PlusEqual) ? BO_Add : BO_Sub; | 
|  | 1573 | if (const auto IntDist = Distance.getAs<nonloc::ConcreteInt>()) { | 
|  | 1574 | // For concrete integers we can calculate the new position | 
|  | 1575 | return Pos.setTo(SVB.evalBinOp(State, BinOp, | 
|  | 1576 | nonloc::SymbolVal(Pos.getOffset()), *IntDist, | 
|  | 1577 | SymMgr.getType(Pos.getOffset())) | 
|  | 1578 | .getAsSymbol()); | 
|  | 1579 | } else { | 
|  | 1580 | // For other symbols create a new symbol to keep expressions simple | 
|  | 1581 | const auto &LCtx = C.getLocationContext(); | 
|  | 1582 | const auto NewPosSym = SymMgr.conjureSymbol(nullptr, LCtx, | 
|  | 1583 | SymMgr.getType(Pos.getOffset()), | 
|  | 1584 | C.blockCount()); | 
|  | 1585 | State = assumeNoOverflow(State, NewPosSym, 4); | 
|  | 1586 | return Pos.setTo(NewPosSym); | 
|  | 1587 | } | 
|  | 1588 | } | 
|  | 1589 |  | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 1590 | void IteratorChecker::reportOutOfRangeBug(const StringRef &Message, | 
|  | 1591 | const SVal &Val, CheckerContext &C, | 
|  | 1592 | ExplodedNode *ErrNode) const { | 
| Jonas Devlieghere | 2b3d49b | 2019-08-14 23:04:18 +0000 | [diff] [blame] | 1593 | auto R = std::make_unique<BugReport>(*OutOfRangeBugType, Message, ErrNode); | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 1594 | R->markInteresting(Val); | 
|  | 1595 | C.emitReport(std::move(R)); | 
|  | 1596 | } | 
|  | 1597 |  | 
| Adam Balogh | 21583b7 | 2018-09-10 09:03:22 +0000 | [diff] [blame] | 1598 | void IteratorChecker::reportMismatchedBug(const StringRef &Message, | 
|  | 1599 | const SVal &Val1, const SVal &Val2, | 
|  | 1600 | CheckerContext &C, | 
|  | 1601 | ExplodedNode *ErrNode) const { | 
| Jonas Devlieghere | 2b3d49b | 2019-08-14 23:04:18 +0000 | [diff] [blame] | 1602 | auto R = std::make_unique<BugReport>(*MismatchedBugType, Message, ErrNode); | 
| Adam Balogh | 21583b7 | 2018-09-10 09:03:22 +0000 | [diff] [blame] | 1603 | R->markInteresting(Val1); | 
|  | 1604 | R->markInteresting(Val2); | 
|  | 1605 | C.emitReport(std::move(R)); | 
|  | 1606 | } | 
|  | 1607 |  | 
| Adam Balogh | 3659f7a | 2018-09-10 09:05:31 +0000 | [diff] [blame] | 1608 | void IteratorChecker::reportMismatchedBug(const StringRef &Message, | 
|  | 1609 | const SVal &Val, const MemRegion *Reg, | 
|  | 1610 | CheckerContext &C, | 
|  | 1611 | ExplodedNode *ErrNode) const { | 
| Jonas Devlieghere | 2b3d49b | 2019-08-14 23:04:18 +0000 | [diff] [blame] | 1612 | auto R = std::make_unique<BugReport>(*MismatchedBugType, Message, ErrNode); | 
| Adam Balogh | 3659f7a | 2018-09-10 09:05:31 +0000 | [diff] [blame] | 1613 | R->markInteresting(Val); | 
|  | 1614 | R->markInteresting(Reg); | 
|  | 1615 | C.emitReport(std::move(R)); | 
|  | 1616 | } | 
|  | 1617 |  | 
| Adam Balogh | 2cfbe93 | 2018-08-28 08:41:15 +0000 | [diff] [blame] | 1618 | void IteratorChecker::reportInvalidatedBug(const StringRef &Message, | 
|  | 1619 | const SVal &Val, CheckerContext &C, | 
|  | 1620 | ExplodedNode *ErrNode) const { | 
| Jonas Devlieghere | 2b3d49b | 2019-08-14 23:04:18 +0000 | [diff] [blame] | 1621 | auto R = std::make_unique<BugReport>(*InvalidatedBugType, Message, ErrNode); | 
| Adam Balogh | 2cfbe93 | 2018-08-28 08:41:15 +0000 | [diff] [blame] | 1622 | R->markInteresting(Val); | 
|  | 1623 | C.emitReport(std::move(R)); | 
|  | 1624 | } | 
|  | 1625 |  | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 1626 | namespace { | 
|  | 1627 |  | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 1628 | bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2); | 
| Adam Balogh | d5bd3f6 | 2018-12-04 10:27:27 +0000 | [diff] [blame] | 1629 | bool isGreater(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2); | 
|  | 1630 | bool isEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2); | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 1631 | bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2, | 
|  | 1632 | BinaryOperator::Opcode Opc); | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 1633 | bool compare(ProgramStateRef State, NonLoc NL1, NonLoc NL2, | 
|  | 1634 | BinaryOperator::Opcode Opc); | 
| Adam Balogh | 9a48ba6 | 2018-09-10 09:06:31 +0000 | [diff] [blame] | 1635 | const CXXRecordDecl *getCXXRecordDecl(ProgramStateRef State, | 
|  | 1636 | const MemRegion *Reg); | 
| Adam Balogh | 6b23b1a | 2018-09-10 09:04:27 +0000 | [diff] [blame] | 1637 | SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB, SymbolRef Expr, | 
|  | 1638 | SymbolRef OldSym, SymbolRef NewSym); | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 1639 |  | 
|  | 1640 | bool isIteratorType(const QualType &Type) { | 
|  | 1641 | if (Type->isPointerType()) | 
|  | 1642 | return true; | 
|  | 1643 |  | 
|  | 1644 | const auto *CRD = Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl(); | 
|  | 1645 | return isIterator(CRD); | 
|  | 1646 | } | 
|  | 1647 |  | 
|  | 1648 | bool isIterator(const CXXRecordDecl *CRD) { | 
|  | 1649 | if (!CRD) | 
|  | 1650 | return false; | 
|  | 1651 |  | 
|  | 1652 | const auto Name = CRD->getName(); | 
|  | 1653 | if (!(Name.endswith_lower("iterator") || Name.endswith_lower("iter") || | 
|  | 1654 | Name.endswith_lower("it"))) | 
|  | 1655 | return false; | 
|  | 1656 |  | 
|  | 1657 | bool HasCopyCtor = false, HasCopyAssign = true, HasDtor = false, | 
|  | 1658 | HasPreIncrOp = false, HasPostIncrOp = false, HasDerefOp = false; | 
|  | 1659 | for (const auto *Method : CRD->methods()) { | 
|  | 1660 | if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(Method)) { | 
|  | 1661 | if (Ctor->isCopyConstructor()) { | 
|  | 1662 | HasCopyCtor = !Ctor->isDeleted() && Ctor->getAccess() == AS_public; | 
|  | 1663 | } | 
|  | 1664 | continue; | 
|  | 1665 | } | 
|  | 1666 | if (const auto *Dtor = dyn_cast<CXXDestructorDecl>(Method)) { | 
|  | 1667 | HasDtor = !Dtor->isDeleted() && Dtor->getAccess() == AS_public; | 
|  | 1668 | continue; | 
|  | 1669 | } | 
|  | 1670 | if (Method->isCopyAssignmentOperator()) { | 
|  | 1671 | HasCopyAssign = !Method->isDeleted() && Method->getAccess() == AS_public; | 
|  | 1672 | continue; | 
|  | 1673 | } | 
|  | 1674 | if (!Method->isOverloadedOperator()) | 
|  | 1675 | continue; | 
|  | 1676 | const auto OPK = Method->getOverloadedOperator(); | 
|  | 1677 | if (OPK == OO_PlusPlus) { | 
|  | 1678 | HasPreIncrOp = HasPreIncrOp || (Method->getNumParams() == 0); | 
|  | 1679 | HasPostIncrOp = HasPostIncrOp || (Method->getNumParams() == 1); | 
|  | 1680 | continue; | 
|  | 1681 | } | 
|  | 1682 | if (OPK == OO_Star) { | 
|  | 1683 | HasDerefOp = (Method->getNumParams() == 0); | 
|  | 1684 | continue; | 
|  | 1685 | } | 
|  | 1686 | } | 
|  | 1687 |  | 
|  | 1688 | return HasCopyCtor && HasCopyAssign && HasDtor && HasPreIncrOp && | 
|  | 1689 | HasPostIncrOp && HasDerefOp; | 
|  | 1690 | } | 
|  | 1691 |  | 
| Adam Balogh | 3659f7a | 2018-09-10 09:05:31 +0000 | [diff] [blame] | 1692 | bool isComparisonOperator(OverloadedOperatorKind OK) { | 
|  | 1693 | return OK == OO_EqualEqual || OK == OO_ExclaimEqual || OK == OO_Less || | 
|  | 1694 | OK == OO_LessEqual || OK == OO_Greater || OK == OO_GreaterEqual; | 
|  | 1695 | } | 
|  | 1696 |  | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 1697 | bool isBeginCall(const FunctionDecl *Func) { | 
|  | 1698 | const auto *IdInfo = Func->getIdentifier(); | 
|  | 1699 | if (!IdInfo) | 
|  | 1700 | return false; | 
|  | 1701 | return IdInfo->getName().endswith_lower("begin"); | 
|  | 1702 | } | 
|  | 1703 |  | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 1704 | bool isEndCall(const FunctionDecl *Func) { | 
|  | 1705 | const auto *IdInfo = Func->getIdentifier(); | 
|  | 1706 | if (!IdInfo) | 
|  | 1707 | return false; | 
|  | 1708 | return IdInfo->getName().endswith_lower("end"); | 
|  | 1709 | } | 
|  | 1710 |  | 
| Adam Balogh | 2e7cb34 | 2018-09-10 09:07:47 +0000 | [diff] [blame] | 1711 | bool isAssignCall(const FunctionDecl *Func) { | 
|  | 1712 | const auto *IdInfo = Func->getIdentifier(); | 
|  | 1713 | if (!IdInfo) | 
|  | 1714 | return false; | 
|  | 1715 | if (Func->getNumParams() > 2) | 
|  | 1716 | return false; | 
|  | 1717 | return IdInfo->getName() == "assign"; | 
|  | 1718 | } | 
|  | 1719 |  | 
|  | 1720 | bool isClearCall(const FunctionDecl *Func) { | 
|  | 1721 | const auto *IdInfo = Func->getIdentifier(); | 
|  | 1722 | if (!IdInfo) | 
|  | 1723 | return false; | 
|  | 1724 | if (Func->getNumParams() > 0) | 
|  | 1725 | return false; | 
|  | 1726 | return IdInfo->getName() == "clear"; | 
|  | 1727 | } | 
|  | 1728 |  | 
| Adam Balogh | 9a48ba6 | 2018-09-10 09:06:31 +0000 | [diff] [blame] | 1729 | bool isPushBackCall(const FunctionDecl *Func) { | 
|  | 1730 | const auto *IdInfo = Func->getIdentifier(); | 
|  | 1731 | if (!IdInfo) | 
|  | 1732 | return false; | 
|  | 1733 | if (Func->getNumParams() != 1) | 
|  | 1734 | return false; | 
|  | 1735 | return IdInfo->getName() == "push_back"; | 
|  | 1736 | } | 
|  | 1737 |  | 
|  | 1738 | bool isEmplaceBackCall(const FunctionDecl *Func) { | 
|  | 1739 | const auto *IdInfo = Func->getIdentifier(); | 
|  | 1740 | if (!IdInfo) | 
|  | 1741 | return false; | 
|  | 1742 | if (Func->getNumParams() < 1) | 
|  | 1743 | return false; | 
|  | 1744 | return IdInfo->getName() == "emplace_back"; | 
|  | 1745 | } | 
|  | 1746 |  | 
|  | 1747 | bool isPopBackCall(const FunctionDecl *Func) { | 
|  | 1748 | const auto *IdInfo = Func->getIdentifier(); | 
|  | 1749 | if (!IdInfo) | 
|  | 1750 | return false; | 
|  | 1751 | if (Func->getNumParams() > 0) | 
|  | 1752 | return false; | 
|  | 1753 | return IdInfo->getName() == "pop_back"; | 
|  | 1754 | } | 
|  | 1755 |  | 
|  | 1756 | bool isPushFrontCall(const FunctionDecl *Func) { | 
|  | 1757 | const auto *IdInfo = Func->getIdentifier(); | 
|  | 1758 | if (!IdInfo) | 
|  | 1759 | return false; | 
|  | 1760 | if (Func->getNumParams() != 1) | 
|  | 1761 | return false; | 
|  | 1762 | return IdInfo->getName() == "push_front"; | 
|  | 1763 | } | 
|  | 1764 |  | 
|  | 1765 | bool isEmplaceFrontCall(const FunctionDecl *Func) { | 
|  | 1766 | const auto *IdInfo = Func->getIdentifier(); | 
|  | 1767 | if (!IdInfo) | 
|  | 1768 | return false; | 
|  | 1769 | if (Func->getNumParams() < 1) | 
|  | 1770 | return false; | 
|  | 1771 | return IdInfo->getName() == "emplace_front"; | 
|  | 1772 | } | 
|  | 1773 |  | 
|  | 1774 | bool isPopFrontCall(const FunctionDecl *Func) { | 
|  | 1775 | const auto *IdInfo = Func->getIdentifier(); | 
|  | 1776 | if (!IdInfo) | 
|  | 1777 | return false; | 
|  | 1778 | if (Func->getNumParams() > 0) | 
|  | 1779 | return false; | 
|  | 1780 | return IdInfo->getName() == "pop_front"; | 
|  | 1781 | } | 
|  | 1782 |  | 
| Adam Balogh | 2e7cb34 | 2018-09-10 09:07:47 +0000 | [diff] [blame] | 1783 | bool isInsertCall(const FunctionDecl *Func) { | 
|  | 1784 | const auto *IdInfo = Func->getIdentifier(); | 
|  | 1785 | if (!IdInfo) | 
|  | 1786 | return false; | 
|  | 1787 | if (Func->getNumParams() < 2 || Func->getNumParams() > 3) | 
|  | 1788 | return false; | 
|  | 1789 | if (!isIteratorType(Func->getParamDecl(0)->getType())) | 
|  | 1790 | return false; | 
|  | 1791 | return IdInfo->getName() == "insert"; | 
|  | 1792 | } | 
|  | 1793 |  | 
|  | 1794 | bool isEmplaceCall(const FunctionDecl *Func) { | 
|  | 1795 | const auto *IdInfo = Func->getIdentifier(); | 
|  | 1796 | if (!IdInfo) | 
|  | 1797 | return false; | 
|  | 1798 | if (Func->getNumParams() < 2) | 
|  | 1799 | return false; | 
|  | 1800 | if (!isIteratorType(Func->getParamDecl(0)->getType())) | 
|  | 1801 | return false; | 
|  | 1802 | return IdInfo->getName() == "emplace"; | 
|  | 1803 | } | 
|  | 1804 |  | 
|  | 1805 | bool isEraseCall(const FunctionDecl *Func) { | 
|  | 1806 | const auto *IdInfo = Func->getIdentifier(); | 
|  | 1807 | if (!IdInfo) | 
|  | 1808 | return false; | 
|  | 1809 | if (Func->getNumParams() < 1 || Func->getNumParams() > 2) | 
|  | 1810 | return false; | 
|  | 1811 | if (!isIteratorType(Func->getParamDecl(0)->getType())) | 
|  | 1812 | return false; | 
|  | 1813 | if (Func->getNumParams() == 2 && | 
|  | 1814 | !isIteratorType(Func->getParamDecl(1)->getType())) | 
|  | 1815 | return false; | 
|  | 1816 | return IdInfo->getName() == "erase"; | 
|  | 1817 | } | 
|  | 1818 |  | 
|  | 1819 | bool isEraseAfterCall(const FunctionDecl *Func) { | 
|  | 1820 | const auto *IdInfo = Func->getIdentifier(); | 
|  | 1821 | if (!IdInfo) | 
|  | 1822 | return false; | 
|  | 1823 | if (Func->getNumParams() < 1 || Func->getNumParams() > 2) | 
|  | 1824 | return false; | 
|  | 1825 | if (!isIteratorType(Func->getParamDecl(0)->getType())) | 
|  | 1826 | return false; | 
|  | 1827 | if (Func->getNumParams() == 2 && | 
|  | 1828 | !isIteratorType(Func->getParamDecl(1)->getType())) | 
|  | 1829 | return false; | 
|  | 1830 | return IdInfo->getName() == "erase_after"; | 
|  | 1831 | } | 
|  | 1832 |  | 
| Adam Balogh | 2cfbe93 | 2018-08-28 08:41:15 +0000 | [diff] [blame] | 1833 | bool isAssignmentOperator(OverloadedOperatorKind OK) { return OK == OO_Equal; } | 
|  | 1834 |  | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 1835 | bool isSimpleComparisonOperator(OverloadedOperatorKind OK) { | 
|  | 1836 | return OK == OO_EqualEqual || OK == OO_ExclaimEqual; | 
|  | 1837 | } | 
|  | 1838 |  | 
| Adam Balogh | 2cfbe93 | 2018-08-28 08:41:15 +0000 | [diff] [blame] | 1839 | bool isAccessOperator(OverloadedOperatorKind OK) { | 
|  | 1840 | return isDereferenceOperator(OK) || isIncrementOperator(OK) || | 
|  | 1841 | isDecrementOperator(OK) || isRandomIncrOrDecrOperator(OK); | 
|  | 1842 | } | 
|  | 1843 |  | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 1844 | bool isDereferenceOperator(OverloadedOperatorKind OK) { | 
|  | 1845 | return OK == OO_Star || OK == OO_Arrow || OK == OO_ArrowStar || | 
|  | 1846 | OK == OO_Subscript; | 
|  | 1847 | } | 
|  | 1848 |  | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 1849 | bool isIncrementOperator(OverloadedOperatorKind OK) { | 
|  | 1850 | return OK == OO_PlusPlus; | 
|  | 1851 | } | 
|  | 1852 |  | 
|  | 1853 | bool isDecrementOperator(OverloadedOperatorKind OK) { | 
|  | 1854 | return OK == OO_MinusMinus; | 
|  | 1855 | } | 
|  | 1856 |  | 
|  | 1857 | bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK) { | 
|  | 1858 | return OK == OO_Plus || OK == OO_PlusEqual || OK == OO_Minus || | 
|  | 1859 | OK == OO_MinusEqual; | 
|  | 1860 | } | 
|  | 1861 |  | 
| Adam Balogh | 9a48ba6 | 2018-09-10 09:06:31 +0000 | [diff] [blame] | 1862 | bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg) { | 
|  | 1863 | const auto *CRD = getCXXRecordDecl(State, Reg); | 
|  | 1864 | if (!CRD) | 
|  | 1865 | return false; | 
|  | 1866 |  | 
|  | 1867 | for (const auto *Method : CRD->methods()) { | 
|  | 1868 | if (!Method->isOverloadedOperator()) | 
|  | 1869 | continue; | 
|  | 1870 | const auto OPK = Method->getOverloadedOperator(); | 
|  | 1871 | if (OPK == OO_Subscript) { | 
|  | 1872 | return true; | 
|  | 1873 | } | 
|  | 1874 | } | 
|  | 1875 | return false; | 
|  | 1876 | } | 
|  | 1877 |  | 
|  | 1878 | bool frontModifiable(ProgramStateRef State, const MemRegion *Reg) { | 
|  | 1879 | const auto *CRD = getCXXRecordDecl(State, Reg); | 
|  | 1880 | if (!CRD) | 
|  | 1881 | return false; | 
|  | 1882 |  | 
|  | 1883 | for (const auto *Method : CRD->methods()) { | 
|  | 1884 | if (!Method->getDeclName().isIdentifier()) | 
|  | 1885 | continue; | 
|  | 1886 | if (Method->getName() == "push_front" || Method->getName() == "pop_front") { | 
|  | 1887 | return true; | 
|  | 1888 | } | 
|  | 1889 | } | 
|  | 1890 | return false; | 
|  | 1891 | } | 
|  | 1892 |  | 
|  | 1893 | bool backModifiable(ProgramStateRef State, const MemRegion *Reg) { | 
|  | 1894 | const auto *CRD = getCXXRecordDecl(State, Reg); | 
|  | 1895 | if (!CRD) | 
|  | 1896 | return false; | 
|  | 1897 |  | 
|  | 1898 | for (const auto *Method : CRD->methods()) { | 
|  | 1899 | if (!Method->getDeclName().isIdentifier()) | 
|  | 1900 | continue; | 
|  | 1901 | if (Method->getName() == "push_back" || Method->getName() == "pop_back") { | 
|  | 1902 | return true; | 
|  | 1903 | } | 
|  | 1904 | } | 
|  | 1905 | return false; | 
|  | 1906 | } | 
|  | 1907 |  | 
|  | 1908 | const CXXRecordDecl *getCXXRecordDecl(ProgramStateRef State, | 
|  | 1909 | const MemRegion *Reg) { | 
|  | 1910 | auto TI = getDynamicTypeInfo(State, Reg); | 
|  | 1911 | if (!TI.isValid()) | 
|  | 1912 | return nullptr; | 
|  | 1913 |  | 
|  | 1914 | auto Type = TI.getType(); | 
|  | 1915 | if (const auto *RefT = Type->getAs<ReferenceType>()) { | 
|  | 1916 | Type = RefT->getPointeeType(); | 
|  | 1917 | } | 
|  | 1918 |  | 
|  | 1919 | return Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl(); | 
|  | 1920 | } | 
|  | 1921 |  | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 1922 | SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont) { | 
|  | 1923 | const auto *CDataPtr = getContainerData(State, Cont); | 
|  | 1924 | if (!CDataPtr) | 
|  | 1925 | return nullptr; | 
|  | 1926 |  | 
|  | 1927 | return CDataPtr->getBegin(); | 
|  | 1928 | } | 
|  | 1929 |  | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 1930 | SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont) { | 
|  | 1931 | const auto *CDataPtr = getContainerData(State, Cont); | 
|  | 1932 | if (!CDataPtr) | 
|  | 1933 | return nullptr; | 
|  | 1934 |  | 
|  | 1935 | return CDataPtr->getEnd(); | 
|  | 1936 | } | 
|  | 1937 |  | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 1938 | ProgramStateRef createContainerBegin(ProgramStateRef State, | 
| Adam Balogh | 33160c4 | 2019-05-20 11:04:27 +0000 | [diff] [blame] | 1939 | const MemRegion *Cont, const Expr *E, | 
|  | 1940 | QualType T, const LocationContext *LCtx, | 
|  | 1941 | unsigned BlockCount) { | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 1942 | // Only create if it does not exist | 
|  | 1943 | const auto *CDataPtr = getContainerData(State, Cont); | 
| Adam Balogh | 33160c4 | 2019-05-20 11:04:27 +0000 | [diff] [blame] | 1944 | if (CDataPtr && CDataPtr->getBegin()) | 
|  | 1945 | return State; | 
|  | 1946 |  | 
|  | 1947 | auto &SymMgr = State->getSymbolManager(); | 
|  | 1948 | const SymbolConjured *Sym = SymMgr.conjureSymbol(E, LCtx, T, BlockCount, | 
|  | 1949 | "begin"); | 
|  | 1950 | State = assumeNoOverflow(State, Sym, 4); | 
|  | 1951 |  | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 1952 | if (CDataPtr) { | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 1953 | const auto CData = CDataPtr->newBegin(Sym); | 
|  | 1954 | return setContainerData(State, Cont, CData); | 
|  | 1955 | } | 
| Adam Balogh | 33160c4 | 2019-05-20 11:04:27 +0000 | [diff] [blame] | 1956 |  | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 1957 | const auto CData = ContainerData::fromBegin(Sym); | 
|  | 1958 | return setContainerData(State, Cont, CData); | 
|  | 1959 | } | 
|  | 1960 |  | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 1961 | ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont, | 
| Adam Balogh | 33160c4 | 2019-05-20 11:04:27 +0000 | [diff] [blame] | 1962 | const Expr *E, QualType T, | 
|  | 1963 | const LocationContext *LCtx, | 
|  | 1964 | unsigned BlockCount) { | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 1965 | // Only create if it does not exist | 
|  | 1966 | const auto *CDataPtr = getContainerData(State, Cont); | 
| Adam Balogh | 33160c4 | 2019-05-20 11:04:27 +0000 | [diff] [blame] | 1967 | if (CDataPtr && CDataPtr->getEnd()) | 
|  | 1968 | return State; | 
|  | 1969 |  | 
|  | 1970 | auto &SymMgr = State->getSymbolManager(); | 
|  | 1971 | const SymbolConjured *Sym = SymMgr.conjureSymbol(E, LCtx, T, BlockCount, | 
|  | 1972 | "end"); | 
|  | 1973 | State = assumeNoOverflow(State, Sym, 4); | 
|  | 1974 |  | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 1975 | if (CDataPtr) { | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 1976 | const auto CData = CDataPtr->newEnd(Sym); | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 1977 | return setContainerData(State, Cont, CData); | 
|  | 1978 | } | 
| Adam Balogh | 33160c4 | 2019-05-20 11:04:27 +0000 | [diff] [blame] | 1979 |  | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 1980 | const auto CData = ContainerData::fromEnd(Sym); | 
|  | 1981 | return setContainerData(State, Cont, CData); | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 1982 | } | 
|  | 1983 |  | 
|  | 1984 | const ContainerData *getContainerData(ProgramStateRef State, | 
|  | 1985 | const MemRegion *Cont) { | 
|  | 1986 | return State->get<ContainerMap>(Cont); | 
|  | 1987 | } | 
|  | 1988 |  | 
|  | 1989 | ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont, | 
|  | 1990 | const ContainerData &CData) { | 
|  | 1991 | return State->set<ContainerMap>(Cont, CData); | 
|  | 1992 | } | 
|  | 1993 |  | 
|  | 1994 | const IteratorPosition *getIteratorPosition(ProgramStateRef State, | 
|  | 1995 | const SVal &Val) { | 
| Adam Balogh | 42d241f | 2018-12-04 10:22:28 +0000 | [diff] [blame] | 1996 | if (auto Reg = Val.getAsRegion()) { | 
|  | 1997 | Reg = Reg->getMostDerivedObjectRegion(); | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 1998 | return State->get<IteratorRegionMap>(Reg); | 
|  | 1999 | } else if (const auto Sym = Val.getAsSymbol()) { | 
|  | 2000 | return State->get<IteratorSymbolMap>(Sym); | 
|  | 2001 | } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) { | 
|  | 2002 | return State->get<IteratorRegionMap>(LCVal->getRegion()); | 
|  | 2003 | } | 
|  | 2004 | return nullptr; | 
|  | 2005 | } | 
|  | 2006 |  | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 2007 | ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val, | 
|  | 2008 | const IteratorPosition &Pos) { | 
| Adam Balogh | 42d241f | 2018-12-04 10:22:28 +0000 | [diff] [blame] | 2009 | if (auto Reg = Val.getAsRegion()) { | 
|  | 2010 | Reg = Reg->getMostDerivedObjectRegion(); | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 2011 | return State->set<IteratorRegionMap>(Reg, Pos); | 
|  | 2012 | } else if (const auto Sym = Val.getAsSymbol()) { | 
|  | 2013 | return State->set<IteratorSymbolMap>(Sym, Pos); | 
|  | 2014 | } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) { | 
|  | 2015 | return State->set<IteratorRegionMap>(LCVal->getRegion(), Pos); | 
|  | 2016 | } | 
|  | 2017 | return nullptr; | 
|  | 2018 | } | 
|  | 2019 |  | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 2020 | ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val) { | 
| Adam Balogh | 42d241f | 2018-12-04 10:22:28 +0000 | [diff] [blame] | 2021 | if (auto Reg = Val.getAsRegion()) { | 
|  | 2022 | Reg = Reg->getMostDerivedObjectRegion(); | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 2023 | return State->remove<IteratorRegionMap>(Reg); | 
|  | 2024 | } else if (const auto Sym = Val.getAsSymbol()) { | 
|  | 2025 | return State->remove<IteratorSymbolMap>(Sym); | 
|  | 2026 | } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) { | 
|  | 2027 | return State->remove<IteratorRegionMap>(LCVal->getRegion()); | 
|  | 2028 | } | 
|  | 2029 | return nullptr; | 
|  | 2030 | } | 
|  | 2031 |  | 
| Adam Balogh | 54976e7 | 2019-04-23 07:15:55 +0000 | [diff] [blame] | 2032 | ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1, | 
|  | 2033 | SymbolRef Sym2, bool Equal) { | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 2034 | auto &SVB = State->getStateManager().getSValBuilder(); | 
| Adam Balogh | 0a7592b | 2018-07-16 09:27:27 +0000 | [diff] [blame] | 2035 |  | 
|  | 2036 | // FIXME: This code should be reworked as follows: | 
|  | 2037 | // 1. Subtract the operands using evalBinOp(). | 
|  | 2038 | // 2. Assume that the result doesn't overflow. | 
|  | 2039 | // 3. Compare the result to 0. | 
|  | 2040 | // 4. Assume the result of the comparison. | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 2041 | const auto comparison = | 
| Adam Balogh | 54976e7 | 2019-04-23 07:15:55 +0000 | [diff] [blame] | 2042 | SVB.evalBinOp(State, BO_EQ, nonloc::SymbolVal(Sym1), | 
|  | 2043 | nonloc::SymbolVal(Sym2), SVB.getConditionType()); | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 2044 |  | 
| Adam Balogh | 0a7592b | 2018-07-16 09:27:27 +0000 | [diff] [blame] | 2045 | assert(comparison.getAs<DefinedSVal>() && | 
|  | 2046 | "Symbol comparison must be a `DefinedSVal`"); | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 2047 |  | 
| Adam Balogh | 0a7592b | 2018-07-16 09:27:27 +0000 | [diff] [blame] | 2048 | auto NewState = State->assume(comparison.castAs<DefinedSVal>(), Equal); | 
| Adam Balogh | 54976e7 | 2019-04-23 07:15:55 +0000 | [diff] [blame] | 2049 | if (!NewState) | 
|  | 2050 | return nullptr; | 
|  | 2051 |  | 
| Adam Balogh | 0a7592b | 2018-07-16 09:27:27 +0000 | [diff] [blame] | 2052 | if (const auto CompSym = comparison.getAsSymbol()) { | 
|  | 2053 | assert(isa<SymIntExpr>(CompSym) && | 
|  | 2054 | "Symbol comparison must be a `SymIntExpr`"); | 
|  | 2055 | assert(BinaryOperator::isComparisonOp( | 
|  | 2056 | cast<SymIntExpr>(CompSym)->getOpcode()) && | 
|  | 2057 | "Symbol comparison must be a comparison"); | 
|  | 2058 | return assumeNoOverflow(NewState, cast<SymIntExpr>(CompSym)->getLHS(), 2); | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 2059 | } | 
|  | 2060 |  | 
| Adam Balogh | 0a7592b | 2018-07-16 09:27:27 +0000 | [diff] [blame] | 2061 | return NewState; | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 2062 | } | 
|  | 2063 |  | 
| Adam Balogh | a692120 | 2018-07-30 08:52:21 +0000 | [diff] [blame] | 2064 | bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont) { | 
|  | 2065 | auto RegionMap = State->get<IteratorRegionMap>(); | 
|  | 2066 | for (const auto Reg : RegionMap) { | 
|  | 2067 | if (Reg.second.getContainer() == Cont) | 
|  | 2068 | return true; | 
|  | 2069 | } | 
|  | 2070 |  | 
|  | 2071 | auto SymbolMap = State->get<IteratorSymbolMap>(); | 
|  | 2072 | for (const auto Sym : SymbolMap) { | 
|  | 2073 | if (Sym.second.getContainer() == Cont) | 
|  | 2074 | return true; | 
|  | 2075 | } | 
|  | 2076 |  | 
|  | 2077 | return false; | 
|  | 2078 | } | 
|  | 2079 |  | 
| Adam Balogh | 21583b7 | 2018-09-10 09:03:22 +0000 | [diff] [blame] | 2080 | bool isBoundThroughLazyCompoundVal(const Environment &Env, | 
|  | 2081 | const MemRegion *Reg) { | 
|  | 2082 | for (const auto Binding: Env) { | 
|  | 2083 | if (const auto LCVal = Binding.second.getAs<nonloc::LazyCompoundVal>()) { | 
|  | 2084 | if (LCVal->getRegion() == Reg) | 
|  | 2085 | return true; | 
|  | 2086 | } | 
|  | 2087 | } | 
|  | 2088 |  | 
|  | 2089 | return false; | 
|  | 2090 | } | 
|  | 2091 |  | 
| Adam Balogh | 54976e7 | 2019-04-23 07:15:55 +0000 | [diff] [blame] | 2092 | // This function tells the analyzer's engine that symbols produced by our | 
|  | 2093 | // checker, most notably iterator positions, are relatively small. | 
|  | 2094 | // A distance between items in the container should not be very large. | 
|  | 2095 | // By assuming that it is within around 1/8 of the address space, | 
|  | 2096 | // we can help the analyzer perform operations on these symbols | 
|  | 2097 | // without being afraid of integer overflows. | 
|  | 2098 | // FIXME: Should we provide it as an API, so that all checkers could use it? | 
|  | 2099 | ProgramStateRef assumeNoOverflow(ProgramStateRef State, SymbolRef Sym, | 
|  | 2100 | long Scale) { | 
|  | 2101 | SValBuilder &SVB = State->getStateManager().getSValBuilder(); | 
|  | 2102 | BasicValueFactory &BV = SVB.getBasicValueFactory(); | 
|  | 2103 |  | 
|  | 2104 | QualType T = Sym->getType(); | 
|  | 2105 | assert(T->isSignedIntegerOrEnumerationType()); | 
|  | 2106 | APSIntType AT = BV.getAPSIntType(T); | 
|  | 2107 |  | 
|  | 2108 | ProgramStateRef NewState = State; | 
|  | 2109 |  | 
|  | 2110 | llvm::APSInt Max = AT.getMaxValue() / AT.getValue(Scale); | 
|  | 2111 | SVal IsCappedFromAbove = | 
|  | 2112 | SVB.evalBinOpNN(State, BO_LE, nonloc::SymbolVal(Sym), | 
|  | 2113 | nonloc::ConcreteInt(Max), SVB.getConditionType()); | 
|  | 2114 | if (auto DV = IsCappedFromAbove.getAs<DefinedSVal>()) { | 
|  | 2115 | NewState = NewState->assume(*DV, true); | 
|  | 2116 | if (!NewState) | 
|  | 2117 | return State; | 
|  | 2118 | } | 
|  | 2119 |  | 
|  | 2120 | llvm::APSInt Min = -Max; | 
|  | 2121 | SVal IsCappedFromBelow = | 
|  | 2122 | SVB.evalBinOpNN(State, BO_GE, nonloc::SymbolVal(Sym), | 
|  | 2123 | nonloc::ConcreteInt(Min), SVB.getConditionType()); | 
|  | 2124 | if (auto DV = IsCappedFromBelow.getAs<DefinedSVal>()) { | 
|  | 2125 | NewState = NewState->assume(*DV, true); | 
|  | 2126 | if (!NewState) | 
|  | 2127 | return State; | 
|  | 2128 | } | 
|  | 2129 |  | 
|  | 2130 | return NewState; | 
|  | 2131 | } | 
|  | 2132 |  | 
| Adam Balogh | 2cfbe93 | 2018-08-28 08:41:15 +0000 | [diff] [blame] | 2133 | template <typename Condition, typename Process> | 
|  | 2134 | ProgramStateRef processIteratorPositions(ProgramStateRef State, Condition Cond, | 
|  | 2135 | Process Proc) { | 
|  | 2136 | auto &RegionMapFactory = State->get_context<IteratorRegionMap>(); | 
|  | 2137 | auto RegionMap = State->get<IteratorRegionMap>(); | 
|  | 2138 | bool Changed = false; | 
|  | 2139 | for (const auto Reg : RegionMap) { | 
|  | 2140 | if (Cond(Reg.second)) { | 
|  | 2141 | RegionMap = RegionMapFactory.add(RegionMap, Reg.first, Proc(Reg.second)); | 
|  | 2142 | Changed = true; | 
|  | 2143 | } | 
|  | 2144 | } | 
|  | 2145 |  | 
|  | 2146 | if (Changed) | 
|  | 2147 | State = State->set<IteratorRegionMap>(RegionMap); | 
|  | 2148 |  | 
|  | 2149 | auto &SymbolMapFactory = State->get_context<IteratorSymbolMap>(); | 
|  | 2150 | auto SymbolMap = State->get<IteratorSymbolMap>(); | 
|  | 2151 | Changed = false; | 
|  | 2152 | for (const auto Sym : SymbolMap) { | 
|  | 2153 | if (Cond(Sym.second)) { | 
|  | 2154 | SymbolMap = SymbolMapFactory.add(SymbolMap, Sym.first, Proc(Sym.second)); | 
|  | 2155 | Changed = true; | 
|  | 2156 | } | 
|  | 2157 | } | 
|  | 2158 |  | 
|  | 2159 | if (Changed) | 
|  | 2160 | State = State->set<IteratorSymbolMap>(SymbolMap); | 
|  | 2161 |  | 
|  | 2162 | return State; | 
|  | 2163 | } | 
|  | 2164 |  | 
|  | 2165 | ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State, | 
|  | 2166 | const MemRegion *Cont) { | 
|  | 2167 | auto MatchCont = [&](const IteratorPosition &Pos) { | 
|  | 2168 | return Pos.getContainer() == Cont; | 
|  | 2169 | }; | 
|  | 2170 | auto Invalidate = [&](const IteratorPosition &Pos) { | 
|  | 2171 | return Pos.invalidate(); | 
|  | 2172 | }; | 
|  | 2173 | return processIteratorPositions(State, MatchCont, Invalidate); | 
|  | 2174 | } | 
|  | 2175 |  | 
| Adam Balogh | 2e7cb34 | 2018-09-10 09:07:47 +0000 | [diff] [blame] | 2176 | ProgramStateRef | 
|  | 2177 | invalidateAllIteratorPositionsExcept(ProgramStateRef State, | 
|  | 2178 | const MemRegion *Cont, SymbolRef Offset, | 
|  | 2179 | BinaryOperator::Opcode Opc) { | 
|  | 2180 | auto MatchContAndCompare = [&](const IteratorPosition &Pos) { | 
|  | 2181 | return Pos.getContainer() == Cont && | 
|  | 2182 | !compare(State, Pos.getOffset(), Offset, Opc); | 
|  | 2183 | }; | 
|  | 2184 | auto Invalidate = [&](const IteratorPosition &Pos) { | 
|  | 2185 | return Pos.invalidate(); | 
|  | 2186 | }; | 
|  | 2187 | return processIteratorPositions(State, MatchContAndCompare, Invalidate); | 
|  | 2188 | } | 
|  | 2189 |  | 
| Adam Balogh | 9a48ba6 | 2018-09-10 09:06:31 +0000 | [diff] [blame] | 2190 | ProgramStateRef invalidateIteratorPositions(ProgramStateRef State, | 
|  | 2191 | SymbolRef Offset, | 
|  | 2192 | BinaryOperator::Opcode Opc) { | 
|  | 2193 | auto Compare = [&](const IteratorPosition &Pos) { | 
|  | 2194 | return compare(State, Pos.getOffset(), Offset, Opc); | 
|  | 2195 | }; | 
|  | 2196 | auto Invalidate = [&](const IteratorPosition &Pos) { | 
|  | 2197 | return Pos.invalidate(); | 
|  | 2198 | }; | 
|  | 2199 | return processIteratorPositions(State, Compare, Invalidate); | 
|  | 2200 | } | 
|  | 2201 |  | 
| Adam Balogh | 2e7cb34 | 2018-09-10 09:07:47 +0000 | [diff] [blame] | 2202 | ProgramStateRef invalidateIteratorPositions(ProgramStateRef State, | 
|  | 2203 | SymbolRef Offset1, | 
|  | 2204 | BinaryOperator::Opcode Opc1, | 
|  | 2205 | SymbolRef Offset2, | 
|  | 2206 | BinaryOperator::Opcode Opc2) { | 
|  | 2207 | auto Compare = [&](const IteratorPosition &Pos) { | 
|  | 2208 | return compare(State, Pos.getOffset(), Offset1, Opc1) && | 
|  | 2209 | compare(State, Pos.getOffset(), Offset2, Opc2); | 
|  | 2210 | }; | 
|  | 2211 | auto Invalidate = [&](const IteratorPosition &Pos) { | 
|  | 2212 | return Pos.invalidate(); | 
|  | 2213 | }; | 
|  | 2214 | return processIteratorPositions(State, Compare, Invalidate); | 
|  | 2215 | } | 
|  | 2216 |  | 
| Adam Balogh | 6b23b1a | 2018-09-10 09:04:27 +0000 | [diff] [blame] | 2217 | ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State, | 
|  | 2218 | const MemRegion *Cont, | 
|  | 2219 | const MemRegion *NewCont) { | 
|  | 2220 | auto MatchCont = [&](const IteratorPosition &Pos) { | 
|  | 2221 | return Pos.getContainer() == Cont; | 
|  | 2222 | }; | 
|  | 2223 | auto ReAssign = [&](const IteratorPosition &Pos) { | 
|  | 2224 | return Pos.reAssign(NewCont); | 
|  | 2225 | }; | 
|  | 2226 | return processIteratorPositions(State, MatchCont, ReAssign); | 
|  | 2227 | } | 
|  | 2228 |  | 
|  | 2229 | ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State, | 
|  | 2230 | const MemRegion *Cont, | 
|  | 2231 | const MemRegion *NewCont, | 
|  | 2232 | SymbolRef Offset, | 
|  | 2233 | BinaryOperator::Opcode Opc) { | 
|  | 2234 | auto MatchContAndCompare = [&](const IteratorPosition &Pos) { | 
|  | 2235 | return Pos.getContainer() == Cont && | 
|  | 2236 | !compare(State, Pos.getOffset(), Offset, Opc); | 
|  | 2237 | }; | 
|  | 2238 | auto ReAssign = [&](const IteratorPosition &Pos) { | 
|  | 2239 | return Pos.reAssign(NewCont); | 
|  | 2240 | }; | 
|  | 2241 | return processIteratorPositions(State, MatchContAndCompare, ReAssign); | 
|  | 2242 | } | 
|  | 2243 |  | 
|  | 2244 | // This function rebases symbolic expression `OldSym + Int` to `NewSym + Int`, | 
|  | 2245 | // `OldSym - Int` to `NewSym - Int` and  `OldSym` to `NewSym` in any iterator | 
|  | 2246 | // position offsets where `CondSym` is true. | 
|  | 2247 | ProgramStateRef rebaseSymbolInIteratorPositionsIf( | 
|  | 2248 | ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym, | 
|  | 2249 | SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc) { | 
|  | 2250 | auto LessThanEnd = [&](const IteratorPosition &Pos) { | 
|  | 2251 | return compare(State, Pos.getOffset(), CondSym, Opc); | 
|  | 2252 | }; | 
|  | 2253 | auto RebaseSymbol = [&](const IteratorPosition &Pos) { | 
|  | 2254 | return Pos.setTo(rebaseSymbol(State, SVB, Pos.getOffset(), OldSym, | 
|  | 2255 | NewSym)); | 
|  | 2256 | }; | 
|  | 2257 | return processIteratorPositions(State, LessThanEnd, RebaseSymbol); | 
|  | 2258 | } | 
|  | 2259 |  | 
|  | 2260 | // This function rebases symbolic expression `OldExpr + Int` to `NewExpr + Int`, | 
|  | 2261 | // `OldExpr - Int` to `NewExpr - Int` and  `OldExpr` to `NewExpr` in expression | 
|  | 2262 | // `OrigExpr`. | 
|  | 2263 | SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB, | 
|  | 2264 | SymbolRef OrigExpr, SymbolRef OldExpr, | 
|  | 2265 | SymbolRef NewSym) { | 
|  | 2266 | auto &SymMgr = SVB.getSymbolManager(); | 
|  | 2267 | auto Diff = SVB.evalBinOpNN(State, BO_Sub, nonloc::SymbolVal(OrigExpr), | 
|  | 2268 | nonloc::SymbolVal(OldExpr), | 
|  | 2269 | SymMgr.getType(OrigExpr)); | 
|  | 2270 |  | 
|  | 2271 | const auto DiffInt = Diff.getAs<nonloc::ConcreteInt>(); | 
|  | 2272 | if (!DiffInt) | 
|  | 2273 | return OrigExpr; | 
|  | 2274 |  | 
|  | 2275 | return SVB.evalBinOpNN(State, BO_Add, *DiffInt, nonloc::SymbolVal(NewSym), | 
|  | 2276 | SymMgr.getType(OrigExpr)).getAsSymbol(); | 
|  | 2277 | } | 
|  | 2278 |  | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 2279 | bool isZero(ProgramStateRef State, const NonLoc &Val) { | 
|  | 2280 | auto &BVF = State->getBasicVals(); | 
|  | 2281 | return compare(State, Val, | 
|  | 2282 | nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))), | 
|  | 2283 | BO_EQ); | 
|  | 2284 | } | 
|  | 2285 |  | 
| Adam Balogh | d5bd3f6 | 2018-12-04 10:27:27 +0000 | [diff] [blame] | 2286 | bool isPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos) { | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 2287 | const auto *Cont = Pos.getContainer(); | 
|  | 2288 | const auto *CData = getContainerData(State, Cont); | 
|  | 2289 | if (!CData) | 
|  | 2290 | return false; | 
|  | 2291 |  | 
| Adam Balogh | d5bd3f6 | 2018-12-04 10:27:27 +0000 | [diff] [blame] | 2292 | const auto End = CData->getEnd(); | 
|  | 2293 | if (End) { | 
|  | 2294 | if (isEqual(State, Pos.getOffset(), End)) { | 
|  | 2295 | return true; | 
|  | 2296 | } | 
|  | 2297 | } | 
|  | 2298 |  | 
|  | 2299 | return false; | 
|  | 2300 | } | 
|  | 2301 |  | 
|  | 2302 | bool isAheadOfRange(ProgramStateRef State, const IteratorPosition &Pos) { | 
|  | 2303 | const auto *Cont = Pos.getContainer(); | 
|  | 2304 | const auto *CData = getContainerData(State, Cont); | 
|  | 2305 | if (!CData) | 
|  | 2306 | return false; | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 2307 |  | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 2308 | const auto Beg = CData->getBegin(); | 
|  | 2309 | if (Beg) { | 
|  | 2310 | if (isLess(State, Pos.getOffset(), Beg)) { | 
|  | 2311 | return true; | 
|  | 2312 | } | 
|  | 2313 | } | 
|  | 2314 |  | 
| Adam Balogh | d5bd3f6 | 2018-12-04 10:27:27 +0000 | [diff] [blame] | 2315 | return false; | 
|  | 2316 | } | 
|  | 2317 |  | 
|  | 2318 | bool isBehindPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos) { | 
|  | 2319 | const auto *Cont = Pos.getContainer(); | 
|  | 2320 | const auto *CData = getContainerData(State, Cont); | 
|  | 2321 | if (!CData) | 
|  | 2322 | return false; | 
|  | 2323 |  | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 2324 | const auto End = CData->getEnd(); | 
|  | 2325 | if (End) { | 
| Adam Balogh | d5bd3f6 | 2018-12-04 10:27:27 +0000 | [diff] [blame] | 2326 | if (isGreater(State, Pos.getOffset(), End)) { | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 2327 | return true; | 
|  | 2328 | } | 
|  | 2329 | } | 
|  | 2330 |  | 
|  | 2331 | return false; | 
|  | 2332 | } | 
|  | 2333 |  | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 2334 | bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) { | 
|  | 2335 | return compare(State, Sym1, Sym2, BO_LT); | 
|  | 2336 | } | 
|  | 2337 |  | 
| Adam Balogh | d5bd3f6 | 2018-12-04 10:27:27 +0000 | [diff] [blame] | 2338 | bool isGreater(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) { | 
|  | 2339 | return compare(State, Sym1, Sym2, BO_GT); | 
|  | 2340 | } | 
|  | 2341 |  | 
|  | 2342 | bool isEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) { | 
|  | 2343 | return compare(State, Sym1, Sym2, BO_EQ); | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 2344 | } | 
|  | 2345 |  | 
|  | 2346 | bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2, | 
|  | 2347 | BinaryOperator::Opcode Opc) { | 
| Adam Balogh | b03ed5e | 2018-06-28 10:58:53 +0000 | [diff] [blame] | 2348 | return compare(State, nonloc::SymbolVal(Sym1), nonloc::SymbolVal(Sym2), Opc); | 
|  | 2349 | } | 
|  | 2350 |  | 
|  | 2351 | bool compare(ProgramStateRef State, NonLoc NL1, NonLoc NL2, | 
|  | 2352 | BinaryOperator::Opcode Opc) { | 
|  | 2353 | auto &SVB = State->getStateManager().getSValBuilder(); | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 2354 |  | 
|  | 2355 | const auto comparison = | 
| Adam Balogh | 0a7592b | 2018-07-16 09:27:27 +0000 | [diff] [blame] | 2356 | SVB.evalBinOp(State, Opc, NL1, NL2, SVB.getConditionType()); | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 2357 |  | 
| Adam Balogh | 0a7592b | 2018-07-16 09:27:27 +0000 | [diff] [blame] | 2358 | assert(comparison.getAs<DefinedSVal>() && | 
|  | 2359 | "Symbol comparison must be a `DefinedSVal`"); | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 2360 |  | 
| Adam Balogh | 0a7592b | 2018-07-16 09:27:27 +0000 | [diff] [blame] | 2361 | return !State->assume(comparison.castAs<DefinedSVal>(), false); | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 2362 | } | 
|  | 2363 |  | 
|  | 2364 | } // namespace | 
|  | 2365 |  | 
| Kristof Umann | 8fd74eb | 2019-01-26 20:06:54 +0000 | [diff] [blame] | 2366 | void ento::registerIteratorModeling(CheckerManager &mgr) { | 
|  | 2367 | mgr.registerChecker<IteratorChecker>(); | 
|  | 2368 | } | 
|  | 2369 |  | 
|  | 2370 | bool ento::shouldRegisterIteratorModeling(const LangOptions &LO) { | 
|  | 2371 | return true; | 
|  | 2372 | } | 
|  | 2373 |  | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 2374 | #define REGISTER_CHECKER(name)                                                 \ | 
|  | 2375 | void ento::register##name(CheckerManager &Mgr) {                             \ | 
| Kristof Umann | 204bf2b | 2019-01-26 21:41:50 +0000 | [diff] [blame] | 2376 | auto *checker = Mgr.getChecker<IteratorChecker>();                         \ | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 2377 | checker->ChecksEnabled[IteratorChecker::CK_##name] = true;                 \ | 
|  | 2378 | checker->CheckNames[IteratorChecker::CK_##name] =                          \ | 
|  | 2379 | Mgr.getCurrentCheckName();                                             \ | 
| Kristof Umann | 058a7a4 | 2019-01-26 14:23:08 +0000 | [diff] [blame] | 2380 | }                                                                            \ | 
|  | 2381 | \ | 
|  | 2382 | bool ento::shouldRegister##name(const LangOptions &LO) {                     \ | 
|  | 2383 | return true;                                                               \ | 
| Artem Dergachev | 8fa639e | 2017-05-29 15:03:20 +0000 | [diff] [blame] | 2384 | } | 
|  | 2385 |  | 
|  | 2386 | REGISTER_CHECKER(IteratorRangeChecker) | 
| Adam Balogh | 21583b7 | 2018-09-10 09:03:22 +0000 | [diff] [blame] | 2387 | REGISTER_CHECKER(MismatchedIteratorChecker) | 
| Adam Balogh | 2cfbe93 | 2018-08-28 08:41:15 +0000 | [diff] [blame] | 2388 | REGISTER_CHECKER(InvalidatedIteratorChecker) |