blob: 80ff661fc71ec119ab99135c19b0793b0c473950 [file] [log] [blame]
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001//===-- IteratorChecker.cpp ---------------------------------------*- C++ -*--//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// Defines a checker for using iterators outside their range (past end). Usage
11// means here dereferencing, incrementing etc.
12//
13//===----------------------------------------------------------------------===//
14//
15// In the code, iterator can be represented as a:
16// * type-I: typedef-ed pointer. Operations over such iterator, such as
17// comparisons or increments, are modeled straightforwardly by the
18// analyzer.
19// * type-II: structure with its method bodies available. Operations over such
20// iterator are inlined by the analyzer, and results of modeling
21// these operations are exposing implementation details of the
22// iterators, which is not necessarily helping.
23// * type-III: completely opaque structure. Operations over such iterator are
24// modeled conservatively, producing conjured symbols everywhere.
25//
26// To handle all these types in a common way we introduce a structure called
27// IteratorPosition which is an abstraction of the position the iterator
28// represents using symbolic expressions. The checker handles all the
29// operations on this structure.
30//
31// Additionally, depending on the circumstances, operators of types II and III
32// can be represented as:
33// * type-IIa, type-IIIa: conjured structure symbols - when returned by value
34// from conservatively evaluated methods such as
35// `.begin()`.
36// * type-IIb, type-IIIb: memory regions of iterator-typed objects, such as
37// variables or temporaries, when the iterator object is
38// currently treated as an lvalue.
39// * type-IIc, type-IIIc: compound values of iterator-typed objects, when the
40// iterator object is treated as an rvalue taken of a
41// particular lvalue, eg. a copy of "type-a" iterator
42// object, or an iterator that existed before the
43// analysis has started.
44//
45// To handle any of these three different representations stored in an SVal we
46// use setter and getters functions which separate the three cases. To store
47// them we use a pointer union of symbol and memory region.
48//
Adam Baloghb03ed5e2018-06-28 10:58:53 +000049// The checker works the following way: We record the begin and the
50// past-end iterator for all containers whenever their `.begin()` and `.end()`
51// are called. Since the Constraint Manager cannot handle such SVals we need
52// to take over its role. We post-check equality and non-equality comparisons
53// and record that the two sides are equal if we are in the 'equal' branch
54// (true-branch for `==` and false-branch for `!=`).
Artem Dergachev8fa639e2017-05-29 15:03:20 +000055//
56// In case of type-I or type-II iterators we get a concrete integer as a result
57// of the comparison (1 or 0) but in case of type-III we only get a Symbol. In
58// this latter case we record the symbol and reload it in evalAssume() and do
59// the propagation there. We also handle (maybe double) negated comparisons
Adam Baloghb03ed5e2018-06-28 10:58:53 +000060// which are represented in the form of (x == 0 or x != 0) where x is the
Artem Dergachev8fa639e2017-05-29 15:03:20 +000061// comparison itself.
Adam Baloghb03ed5e2018-06-28 10:58:53 +000062//
63// Since `SimpleConstraintManager` cannot handle complex symbolic expressions
64// we only use expressions of the format S, S+n or S-n for iterator positions
65// where S is a conjured symbol and n is an unsigned concrete integer. When
66// making an assumption e.g. `S1 + n == S2 + m` we store `S1 - S2 == m - n` as
67// a constraint which we later retrieve when doing an actual comparison.
Artem Dergachev8fa639e2017-05-29 15:03:20 +000068
69#include "ClangSACheckers.h"
Adam Balogh2cfbe932018-08-28 08:41:15 +000070#include "clang/AST/DeclTemplate.h"
Artem Dergachev8fa639e2017-05-29 15:03:20 +000071#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
72#include "clang/StaticAnalyzer/Core/Checker.h"
73#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
74#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
75
Adam Balogh2cfbe932018-08-28 08:41:15 +000076#include <utility>
77
Artem Dergachev8fa639e2017-05-29 15:03:20 +000078using namespace clang;
79using namespace ento;
80
81namespace {
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.
85struct IteratorPosition {
86private:
87
88 // Container the iterator belongs to
89 const MemRegion *Cont;
90
Adam Balogh2cfbe932018-08-28 08:41:15 +000091 // Whether iterator is valid
92 const bool Valid;
93
Artem Dergachev8fa639e2017-05-29 15:03:20 +000094 // Abstract offset
Adam Baloghb03ed5e2018-06-28 10:58:53 +000095 const SymbolRef Offset;
Artem Dergachev8fa639e2017-05-29 15:03:20 +000096
Adam Balogh2cfbe932018-08-28 08:41:15 +000097 IteratorPosition(const MemRegion *C, bool V, SymbolRef Of)
98 : Cont(C), Valid(V), Offset(Of) {}
Artem Dergachev8fa639e2017-05-29 15:03:20 +000099
100public:
101 const MemRegion *getContainer() const { return Cont; }
Adam Balogh2cfbe932018-08-28 08:41:15 +0000102 bool isValid() const { return Valid; }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000103 SymbolRef getOffset() const { return Offset; }
104
Adam Balogh2cfbe932018-08-28 08:41:15 +0000105 IteratorPosition invalidate() const {
106 return IteratorPosition(Cont, false, Offset);
107 }
108
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000109 static IteratorPosition getPosition(const MemRegion *C, SymbolRef Of) {
Adam Balogh2cfbe932018-08-28 08:41:15 +0000110 return IteratorPosition(C, true, Of);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000111 }
112
113 IteratorPosition setTo(SymbolRef NewOf) const {
Adam Balogh2cfbe932018-08-28 08:41:15 +0000114 return IteratorPosition(Cont, Valid, NewOf);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000115 }
116
117 bool operator==(const IteratorPosition &X) const {
Adam Balogh2cfbe932018-08-28 08:41:15 +0000118 return Cont == X.Cont && Valid == X.Valid && Offset == X.Offset;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000119 }
120
121 bool operator!=(const IteratorPosition &X) const {
Adam Balogh2cfbe932018-08-28 08:41:15 +0000122 return Cont != X.Cont || Valid != X.Valid || Offset != X.Offset;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000123 }
124
125 void Profile(llvm::FoldingSetNodeID &ID) const {
126 ID.AddPointer(Cont);
Adam Balogh2cfbe932018-08-28 08:41:15 +0000127 ID.AddInteger(Valid);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000128 ID.Add(Offset);
129 }
130};
131
132typedef llvm::PointerUnion<const MemRegion *, SymbolRef> RegionOrSymbol;
133
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000134// Structure to record the symbolic begin and end position of a container
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000135struct ContainerData {
136private:
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000137 const SymbolRef Begin, End;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000138
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000139 ContainerData(SymbolRef B, SymbolRef E) : Begin(B), End(E) {}
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000140
141public:
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000142 static ContainerData fromBegin(SymbolRef B) {
143 return ContainerData(B, nullptr);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000144 }
145
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000146 static ContainerData fromEnd(SymbolRef E) {
147 return ContainerData(nullptr, E);
148 }
149
150 SymbolRef getBegin() const { return Begin; }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000151 SymbolRef getEnd() const { return End; }
152
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000153 ContainerData newBegin(SymbolRef B) const { return ContainerData(B, End); }
154
155 ContainerData newEnd(SymbolRef E) const { return ContainerData(Begin, E); }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000156
157 bool operator==(const ContainerData &X) const {
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000158 return Begin == X.Begin && End == X.End;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000159 }
160
161 bool operator!=(const ContainerData &X) const {
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000162 return Begin != X.Begin || End != X.End;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000163 }
164
165 void Profile(llvm::FoldingSetNodeID &ID) const {
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000166 ID.Add(Begin);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000167 ID.Add(End);
168 }
169};
170
171// Structure fo recording iterator comparisons. We needed to retrieve the
172// original comparison expression in assumptions.
173struct IteratorComparison {
174private:
175 RegionOrSymbol Left, Right;
176 bool Equality;
177
178public:
179 IteratorComparison(RegionOrSymbol L, RegionOrSymbol R, bool Eq)
180 : Left(L), Right(R), Equality(Eq) {}
181
182 RegionOrSymbol getLeft() const { return Left; }
183 RegionOrSymbol getRight() const { return Right; }
184 bool isEquality() const { return Equality; }
185 bool operator==(const IteratorComparison &X) const {
186 return Left == X.Left && Right == X.Right && Equality == X.Equality;
187 }
188 bool operator!=(const IteratorComparison &X) const {
189 return Left != X.Left || Right != X.Right || Equality != X.Equality;
190 }
191 void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(Equality); }
192};
193
194class IteratorChecker
195 : public Checker<check::PreCall, check::PostCall,
196 check::PostStmt<MaterializeTemporaryExpr>,
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000197 check::LiveSymbols, check::DeadSymbols,
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000198 eval::Assume> {
199
200 std::unique_ptr<BugType> OutOfRangeBugType;
Adam Balogh21583b72018-09-10 09:03:22 +0000201 std::unique_ptr<BugType> MismatchedBugType;
Adam Balogh2cfbe932018-08-28 08:41:15 +0000202 std::unique_ptr<BugType> InvalidatedBugType;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000203
204 void handleComparison(CheckerContext &C, const SVal &RetVal, const SVal &LVal,
205 const SVal &RVal, OverloadedOperatorKind Op) const;
Adam Balogh2cfbe932018-08-28 08:41:15 +0000206 void verifyAccess(CheckerContext &C, const SVal &Val) const;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000207 void verifyDereference(CheckerContext &C, const SVal &Val) const;
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000208 void handleIncrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
209 bool Postfix) const;
210 void handleDecrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
211 bool Postfix) const;
212 void handleRandomIncrOrDecr(CheckerContext &C, OverloadedOperatorKind Op,
213 const SVal &RetVal, const SVal &LHS,
214 const SVal &RHS) const;
215 void handleBegin(CheckerContext &C, const Expr *CE, const SVal &RetVal,
216 const SVal &Cont) const;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000217 void handleEnd(CheckerContext &C, const Expr *CE, const SVal &RetVal,
218 const SVal &Cont) const;
219 void assignToContainer(CheckerContext &C, const Expr *CE, const SVal &RetVal,
220 const MemRegion *Cont) const;
Adam Balogh2cfbe932018-08-28 08:41:15 +0000221 void handleAssign(CheckerContext &C, const SVal &Cont) const;
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000222 void verifyRandomIncrOrDecr(CheckerContext &C, OverloadedOperatorKind Op,
223 const SVal &RetVal, const SVal &LHS,
224 const SVal &RHS) const;
Adam Balogh21583b72018-09-10 09:03:22 +0000225 void verifyMatch(CheckerContext &C, const SVal &Iter1,
226 const SVal &Iter2) const;
227
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000228 void reportOutOfRangeBug(const StringRef &Message, const SVal &Val,
229 CheckerContext &C, ExplodedNode *ErrNode) const;
Adam Balogh21583b72018-09-10 09:03:22 +0000230 void reportMismatchedBug(const StringRef &Message, const SVal &Val1,
231 const SVal &Val2, CheckerContext &C,
232 ExplodedNode *ErrNode) const;
Adam Balogh2cfbe932018-08-28 08:41:15 +0000233 void reportInvalidatedBug(const StringRef &Message, const SVal &Val,
234 CheckerContext &C, ExplodedNode *ErrNode) const;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000235
236public:
237 IteratorChecker();
238
239 enum CheckKind {
240 CK_IteratorRangeChecker,
Adam Balogh21583b72018-09-10 09:03:22 +0000241 CK_MismatchedIteratorChecker,
Adam Balogh2cfbe932018-08-28 08:41:15 +0000242 CK_InvalidatedIteratorChecker,
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000243 CK_NumCheckKinds
244 };
245
246 DefaultBool ChecksEnabled[CK_NumCheckKinds];
247 CheckName CheckNames[CK_NumCheckKinds];
248
249 void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
250 void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
251 void checkPostStmt(const MaterializeTemporaryExpr *MTE,
252 CheckerContext &C) const;
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000253 void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000254 void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
255 ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond,
256 bool Assumption) const;
257};
258} // namespace
259
260REGISTER_MAP_WITH_PROGRAMSTATE(IteratorSymbolMap, SymbolRef, IteratorPosition)
261REGISTER_MAP_WITH_PROGRAMSTATE(IteratorRegionMap, const MemRegion *,
262 IteratorPosition)
263
264REGISTER_MAP_WITH_PROGRAMSTATE(ContainerMap, const MemRegion *, ContainerData)
265
266REGISTER_MAP_WITH_PROGRAMSTATE(IteratorComparisonMap, const SymExpr *,
267 IteratorComparison)
268
269namespace {
270
271bool isIteratorType(const QualType &Type);
272bool isIterator(const CXXRecordDecl *CRD);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000273bool isBeginCall(const FunctionDecl *Func);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000274bool isEndCall(const FunctionDecl *Func);
Adam Balogh2cfbe932018-08-28 08:41:15 +0000275bool isAssignmentOperator(OverloadedOperatorKind OK);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000276bool isSimpleComparisonOperator(OverloadedOperatorKind OK);
Adam Balogh2cfbe932018-08-28 08:41:15 +0000277bool isAccessOperator(OverloadedOperatorKind OK);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000278bool isDereferenceOperator(OverloadedOperatorKind OK);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000279bool isIncrementOperator(OverloadedOperatorKind OK);
280bool isDecrementOperator(OverloadedOperatorKind OK);
281bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000282BinaryOperator::Opcode getOpcode(const SymExpr *SE);
283const RegionOrSymbol getRegionOrSymbol(const SVal &Val);
284const ProgramStateRef processComparison(ProgramStateRef State,
285 RegionOrSymbol LVal,
286 RegionOrSymbol RVal, bool Equal);
287const ProgramStateRef saveComparison(ProgramStateRef State,
288 const SymExpr *Condition, const SVal &LVal,
289 const SVal &RVal, bool Eq);
290const IteratorComparison *loadComparison(ProgramStateRef State,
291 const SymExpr *Condition);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000292SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000293SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000294ProgramStateRef createContainerBegin(ProgramStateRef State,
295 const MemRegion *Cont,
296 const SymbolRef Sym);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000297ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
298 const SymbolRef Sym);
299const IteratorPosition *getIteratorPosition(ProgramStateRef State,
300 const SVal &Val);
301const IteratorPosition *getIteratorPosition(ProgramStateRef State,
302 RegionOrSymbol RegOrSym);
303ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
304 const IteratorPosition &Pos);
305ProgramStateRef setIteratorPosition(ProgramStateRef State,
306 RegionOrSymbol RegOrSym,
307 const IteratorPosition &Pos);
308ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val);
309ProgramStateRef adjustIteratorPosition(ProgramStateRef State,
310 RegionOrSymbol RegOrSym,
311 const IteratorPosition &Pos, bool Equal);
312ProgramStateRef relateIteratorPositions(ProgramStateRef State,
313 const IteratorPosition &Pos1,
314 const IteratorPosition &Pos2,
315 bool Equal);
Adam Balogh2cfbe932018-08-28 08:41:15 +0000316ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State,
317 const MemRegion *Cont);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000318const ContainerData *getContainerData(ProgramStateRef State,
319 const MemRegion *Cont);
320ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
321 const ContainerData &CData);
Adam Balogha6921202018-07-30 08:52:21 +0000322bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont);
Adam Balogh21583b72018-09-10 09:03:22 +0000323bool isBoundThroughLazyCompoundVal(const Environment &Env,
324 const MemRegion *Reg);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000325bool isOutOfRange(ProgramStateRef State, const IteratorPosition &Pos);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000326bool isZero(ProgramStateRef State, const NonLoc &Val);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000327} // namespace
328
329IteratorChecker::IteratorChecker() {
330 OutOfRangeBugType.reset(
331 new BugType(this, "Iterator out of range", "Misuse of STL APIs"));
332 OutOfRangeBugType->setSuppressOnSink(true);
Adam Balogh21583b72018-09-10 09:03:22 +0000333 MismatchedBugType.reset(
334 new BugType(this, "Iterator(s) mismatched", "Misuse of STL APIs"));
335 MismatchedBugType->setSuppressOnSink(true);
Adam Balogh2cfbe932018-08-28 08:41:15 +0000336 InvalidatedBugType.reset(
337 new BugType(this, "Iterator invalidated", "Misuse of STL APIs"));
338 InvalidatedBugType->setSuppressOnSink(true);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000339}
340
341void IteratorChecker::checkPreCall(const CallEvent &Call,
342 CheckerContext &C) const {
Adam Balogh2cfbe932018-08-28 08:41:15 +0000343 // Check for out of range access or access of invalidated position and
344 // iterator mismatches
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000345 const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
346 if (!Func)
347 return;
348
349 if (Func->isOverloadedOperator()) {
Adam Balogh2cfbe932018-08-28 08:41:15 +0000350 if (ChecksEnabled[CK_InvalidatedIteratorChecker] &&
351 isAccessOperator(Func->getOverloadedOperator())) {
352 // Check for any kind of access of invalidated iterator positions
353 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
354 verifyAccess(C, InstCall->getCXXThisVal());
355 } else {
356 verifyAccess(C, Call.getArgSVal(0));
357 }
358 }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000359 if (ChecksEnabled[CK_IteratorRangeChecker] &&
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000360 isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) {
361 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
362 // Check for out-of-range incrementions and decrementions
363 if (Call.getNumArgs() >= 1) {
364 verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(),
365 Call.getReturnValue(),
366 InstCall->getCXXThisVal(), Call.getArgSVal(0));
367 }
368 } else {
369 if (Call.getNumArgs() >= 2) {
370 verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(),
371 Call.getReturnValue(), Call.getArgSVal(0),
372 Call.getArgSVal(1));
373 }
374 }
375 } else if (ChecksEnabled[CK_IteratorRangeChecker] &&
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000376 isDereferenceOperator(Func->getOverloadedOperator())) {
377 // Check for dereference of out-of-range iterators
378 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
379 verifyDereference(C, InstCall->getCXXThisVal());
380 } else {
381 verifyDereference(C, Call.getArgSVal(0));
382 }
383 }
Adam Balogh21583b72018-09-10 09:03:22 +0000384 } else if (!isa<CXXConstructorCall>(&Call)) {
385 // The main purpose of iterators is to abstract away from different
386 // containers and provide a (maybe limited) uniform access to them.
387 // This implies that any correctly written template function that
388 // works on multiple containers using iterators takes different
389 // template parameters for different containers. So we can safely
390 // assume that passing iterators of different containers as arguments
391 // whose type replaces the same template parameter is a bug.
392 //
393 // Example:
394 // template<typename I1, typename I2>
395 // void f(I1 first1, I1 last1, I2 first2, I2 last2);
396 //
397 // In this case the first two arguments to f() must be iterators must belong
398 // to the same container and the last to also to the same container but
399 // not neccessarily to the same as the first two.
400
401 if (!ChecksEnabled[CK_MismatchedIteratorChecker])
402 return;
403
404 const auto *Templ = Func->getPrimaryTemplate();
405 if (!Templ)
406 return;
407
408 const auto *TParams = Templ->getTemplateParameters();
409 const auto *TArgs = Func->getTemplateSpecializationArgs();
410
411 // Iterate over all the template parameters
412 for (size_t I = 0; I < TParams->size(); ++I) {
413 const auto *TPDecl = dyn_cast<TemplateTypeParmDecl>(TParams->getParam(I));
414 if (!TPDecl)
415 continue;
416
417 if (TPDecl->isParameterPack())
418 continue;
419
420 const auto TAType = TArgs->get(I).getAsType();
421 if (!isIteratorType(TAType))
422 continue;
423
424 SVal LHS = UndefinedVal();
425
426 // For every template parameter which is an iterator type in the
427 // instantiation look for all functions' parameters' type by it and
428 // check whether they belong to the same container
429 for (auto J = 0U; J < Func->getNumParams(); ++J) {
430 const auto *Param = Func->getParamDecl(J);
431 const auto *ParamType =
432 Param->getType()->getAs<SubstTemplateTypeParmType>();
433 if (!ParamType ||
434 ParamType->getReplacedParameter()->getDecl() != TPDecl)
435 continue;
436 if (LHS.isUndef()) {
437 LHS = Call.getArgSVal(J);
438 } else {
439 verifyMatch(C, LHS, Call.getArgSVal(J));
440 }
441 }
442 }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000443 }
444}
445
446void IteratorChecker::checkPostCall(const CallEvent &Call,
447 CheckerContext &C) const {
448 // Record new iterator positions and iterator position changes
449 const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
450 if (!Func)
451 return;
452
453 if (Func->isOverloadedOperator()) {
454 const auto Op = Func->getOverloadedOperator();
Adam Balogh2cfbe932018-08-28 08:41:15 +0000455 if (isAssignmentOperator(Op)) {
456 const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call);
457 handleAssign(C, InstCall->getCXXThisVal());
458 } else if (isSimpleComparisonOperator(Op)) {
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000459 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
460 handleComparison(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
461 Call.getArgSVal(0), Op);
462 } else {
463 handleComparison(C, Call.getReturnValue(), Call.getArgSVal(0),
464 Call.getArgSVal(1), Op);
465 }
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000466 } else if (isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) {
467 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
468 if (Call.getNumArgs() >= 1) {
469 handleRandomIncrOrDecr(C, Func->getOverloadedOperator(),
470 Call.getReturnValue(),
471 InstCall->getCXXThisVal(), Call.getArgSVal(0));
472 }
473 } else {
474 if (Call.getNumArgs() >= 2) {
475 handleRandomIncrOrDecr(C, Func->getOverloadedOperator(),
476 Call.getReturnValue(), Call.getArgSVal(0),
477 Call.getArgSVal(1));
478 }
479 }
480 } else if (isIncrementOperator(Func->getOverloadedOperator())) {
481 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
482 handleIncrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
483 Call.getNumArgs());
484 } else {
485 handleIncrement(C, Call.getReturnValue(), Call.getArgSVal(0),
486 Call.getNumArgs());
487 }
488 } else if (isDecrementOperator(Func->getOverloadedOperator())) {
489 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
490 handleDecrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
491 Call.getNumArgs());
492 } else {
493 handleDecrement(C, Call.getReturnValue(), Call.getArgSVal(0),
494 Call.getNumArgs());
495 }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000496 }
497 } else {
498 const auto *OrigExpr = Call.getOriginExpr();
499 if (!OrigExpr)
500 return;
501
502 if (!isIteratorType(Call.getResultType()))
503 return;
504
505 auto State = C.getState();
506 // Already bound to container?
507 if (getIteratorPosition(State, Call.getReturnValue()))
508 return;
509
510 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000511 if (isBeginCall(Func)) {
512 handleBegin(C, OrigExpr, Call.getReturnValue(),
513 InstCall->getCXXThisVal());
514 return;
515 }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000516 if (isEndCall(Func)) {
517 handleEnd(C, OrigExpr, Call.getReturnValue(),
518 InstCall->getCXXThisVal());
519 return;
520 }
521 }
522
523 // Copy-like and move constructors
524 if (isa<CXXConstructorCall>(&Call) && Call.getNumArgs() == 1) {
525 if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(0))) {
526 State = setIteratorPosition(State, Call.getReturnValue(), *Pos);
527 if (cast<CXXConstructorDecl>(Func)->isMoveConstructor()) {
528 State = removeIteratorPosition(State, Call.getArgSVal(0));
529 }
530 C.addTransition(State);
531 return;
532 }
533 }
534
535 // Assumption: if return value is an iterator which is not yet bound to a
536 // container, then look for the first iterator argument, and
537 // bind the return value to the same container. This approach
538 // works for STL algorithms.
539 // FIXME: Add a more conservative mode
540 for (unsigned i = 0; i < Call.getNumArgs(); ++i) {
541 if (isIteratorType(Call.getArgExpr(i)->getType())) {
542 if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(i))) {
543 assignToContainer(C, OrigExpr, Call.getReturnValue(),
544 Pos->getContainer());
545 return;
546 }
547 }
548 }
549 }
550}
551
552void IteratorChecker::checkPostStmt(const MaterializeTemporaryExpr *MTE,
553 CheckerContext &C) const {
554 /* Transfer iterator state to temporary objects */
555 auto State = C.getState();
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000556 const auto *Pos =
George Karpenkovd703ec92018-01-17 20:27:29 +0000557 getIteratorPosition(State, C.getSVal(MTE->GetTemporaryExpr()));
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000558 if (!Pos)
559 return;
George Karpenkovd703ec92018-01-17 20:27:29 +0000560 State = setIteratorPosition(State, C.getSVal(MTE), *Pos);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000561 C.addTransition(State);
562}
563
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000564void IteratorChecker::checkLiveSymbols(ProgramStateRef State,
565 SymbolReaper &SR) const {
566 // Keep symbolic expressions of iterator positions, container begins and ends
567 // alive
568 auto RegionMap = State->get<IteratorRegionMap>();
569 for (const auto Reg : RegionMap) {
Adam Balogh0a7592b2018-07-16 09:27:27 +0000570 const auto Offset = Reg.second.getOffset();
571 for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
572 if (isa<SymbolData>(*i))
573 SR.markLive(*i);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000574 }
575
576 auto SymbolMap = State->get<IteratorSymbolMap>();
577 for (const auto Sym : SymbolMap) {
Adam Balogh0a7592b2018-07-16 09:27:27 +0000578 const auto Offset = Sym.second.getOffset();
579 for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
580 if (isa<SymbolData>(*i))
581 SR.markLive(*i);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000582 }
583
584 auto ContMap = State->get<ContainerMap>();
585 for (const auto Cont : ContMap) {
586 const auto CData = Cont.second;
587 if (CData.getBegin()) {
588 SR.markLive(CData.getBegin());
589 }
590 if (CData.getEnd()) {
591 SR.markLive(CData.getEnd());
592 }
593 }
594}
595
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000596void IteratorChecker::checkDeadSymbols(SymbolReaper &SR,
597 CheckerContext &C) const {
598 // Cleanup
599 auto State = C.getState();
600
601 auto RegionMap = State->get<IteratorRegionMap>();
602 for (const auto Reg : RegionMap) {
603 if (!SR.isLiveRegion(Reg.first)) {
Adam Balogh21583b72018-09-10 09:03:22 +0000604 // The region behind the `LazyCompoundVal` is often cleaned up before
605 // the `LazyCompoundVal` itself. If there are iterator positions keyed
606 // by these regions their cleanup must be deferred.
607 if (!isBoundThroughLazyCompoundVal(State->getEnvironment(), Reg.first)) {
608 State = State->remove<IteratorRegionMap>(Reg.first);
609 }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000610 }
611 }
612
613 auto SymbolMap = State->get<IteratorSymbolMap>();
614 for (const auto Sym : SymbolMap) {
615 if (!SR.isLive(Sym.first)) {
616 State = State->remove<IteratorSymbolMap>(Sym.first);
617 }
618 }
619
620 auto ContMap = State->get<ContainerMap>();
621 for (const auto Cont : ContMap) {
622 if (!SR.isLiveRegion(Cont.first)) {
Adam Balogha6921202018-07-30 08:52:21 +0000623 // We must keep the container data while it has live iterators to be able
624 // to compare them to the begin and the end of the container.
625 if (!hasLiveIterators(State, Cont.first)) {
626 State = State->remove<ContainerMap>(Cont.first);
627 }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000628 }
629 }
630
631 auto ComparisonMap = State->get<IteratorComparisonMap>();
632 for (const auto Comp : ComparisonMap) {
633 if (!SR.isLive(Comp.first)) {
634 State = State->remove<IteratorComparisonMap>(Comp.first);
635 }
636 }
Reka Kovacse48ea892018-07-30 16:14:59 +0000637
638 C.addTransition(State);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000639}
640
641ProgramStateRef IteratorChecker::evalAssume(ProgramStateRef State, SVal Cond,
642 bool Assumption) const {
643 // Load recorded comparison and transfer iterator state between sides
644 // according to comparison operator and assumption
645 const auto *SE = Cond.getAsSymExpr();
646 if (!SE)
647 return State;
648
649 auto Opc = getOpcode(SE);
650 if (Opc != BO_EQ && Opc != BO_NE)
651 return State;
652
653 bool Negated = false;
654 const auto *Comp = loadComparison(State, SE);
655 if (!Comp) {
656 // Try negated comparison, which is a SymExpr to 0 integer comparison
657 const auto *SIE = dyn_cast<SymIntExpr>(SE);
658 if (!SIE)
659 return State;
660
661 if (SIE->getRHS() != 0)
662 return State;
663
664 SE = SIE->getLHS();
665 Negated = SIE->getOpcode() == BO_EQ; // Equal to zero means negation
666 Opc = getOpcode(SE);
667 if (Opc != BO_EQ && Opc != BO_NE)
668 return State;
669
670 Comp = loadComparison(State, SE);
671 if (!Comp)
672 return State;
673 }
674
675 return processComparison(State, Comp->getLeft(), Comp->getRight(),
676 (Comp->isEquality() == Assumption) != Negated);
677}
678
679void IteratorChecker::handleComparison(CheckerContext &C, const SVal &RetVal,
680 const SVal &LVal, const SVal &RVal,
681 OverloadedOperatorKind Op) const {
682 // Record the operands and the operator of the comparison for the next
683 // evalAssume, if the result is a symbolic expression. If it is a concrete
684 // value (only one branch is possible), then transfer the state between
685 // the operands according to the operator and the result
686 auto State = C.getState();
687 if (const auto *Condition = RetVal.getAsSymbolicExpression()) {
688 const auto *LPos = getIteratorPosition(State, LVal);
689 const auto *RPos = getIteratorPosition(State, RVal);
690 if (!LPos && !RPos)
691 return;
692 State = saveComparison(State, Condition, LVal, RVal, Op == OO_EqualEqual);
693 C.addTransition(State);
694 } else if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) {
695 if ((State = processComparison(
696 State, getRegionOrSymbol(LVal), getRegionOrSymbol(RVal),
697 (Op == OO_EqualEqual) == (TruthVal->getValue() != 0)))) {
698 C.addTransition(State);
699 } else {
700 C.generateSink(State, C.getPredecessor());
701 }
702 }
703}
704
705void IteratorChecker::verifyDereference(CheckerContext &C,
706 const SVal &Val) const {
707 auto State = C.getState();
708 const auto *Pos = getIteratorPosition(State, Val);
709 if (Pos && isOutOfRange(State, *Pos)) {
Adam Balogh2cfbe932018-08-28 08:41:15 +0000710 auto *N = C.generateNonFatalErrorNode(State);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000711 if (!N)
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000712 return;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000713 reportOutOfRangeBug("Iterator accessed outside of its range.", Val, C, N);
714 }
715}
716
Adam Balogh2cfbe932018-08-28 08:41:15 +0000717void IteratorChecker::verifyAccess(CheckerContext &C, const SVal &Val) const {
718 auto State = C.getState();
719 const auto *Pos = getIteratorPosition(State, Val);
720 if (Pos && !Pos->isValid()) {
721 auto *N = C.generateNonFatalErrorNode(State);
722 if (!N) {
723 return;
724 }
725 reportInvalidatedBug("Invalidated iterator accessed.", Val, C, N);
726 }
727}
728
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000729void IteratorChecker::handleIncrement(CheckerContext &C, const SVal &RetVal,
730 const SVal &Iter, bool Postfix) const {
731 // Increment the symbolic expressions which represents the position of the
732 // iterator
733 auto State = C.getState();
734 const auto *Pos = getIteratorPosition(State, Iter);
735 if (Pos) {
736 auto &SymMgr = C.getSymbolManager();
737 auto &BVF = SymMgr.getBasicVals();
738 auto &SVB = C.getSValBuilder();
739 const auto OldOffset = Pos->getOffset();
740 auto NewOffset =
741 SVB.evalBinOp(State, BO_Add,
742 nonloc::SymbolVal(OldOffset),
743 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
744 SymMgr.getType(OldOffset)).getAsSymbol();
745 auto NewPos = Pos->setTo(NewOffset);
746 State = setIteratorPosition(State, Iter, NewPos);
747 State = setIteratorPosition(State, RetVal, Postfix ? *Pos : NewPos);
748 C.addTransition(State);
749 }
750}
751
752void IteratorChecker::handleDecrement(CheckerContext &C, const SVal &RetVal,
753 const SVal &Iter, bool Postfix) const {
754 // Decrement the symbolic expressions which represents the position of the
755 // iterator
756 auto State = C.getState();
757 const auto *Pos = getIteratorPosition(State, Iter);
758 if (Pos) {
759 auto &SymMgr = C.getSymbolManager();
760 auto &BVF = SymMgr.getBasicVals();
761 auto &SVB = C.getSValBuilder();
762 const auto OldOffset = Pos->getOffset();
763 auto NewOffset =
764 SVB.evalBinOp(State, BO_Sub,
765 nonloc::SymbolVal(OldOffset),
766 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
767 SymMgr.getType(OldOffset)).getAsSymbol();
768 auto NewPos = Pos->setTo(NewOffset);
769 State = setIteratorPosition(State, Iter, NewPos);
770 State = setIteratorPosition(State, RetVal, Postfix ? *Pos : NewPos);
771 C.addTransition(State);
772 }
773}
774
775// This function tells the analyzer's engine that symbols produced by our
776// checker, most notably iterator positions, are relatively small.
777// A distance between items in the container should not be very large.
778// By assuming that it is within around 1/8 of the address space,
779// we can help the analyzer perform operations on these symbols
780// without being afraid of integer overflows.
781// FIXME: Should we provide it as an API, so that all checkers could use it?
782static ProgramStateRef assumeNoOverflow(ProgramStateRef State, SymbolRef Sym,
783 long Scale) {
784 SValBuilder &SVB = State->getStateManager().getSValBuilder();
785 BasicValueFactory &BV = SVB.getBasicValueFactory();
786
787 QualType T = Sym->getType();
788 assert(T->isSignedIntegerOrEnumerationType());
789 APSIntType AT = BV.getAPSIntType(T);
790
791 ProgramStateRef NewState = State;
792
793 llvm::APSInt Max = AT.getMaxValue() / AT.getValue(Scale);
794 SVal IsCappedFromAbove =
795 SVB.evalBinOpNN(State, BO_LE, nonloc::SymbolVal(Sym),
796 nonloc::ConcreteInt(Max), SVB.getConditionType());
797 if (auto DV = IsCappedFromAbove.getAs<DefinedSVal>()) {
798 NewState = NewState->assume(*DV, true);
799 if (!NewState)
800 return State;
801 }
802
803 llvm::APSInt Min = -Max;
804 SVal IsCappedFromBelow =
805 SVB.evalBinOpNN(State, BO_GE, nonloc::SymbolVal(Sym),
806 nonloc::ConcreteInt(Min), SVB.getConditionType());
807 if (auto DV = IsCappedFromBelow.getAs<DefinedSVal>()) {
808 NewState = NewState->assume(*DV, true);
809 if (!NewState)
810 return State;
811 }
812
813 return NewState;
814}
815
816void IteratorChecker::handleRandomIncrOrDecr(CheckerContext &C,
817 OverloadedOperatorKind Op,
818 const SVal &RetVal,
819 const SVal &LHS,
820 const SVal &RHS) const {
821 // Increment or decrement the symbolic expressions which represents the
822 // position of the iterator
823 auto State = C.getState();
824 const auto *Pos = getIteratorPosition(State, LHS);
825 if (!Pos)
826 return;
827
828 const auto *value = &RHS;
829 if (auto loc = RHS.getAs<Loc>()) {
830 const auto val = State->getRawSVal(*loc);
831 value = &val;
832 }
833
834 auto &SymMgr = C.getSymbolManager();
835 auto &SVB = C.getSValBuilder();
836 auto BinOp = (Op == OO_Plus || Op == OO_PlusEqual) ? BO_Add : BO_Sub;
837 const auto OldOffset = Pos->getOffset();
838 SymbolRef NewOffset;
839 if (const auto intValue = value->getAs<nonloc::ConcreteInt>()) {
840 // For concrete integers we can calculate the new position
841 NewOffset = SVB.evalBinOp(State, BinOp, nonloc::SymbolVal(OldOffset),
842 *intValue,
843 SymMgr.getType(OldOffset)).getAsSymbol();
844 } else {
845 // For other symbols create a new symbol to keep expressions simple
846 const auto &LCtx = C.getLocationContext();
847 NewOffset = SymMgr.conjureSymbol(nullptr, LCtx, SymMgr.getType(OldOffset),
848 C.blockCount());
849 State = assumeNoOverflow(State, NewOffset, 4);
850 }
851 auto NewPos = Pos->setTo(NewOffset);
852 auto &TgtVal = (Op == OO_PlusEqual || Op == OO_MinusEqual) ? LHS : RetVal;
853 State = setIteratorPosition(State, TgtVal, NewPos);
854 C.addTransition(State);
855}
856
857void IteratorChecker::verifyRandomIncrOrDecr(CheckerContext &C,
858 OverloadedOperatorKind Op,
859 const SVal &RetVal,
860 const SVal &LHS,
861 const SVal &RHS) const {
862 auto State = C.getState();
863
864 // If the iterator is initially inside its range, then the operation is valid
865 const auto *Pos = getIteratorPosition(State, LHS);
866 if (!Pos || !isOutOfRange(State, *Pos))
867 return;
868
869 auto value = RHS;
870 if (auto loc = RHS.getAs<Loc>()) {
871 value = State->getRawSVal(*loc);
872 }
873
874 // Incremention or decremention by 0 is never bug
875 if (isZero(State, value.castAs<NonLoc>()))
876 return;
877
878 auto &SymMgr = C.getSymbolManager();
879 auto &SVB = C.getSValBuilder();
880 auto BinOp = (Op == OO_Plus || Op == OO_PlusEqual) ? BO_Add : BO_Sub;
881 const auto OldOffset = Pos->getOffset();
882 const auto intValue = value.getAs<nonloc::ConcreteInt>();
883 if (!intValue)
884 return;
885
886 auto NewOffset = SVB.evalBinOp(State, BinOp, nonloc::SymbolVal(OldOffset),
887 *intValue,
888 SymMgr.getType(OldOffset)).getAsSymbol();
889 auto NewPos = Pos->setTo(NewOffset);
890
891 // If out of range, the only valid operation is to step into the range
892 if (isOutOfRange(State, NewPos)) {
893 auto *N = C.generateNonFatalErrorNode(State);
894 if (!N)
895 return;
896 reportOutOfRangeBug("Iterator accessed past its end.", LHS, C, N);
897 }
898}
899
Adam Balogh21583b72018-09-10 09:03:22 +0000900void IteratorChecker::verifyMatch(CheckerContext &C, const SVal &Iter1,
901 const SVal &Iter2) const {
902 // Verify match between the containers of two iterators
903 auto State = C.getState();
904 const auto *Pos1 = getIteratorPosition(State, Iter1);
905 const auto *Pos2 = getIteratorPosition(State, Iter2);
906 if (Pos1 && Pos2 && Pos1->getContainer() != Pos2->getContainer()) {
907 auto *N = C.generateNonFatalErrorNode(State);
908 if (!N)
909 return;
910 reportMismatchedBug("Iterators of different containers used where the "
911 "same container is expected.", Iter1, Iter2, C, N);
912 }
913}
914
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000915void IteratorChecker::handleBegin(CheckerContext &C, const Expr *CE,
916 const SVal &RetVal, const SVal &Cont) const {
917 const auto *ContReg = Cont.getAsRegion();
918 if (!ContReg)
919 return;
920
921 while (const auto *CBOR = ContReg->getAs<CXXBaseObjectRegion>()) {
922 ContReg = CBOR->getSuperRegion();
923 }
924
925 // If the container already has a begin symbol then use it. Otherwise first
926 // create a new one.
927 auto State = C.getState();
928 auto BeginSym = getContainerBegin(State, ContReg);
929 if (!BeginSym) {
930 auto &SymMgr = C.getSymbolManager();
931 BeginSym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
932 C.getASTContext().LongTy, C.blockCount());
933 State = assumeNoOverflow(State, BeginSym, 4);
934 State = createContainerBegin(State, ContReg, BeginSym);
935 }
936 State = setIteratorPosition(State, RetVal,
937 IteratorPosition::getPosition(ContReg, BeginSym));
938 C.addTransition(State);
939}
940
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000941void IteratorChecker::handleEnd(CheckerContext &C, const Expr *CE,
942 const SVal &RetVal, const SVal &Cont) const {
943 const auto *ContReg = Cont.getAsRegion();
944 if (!ContReg)
945 return;
946
947 while (const auto *CBOR = ContReg->getAs<CXXBaseObjectRegion>()) {
948 ContReg = CBOR->getSuperRegion();
949 }
950
951 // If the container already has an end symbol then use it. Otherwise first
952 // create a new one.
953 auto State = C.getState();
954 auto EndSym = getContainerEnd(State, ContReg);
955 if (!EndSym) {
956 auto &SymMgr = C.getSymbolManager();
957 EndSym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
958 C.getASTContext().LongTy, C.blockCount());
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000959 State = assumeNoOverflow(State, EndSym, 4);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000960 State = createContainerEnd(State, ContReg, EndSym);
961 }
962 State = setIteratorPosition(State, RetVal,
963 IteratorPosition::getPosition(ContReg, EndSym));
964 C.addTransition(State);
965}
966
967void IteratorChecker::assignToContainer(CheckerContext &C, const Expr *CE,
968 const SVal &RetVal,
969 const MemRegion *Cont) const {
970 while (const auto *CBOR = Cont->getAs<CXXBaseObjectRegion>()) {
971 Cont = CBOR->getSuperRegion();
972 }
973
974 auto State = C.getState();
975 auto &SymMgr = C.getSymbolManager();
976 auto Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
977 C.getASTContext().LongTy, C.blockCount());
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000978 State = assumeNoOverflow(State, Sym, 4);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000979 State = setIteratorPosition(State, RetVal,
980 IteratorPosition::getPosition(Cont, Sym));
981 C.addTransition(State);
982}
983
Adam Balogh2cfbe932018-08-28 08:41:15 +0000984void IteratorChecker::handleAssign(CheckerContext &C, const SVal &Cont) const {
985 const auto *ContReg = Cont.getAsRegion();
986 if (!ContReg)
987 return;
988
989 while (const auto *CBOR = ContReg->getAs<CXXBaseObjectRegion>()) {
990 ContReg = CBOR->getSuperRegion();
991 }
992
993 // Assignment of a new value to a container always invalidates all its
994 // iterators
995 auto State = C.getState();
996 const auto CData = getContainerData(State, ContReg);
997 if (CData) {
998 State = invalidateAllIteratorPositions(State, ContReg);
999 }
1000
1001 C.addTransition(State);
1002}
1003
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001004void IteratorChecker::reportOutOfRangeBug(const StringRef &Message,
1005 const SVal &Val, CheckerContext &C,
1006 ExplodedNode *ErrNode) const {
1007 auto R = llvm::make_unique<BugReport>(*OutOfRangeBugType, Message, ErrNode);
1008 R->markInteresting(Val);
1009 C.emitReport(std::move(R));
1010}
1011
Adam Balogh21583b72018-09-10 09:03:22 +00001012void IteratorChecker::reportMismatchedBug(const StringRef &Message,
1013 const SVal &Val1, const SVal &Val2,
1014 CheckerContext &C,
1015 ExplodedNode *ErrNode) const {
1016 auto R = llvm::make_unique<BugReport>(*MismatchedBugType, Message, ErrNode);
1017 R->markInteresting(Val1);
1018 R->markInteresting(Val2);
1019 C.emitReport(std::move(R));
1020}
1021
Adam Balogh2cfbe932018-08-28 08:41:15 +00001022void IteratorChecker::reportInvalidatedBug(const StringRef &Message,
1023 const SVal &Val, CheckerContext &C,
1024 ExplodedNode *ErrNode) const {
1025 auto R = llvm::make_unique<BugReport>(*InvalidatedBugType, Message, ErrNode);
1026 R->markInteresting(Val);
1027 C.emitReport(std::move(R));
1028}
1029
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001030namespace {
1031
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001032bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001033bool isGreaterOrEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
1034bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
1035 BinaryOperator::Opcode Opc);
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001036bool compare(ProgramStateRef State, NonLoc NL1, NonLoc NL2,
1037 BinaryOperator::Opcode Opc);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001038
1039bool isIteratorType(const QualType &Type) {
1040 if (Type->isPointerType())
1041 return true;
1042
1043 const auto *CRD = Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
1044 return isIterator(CRD);
1045}
1046
1047bool isIterator(const CXXRecordDecl *CRD) {
1048 if (!CRD)
1049 return false;
1050
1051 const auto Name = CRD->getName();
1052 if (!(Name.endswith_lower("iterator") || Name.endswith_lower("iter") ||
1053 Name.endswith_lower("it")))
1054 return false;
1055
1056 bool HasCopyCtor = false, HasCopyAssign = true, HasDtor = false,
1057 HasPreIncrOp = false, HasPostIncrOp = false, HasDerefOp = false;
1058 for (const auto *Method : CRD->methods()) {
1059 if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(Method)) {
1060 if (Ctor->isCopyConstructor()) {
1061 HasCopyCtor = !Ctor->isDeleted() && Ctor->getAccess() == AS_public;
1062 }
1063 continue;
1064 }
1065 if (const auto *Dtor = dyn_cast<CXXDestructorDecl>(Method)) {
1066 HasDtor = !Dtor->isDeleted() && Dtor->getAccess() == AS_public;
1067 continue;
1068 }
1069 if (Method->isCopyAssignmentOperator()) {
1070 HasCopyAssign = !Method->isDeleted() && Method->getAccess() == AS_public;
1071 continue;
1072 }
1073 if (!Method->isOverloadedOperator())
1074 continue;
1075 const auto OPK = Method->getOverloadedOperator();
1076 if (OPK == OO_PlusPlus) {
1077 HasPreIncrOp = HasPreIncrOp || (Method->getNumParams() == 0);
1078 HasPostIncrOp = HasPostIncrOp || (Method->getNumParams() == 1);
1079 continue;
1080 }
1081 if (OPK == OO_Star) {
1082 HasDerefOp = (Method->getNumParams() == 0);
1083 continue;
1084 }
1085 }
1086
1087 return HasCopyCtor && HasCopyAssign && HasDtor && HasPreIncrOp &&
1088 HasPostIncrOp && HasDerefOp;
1089}
1090
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001091bool isBeginCall(const FunctionDecl *Func) {
1092 const auto *IdInfo = Func->getIdentifier();
1093 if (!IdInfo)
1094 return false;
1095 return IdInfo->getName().endswith_lower("begin");
1096}
1097
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001098bool isEndCall(const FunctionDecl *Func) {
1099 const auto *IdInfo = Func->getIdentifier();
1100 if (!IdInfo)
1101 return false;
1102 return IdInfo->getName().endswith_lower("end");
1103}
1104
Adam Balogh2cfbe932018-08-28 08:41:15 +00001105bool isAssignmentOperator(OverloadedOperatorKind OK) { return OK == OO_Equal; }
1106
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001107bool isSimpleComparisonOperator(OverloadedOperatorKind OK) {
1108 return OK == OO_EqualEqual || OK == OO_ExclaimEqual;
1109}
1110
Adam Balogh2cfbe932018-08-28 08:41:15 +00001111bool isAccessOperator(OverloadedOperatorKind OK) {
1112 return isDereferenceOperator(OK) || isIncrementOperator(OK) ||
1113 isDecrementOperator(OK) || isRandomIncrOrDecrOperator(OK);
1114}
1115
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001116bool isDereferenceOperator(OverloadedOperatorKind OK) {
1117 return OK == OO_Star || OK == OO_Arrow || OK == OO_ArrowStar ||
1118 OK == OO_Subscript;
1119}
1120
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001121bool isIncrementOperator(OverloadedOperatorKind OK) {
1122 return OK == OO_PlusPlus;
1123}
1124
1125bool isDecrementOperator(OverloadedOperatorKind OK) {
1126 return OK == OO_MinusMinus;
1127}
1128
1129bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK) {
1130 return OK == OO_Plus || OK == OO_PlusEqual || OK == OO_Minus ||
1131 OK == OO_MinusEqual;
1132}
1133
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001134BinaryOperator::Opcode getOpcode(const SymExpr *SE) {
1135 if (const auto *BSE = dyn_cast<BinarySymExpr>(SE)) {
1136 return BSE->getOpcode();
1137 } else if (const auto *SC = dyn_cast<SymbolConjured>(SE)) {
Henry Wong073d5f02018-03-20 09:27:02 +00001138 const auto *COE = dyn_cast_or_null<CXXOperatorCallExpr>(SC->getStmt());
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001139 if (!COE)
1140 return BO_Comma; // Extremal value, neither EQ nor NE
1141 if (COE->getOperator() == OO_EqualEqual) {
1142 return BO_EQ;
1143 } else if (COE->getOperator() == OO_ExclaimEqual) {
1144 return BO_NE;
1145 }
1146 return BO_Comma; // Extremal value, neither EQ nor NE
1147 }
1148 return BO_Comma; // Extremal value, neither EQ nor NE
1149}
1150
1151const RegionOrSymbol getRegionOrSymbol(const SVal &Val) {
1152 if (const auto Reg = Val.getAsRegion()) {
1153 return Reg;
1154 } else if (const auto Sym = Val.getAsSymbol()) {
1155 return Sym;
1156 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
1157 return LCVal->getRegion();
1158 }
1159 return RegionOrSymbol();
1160}
1161
1162const ProgramStateRef processComparison(ProgramStateRef State,
1163 RegionOrSymbol LVal,
1164 RegionOrSymbol RVal, bool Equal) {
1165 const auto *LPos = getIteratorPosition(State, LVal);
1166 const auto *RPos = getIteratorPosition(State, RVal);
1167 if (LPos && !RPos) {
1168 State = adjustIteratorPosition(State, RVal, *LPos, Equal);
1169 } else if (!LPos && RPos) {
1170 State = adjustIteratorPosition(State, LVal, *RPos, Equal);
1171 } else if (LPos && RPos) {
1172 State = relateIteratorPositions(State, *LPos, *RPos, Equal);
1173 }
1174 return State;
1175}
1176
1177const ProgramStateRef saveComparison(ProgramStateRef State,
1178 const SymExpr *Condition, const SVal &LVal,
1179 const SVal &RVal, bool Eq) {
1180 const auto Left = getRegionOrSymbol(LVal);
1181 const auto Right = getRegionOrSymbol(RVal);
1182 if (!Left || !Right)
1183 return State;
1184 return State->set<IteratorComparisonMap>(Condition,
1185 IteratorComparison(Left, Right, Eq));
1186}
1187
1188const IteratorComparison *loadComparison(ProgramStateRef State,
1189 const SymExpr *Condition) {
1190 return State->get<IteratorComparisonMap>(Condition);
1191}
1192
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001193SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont) {
1194 const auto *CDataPtr = getContainerData(State, Cont);
1195 if (!CDataPtr)
1196 return nullptr;
1197
1198 return CDataPtr->getBegin();
1199}
1200
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001201SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont) {
1202 const auto *CDataPtr = getContainerData(State, Cont);
1203 if (!CDataPtr)
1204 return nullptr;
1205
1206 return CDataPtr->getEnd();
1207}
1208
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001209ProgramStateRef createContainerBegin(ProgramStateRef State,
1210 const MemRegion *Cont,
1211 const SymbolRef Sym) {
1212 // Only create if it does not exist
1213 const auto *CDataPtr = getContainerData(State, Cont);
1214 if (CDataPtr) {
1215 if (CDataPtr->getBegin()) {
1216 return State;
1217 }
1218 const auto CData = CDataPtr->newBegin(Sym);
1219 return setContainerData(State, Cont, CData);
1220 }
1221 const auto CData = ContainerData::fromBegin(Sym);
1222 return setContainerData(State, Cont, CData);
1223}
1224
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001225ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
1226 const SymbolRef Sym) {
1227 // Only create if it does not exist
1228 const auto *CDataPtr = getContainerData(State, Cont);
1229 if (CDataPtr) {
1230 if (CDataPtr->getEnd()) {
1231 return State;
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001232 }
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001233 const auto CData = CDataPtr->newEnd(Sym);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001234 return setContainerData(State, Cont, CData);
1235 }
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001236 const auto CData = ContainerData::fromEnd(Sym);
1237 return setContainerData(State, Cont, CData);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001238}
1239
1240const ContainerData *getContainerData(ProgramStateRef State,
1241 const MemRegion *Cont) {
1242 return State->get<ContainerMap>(Cont);
1243}
1244
1245ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
1246 const ContainerData &CData) {
1247 return State->set<ContainerMap>(Cont, CData);
1248}
1249
1250const IteratorPosition *getIteratorPosition(ProgramStateRef State,
1251 const SVal &Val) {
1252 if (const auto Reg = Val.getAsRegion()) {
1253 return State->get<IteratorRegionMap>(Reg);
1254 } else if (const auto Sym = Val.getAsSymbol()) {
1255 return State->get<IteratorSymbolMap>(Sym);
1256 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
1257 return State->get<IteratorRegionMap>(LCVal->getRegion());
1258 }
1259 return nullptr;
1260}
1261
1262const IteratorPosition *getIteratorPosition(ProgramStateRef State,
1263 RegionOrSymbol RegOrSym) {
1264 if (RegOrSym.is<const MemRegion *>()) {
1265 return State->get<IteratorRegionMap>(RegOrSym.get<const MemRegion *>());
1266 } else if (RegOrSym.is<SymbolRef>()) {
1267 return State->get<IteratorSymbolMap>(RegOrSym.get<SymbolRef>());
1268 }
1269 return nullptr;
1270}
1271
1272ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
1273 const IteratorPosition &Pos) {
1274 if (const auto Reg = Val.getAsRegion()) {
1275 return State->set<IteratorRegionMap>(Reg, Pos);
1276 } else if (const auto Sym = Val.getAsSymbol()) {
1277 return State->set<IteratorSymbolMap>(Sym, Pos);
1278 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
1279 return State->set<IteratorRegionMap>(LCVal->getRegion(), Pos);
1280 }
1281 return nullptr;
1282}
1283
1284ProgramStateRef setIteratorPosition(ProgramStateRef State,
1285 RegionOrSymbol RegOrSym,
1286 const IteratorPosition &Pos) {
1287 if (RegOrSym.is<const MemRegion *>()) {
1288 return State->set<IteratorRegionMap>(RegOrSym.get<const MemRegion *>(),
1289 Pos);
1290 } else if (RegOrSym.is<SymbolRef>()) {
1291 return State->set<IteratorSymbolMap>(RegOrSym.get<SymbolRef>(), Pos);
1292 }
1293 return nullptr;
1294}
1295
1296ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val) {
1297 if (const auto Reg = Val.getAsRegion()) {
1298 return State->remove<IteratorRegionMap>(Reg);
1299 } else if (const auto Sym = Val.getAsSymbol()) {
1300 return State->remove<IteratorSymbolMap>(Sym);
1301 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
1302 return State->remove<IteratorRegionMap>(LCVal->getRegion());
1303 }
1304 return nullptr;
1305}
1306
1307ProgramStateRef adjustIteratorPosition(ProgramStateRef State,
1308 RegionOrSymbol RegOrSym,
1309 const IteratorPosition &Pos,
1310 bool Equal) {
1311 if (Equal) {
1312 return setIteratorPosition(State, RegOrSym, Pos);
1313 } else {
1314 return State;
1315 }
1316}
1317
1318ProgramStateRef relateIteratorPositions(ProgramStateRef State,
1319 const IteratorPosition &Pos1,
1320 const IteratorPosition &Pos2,
1321 bool Equal) {
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001322 auto &SVB = State->getStateManager().getSValBuilder();
Adam Balogh0a7592b2018-07-16 09:27:27 +00001323
1324 // FIXME: This code should be reworked as follows:
1325 // 1. Subtract the operands using evalBinOp().
1326 // 2. Assume that the result doesn't overflow.
1327 // 3. Compare the result to 0.
1328 // 4. Assume the result of the comparison.
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001329 const auto comparison =
1330 SVB.evalBinOp(State, BO_EQ, nonloc::SymbolVal(Pos1.getOffset()),
Adam Balogh0a7592b2018-07-16 09:27:27 +00001331 nonloc::SymbolVal(Pos2.getOffset()),
1332 SVB.getConditionType());
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001333
Adam Balogh0a7592b2018-07-16 09:27:27 +00001334 assert(comparison.getAs<DefinedSVal>() &&
1335 "Symbol comparison must be a `DefinedSVal`");
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001336
Adam Balogh0a7592b2018-07-16 09:27:27 +00001337 auto NewState = State->assume(comparison.castAs<DefinedSVal>(), Equal);
1338 if (const auto CompSym = comparison.getAsSymbol()) {
1339 assert(isa<SymIntExpr>(CompSym) &&
1340 "Symbol comparison must be a `SymIntExpr`");
1341 assert(BinaryOperator::isComparisonOp(
1342 cast<SymIntExpr>(CompSym)->getOpcode()) &&
1343 "Symbol comparison must be a comparison");
1344 return assumeNoOverflow(NewState, cast<SymIntExpr>(CompSym)->getLHS(), 2);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001345 }
1346
Adam Balogh0a7592b2018-07-16 09:27:27 +00001347 return NewState;
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001348}
1349
Adam Balogha6921202018-07-30 08:52:21 +00001350bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont) {
1351 auto RegionMap = State->get<IteratorRegionMap>();
1352 for (const auto Reg : RegionMap) {
1353 if (Reg.second.getContainer() == Cont)
1354 return true;
1355 }
1356
1357 auto SymbolMap = State->get<IteratorSymbolMap>();
1358 for (const auto Sym : SymbolMap) {
1359 if (Sym.second.getContainer() == Cont)
1360 return true;
1361 }
1362
1363 return false;
1364}
1365
Adam Balogh21583b72018-09-10 09:03:22 +00001366bool isBoundThroughLazyCompoundVal(const Environment &Env,
1367 const MemRegion *Reg) {
1368 for (const auto Binding: Env) {
1369 if (const auto LCVal = Binding.second.getAs<nonloc::LazyCompoundVal>()) {
1370 if (LCVal->getRegion() == Reg)
1371 return true;
1372 }
1373 }
1374
1375 return false;
1376}
1377
Adam Balogh2cfbe932018-08-28 08:41:15 +00001378template <typename Condition, typename Process>
1379ProgramStateRef processIteratorPositions(ProgramStateRef State, Condition Cond,
1380 Process Proc) {
1381 auto &RegionMapFactory = State->get_context<IteratorRegionMap>();
1382 auto RegionMap = State->get<IteratorRegionMap>();
1383 bool Changed = false;
1384 for (const auto Reg : RegionMap) {
1385 if (Cond(Reg.second)) {
1386 RegionMap = RegionMapFactory.add(RegionMap, Reg.first, Proc(Reg.second));
1387 Changed = true;
1388 }
1389 }
1390
1391 if (Changed)
1392 State = State->set<IteratorRegionMap>(RegionMap);
1393
1394 auto &SymbolMapFactory = State->get_context<IteratorSymbolMap>();
1395 auto SymbolMap = State->get<IteratorSymbolMap>();
1396 Changed = false;
1397 for (const auto Sym : SymbolMap) {
1398 if (Cond(Sym.second)) {
1399 SymbolMap = SymbolMapFactory.add(SymbolMap, Sym.first, Proc(Sym.second));
1400 Changed = true;
1401 }
1402 }
1403
1404 if (Changed)
1405 State = State->set<IteratorSymbolMap>(SymbolMap);
1406
1407 return State;
1408}
1409
1410ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State,
1411 const MemRegion *Cont) {
1412 auto MatchCont = [&](const IteratorPosition &Pos) {
1413 return Pos.getContainer() == Cont;
1414 };
1415 auto Invalidate = [&](const IteratorPosition &Pos) {
1416 return Pos.invalidate();
1417 };
1418 return processIteratorPositions(State, MatchCont, Invalidate);
1419}
1420
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001421bool isZero(ProgramStateRef State, const NonLoc &Val) {
1422 auto &BVF = State->getBasicVals();
1423 return compare(State, Val,
1424 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))),
1425 BO_EQ);
1426}
1427
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001428bool isOutOfRange(ProgramStateRef State, const IteratorPosition &Pos) {
1429 const auto *Cont = Pos.getContainer();
1430 const auto *CData = getContainerData(State, Cont);
1431 if (!CData)
1432 return false;
1433
1434 // Out of range means less than the begin symbol or greater or equal to the
1435 // end symbol.
1436
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001437 const auto Beg = CData->getBegin();
1438 if (Beg) {
1439 if (isLess(State, Pos.getOffset(), Beg)) {
1440 return true;
1441 }
1442 }
1443
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001444 const auto End = CData->getEnd();
1445 if (End) {
1446 if (isGreaterOrEqual(State, Pos.getOffset(), End)) {
1447 return true;
1448 }
1449 }
1450
1451 return false;
1452}
1453
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001454bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
1455 return compare(State, Sym1, Sym2, BO_LT);
1456}
1457
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001458bool isGreaterOrEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
1459 return compare(State, Sym1, Sym2, BO_GE);
1460}
1461
1462bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
1463 BinaryOperator::Opcode Opc) {
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001464 return compare(State, nonloc::SymbolVal(Sym1), nonloc::SymbolVal(Sym2), Opc);
1465}
1466
1467bool compare(ProgramStateRef State, NonLoc NL1, NonLoc NL2,
1468 BinaryOperator::Opcode Opc) {
1469 auto &SVB = State->getStateManager().getSValBuilder();
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001470
1471 const auto comparison =
Adam Balogh0a7592b2018-07-16 09:27:27 +00001472 SVB.evalBinOp(State, Opc, NL1, NL2, SVB.getConditionType());
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001473
Adam Balogh0a7592b2018-07-16 09:27:27 +00001474 assert(comparison.getAs<DefinedSVal>() &&
1475 "Symbol comparison must be a `DefinedSVal`");
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001476
Adam Balogh0a7592b2018-07-16 09:27:27 +00001477 return !State->assume(comparison.castAs<DefinedSVal>(), false);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001478}
1479
1480} // namespace
1481
1482#define REGISTER_CHECKER(name) \
1483 void ento::register##name(CheckerManager &Mgr) { \
1484 auto *checker = Mgr.registerChecker<IteratorChecker>(); \
1485 checker->ChecksEnabled[IteratorChecker::CK_##name] = true; \
1486 checker->CheckNames[IteratorChecker::CK_##name] = \
1487 Mgr.getCurrentCheckName(); \
1488 }
1489
1490REGISTER_CHECKER(IteratorRangeChecker)
Adam Balogh21583b72018-09-10 09:03:22 +00001491REGISTER_CHECKER(MismatchedIteratorChecker)
Adam Balogh2cfbe932018-08-28 08:41:15 +00001492REGISTER_CHECKER(InvalidatedIteratorChecker)