blob: a4c2891e59b891ee5c973501055a3e10ffc3df34 [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
Adam Balogh6b23b1a2018-09-10 09:04:27 +0000117 IteratorPosition reAssign(const MemRegion *NewCont) const {
118 return IteratorPosition(NewCont, Valid, Offset);
119 }
120
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000121 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 bool operator!=(const IteratorPosition &X) const {
Adam Balogh2cfbe932018-08-28 08:41:15 +0000126 return Cont != X.Cont || Valid != X.Valid || Offset != X.Offset;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000127 }
128
129 void Profile(llvm::FoldingSetNodeID &ID) const {
130 ID.AddPointer(Cont);
Adam Balogh2cfbe932018-08-28 08:41:15 +0000131 ID.AddInteger(Valid);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000132 ID.Add(Offset);
133 }
134};
135
136typedef llvm::PointerUnion<const MemRegion *, SymbolRef> RegionOrSymbol;
137
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000138// Structure to record the symbolic begin and end position of a container
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000139struct ContainerData {
140private:
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000141 const SymbolRef Begin, End;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000142
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000143 ContainerData(SymbolRef B, SymbolRef E) : Begin(B), End(E) {}
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000144
145public:
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000146 static ContainerData fromBegin(SymbolRef B) {
147 return ContainerData(B, nullptr);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000148 }
149
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000150 static ContainerData fromEnd(SymbolRef E) {
151 return ContainerData(nullptr, E);
152 }
153
154 SymbolRef getBegin() const { return Begin; }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000155 SymbolRef getEnd() const { return End; }
156
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000157 ContainerData newBegin(SymbolRef B) const { return ContainerData(B, End); }
158
159 ContainerData newEnd(SymbolRef E) const { return ContainerData(Begin, E); }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000160
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 bool operator!=(const ContainerData &X) const {
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000166 return Begin != X.Begin || End != X.End;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000167 }
168
169 void Profile(llvm::FoldingSetNodeID &ID) const {
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000170 ID.Add(Begin);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000171 ID.Add(End);
172 }
173};
174
175// Structure fo recording iterator comparisons. We needed to retrieve the
176// original comparison expression in assumptions.
177struct IteratorComparison {
178private:
179 RegionOrSymbol Left, Right;
180 bool Equality;
181
182public:
183 IteratorComparison(RegionOrSymbol L, RegionOrSymbol R, bool Eq)
184 : Left(L), Right(R), Equality(Eq) {}
185
186 RegionOrSymbol getLeft() const { return Left; }
187 RegionOrSymbol getRight() const { return Right; }
188 bool isEquality() const { return Equality; }
189 bool operator==(const IteratorComparison &X) const {
190 return Left == X.Left && Right == X.Right && Equality == X.Equality;
191 }
192 bool operator!=(const IteratorComparison &X) const {
193 return Left != X.Left || Right != X.Right || Equality != X.Equality;
194 }
195 void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(Equality); }
196};
197
198class IteratorChecker
199 : public Checker<check::PreCall, check::PostCall,
200 check::PostStmt<MaterializeTemporaryExpr>,
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000201 check::LiveSymbols, check::DeadSymbols,
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000202 eval::Assume> {
203
204 std::unique_ptr<BugType> OutOfRangeBugType;
Adam Balogh21583b72018-09-10 09:03:22 +0000205 std::unique_ptr<BugType> MismatchedBugType;
Adam Balogh2cfbe932018-08-28 08:41:15 +0000206 std::unique_ptr<BugType> InvalidatedBugType;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000207
208 void handleComparison(CheckerContext &C, const SVal &RetVal, const SVal &LVal,
209 const SVal &RVal, OverloadedOperatorKind Op) const;
Adam Balogh2cfbe932018-08-28 08:41:15 +0000210 void verifyAccess(CheckerContext &C, const SVal &Val) const;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000211 void verifyDereference(CheckerContext &C, const SVal &Val) const;
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000212 void handleIncrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
213 bool Postfix) const;
214 void handleDecrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
215 bool Postfix) const;
216 void handleRandomIncrOrDecr(CheckerContext &C, OverloadedOperatorKind Op,
217 const SVal &RetVal, const SVal &LHS,
218 const SVal &RHS) const;
219 void handleBegin(CheckerContext &C, const Expr *CE, const SVal &RetVal,
220 const SVal &Cont) const;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000221 void handleEnd(CheckerContext &C, const Expr *CE, const SVal &RetVal,
222 const SVal &Cont) const;
223 void assignToContainer(CheckerContext &C, const Expr *CE, const SVal &RetVal,
224 const MemRegion *Cont) const;
Adam Balogh6b23b1a2018-09-10 09:04:27 +0000225 void handleAssign(CheckerContext &C, const SVal &Cont,
226 const Expr *CE = nullptr,
227 const SVal &OldCont = UndefinedVal()) const;
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000228 void verifyRandomIncrOrDecr(CheckerContext &C, OverloadedOperatorKind Op,
229 const SVal &RetVal, const SVal &LHS,
230 const SVal &RHS) const;
Adam Balogh21583b72018-09-10 09:03:22 +0000231 void verifyMatch(CheckerContext &C, const SVal &Iter1,
232 const SVal &Iter2) const;
233
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000234 void reportOutOfRangeBug(const StringRef &Message, const SVal &Val,
235 CheckerContext &C, ExplodedNode *ErrNode) const;
Adam Balogh21583b72018-09-10 09:03:22 +0000236 void reportMismatchedBug(const StringRef &Message, const SVal &Val1,
237 const SVal &Val2, CheckerContext &C,
238 ExplodedNode *ErrNode) const;
Adam Balogh2cfbe932018-08-28 08:41:15 +0000239 void reportInvalidatedBug(const StringRef &Message, const SVal &Val,
240 CheckerContext &C, ExplodedNode *ErrNode) const;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000241
242public:
243 IteratorChecker();
244
245 enum CheckKind {
246 CK_IteratorRangeChecker,
Adam Balogh21583b72018-09-10 09:03:22 +0000247 CK_MismatchedIteratorChecker,
Adam Balogh2cfbe932018-08-28 08:41:15 +0000248 CK_InvalidatedIteratorChecker,
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000249 CK_NumCheckKinds
250 };
251
252 DefaultBool ChecksEnabled[CK_NumCheckKinds];
253 CheckName CheckNames[CK_NumCheckKinds];
254
255 void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
256 void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
257 void checkPostStmt(const MaterializeTemporaryExpr *MTE,
258 CheckerContext &C) const;
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000259 void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000260 void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
261 ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond,
262 bool Assumption) const;
263};
264} // namespace
265
266REGISTER_MAP_WITH_PROGRAMSTATE(IteratorSymbolMap, SymbolRef, IteratorPosition)
267REGISTER_MAP_WITH_PROGRAMSTATE(IteratorRegionMap, const MemRegion *,
268 IteratorPosition)
269
270REGISTER_MAP_WITH_PROGRAMSTATE(ContainerMap, const MemRegion *, ContainerData)
271
272REGISTER_MAP_WITH_PROGRAMSTATE(IteratorComparisonMap, const SymExpr *,
273 IteratorComparison)
274
275namespace {
276
277bool isIteratorType(const QualType &Type);
278bool isIterator(const CXXRecordDecl *CRD);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000279bool isBeginCall(const FunctionDecl *Func);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000280bool isEndCall(const FunctionDecl *Func);
Adam Balogh2cfbe932018-08-28 08:41:15 +0000281bool isAssignmentOperator(OverloadedOperatorKind OK);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000282bool isSimpleComparisonOperator(OverloadedOperatorKind OK);
Adam Balogh2cfbe932018-08-28 08:41:15 +0000283bool isAccessOperator(OverloadedOperatorKind OK);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000284bool isDereferenceOperator(OverloadedOperatorKind OK);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000285bool isIncrementOperator(OverloadedOperatorKind OK);
286bool isDecrementOperator(OverloadedOperatorKind OK);
287bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000288BinaryOperator::Opcode getOpcode(const SymExpr *SE);
289const RegionOrSymbol getRegionOrSymbol(const SVal &Val);
290const ProgramStateRef processComparison(ProgramStateRef State,
291 RegionOrSymbol LVal,
292 RegionOrSymbol RVal, bool Equal);
293const ProgramStateRef saveComparison(ProgramStateRef State,
294 const SymExpr *Condition, const SVal &LVal,
295 const SVal &RVal, bool Eq);
296const IteratorComparison *loadComparison(ProgramStateRef State,
297 const SymExpr *Condition);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000298SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000299SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000300ProgramStateRef createContainerBegin(ProgramStateRef State,
301 const MemRegion *Cont,
302 const SymbolRef Sym);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000303ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
304 const SymbolRef Sym);
305const IteratorPosition *getIteratorPosition(ProgramStateRef State,
306 const SVal &Val);
307const IteratorPosition *getIteratorPosition(ProgramStateRef State,
308 RegionOrSymbol RegOrSym);
309ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
310 const IteratorPosition &Pos);
311ProgramStateRef setIteratorPosition(ProgramStateRef State,
312 RegionOrSymbol RegOrSym,
313 const IteratorPosition &Pos);
314ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val);
315ProgramStateRef adjustIteratorPosition(ProgramStateRef State,
316 RegionOrSymbol RegOrSym,
317 const IteratorPosition &Pos, bool Equal);
318ProgramStateRef relateIteratorPositions(ProgramStateRef State,
319 const IteratorPosition &Pos1,
320 const IteratorPosition &Pos2,
321 bool Equal);
Adam Balogh2cfbe932018-08-28 08:41:15 +0000322ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State,
323 const MemRegion *Cont);
Adam Balogh6b23b1a2018-09-10 09:04:27 +0000324ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State,
325 const MemRegion *Cont,
326 const MemRegion *NewCont);
327ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State,
328 const MemRegion *Cont,
329 const MemRegion *NewCont,
330 SymbolRef Offset,
331 BinaryOperator::Opcode Opc);
332ProgramStateRef rebaseSymbolInIteratorPositionsIf(
333 ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym,
334 SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000335const ContainerData *getContainerData(ProgramStateRef State,
336 const MemRegion *Cont);
337ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
338 const ContainerData &CData);
Adam Balogha6921202018-07-30 08:52:21 +0000339bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont);
Adam Balogh21583b72018-09-10 09:03:22 +0000340bool isBoundThroughLazyCompoundVal(const Environment &Env,
341 const MemRegion *Reg);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000342bool isOutOfRange(ProgramStateRef State, const IteratorPosition &Pos);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000343bool isZero(ProgramStateRef State, const NonLoc &Val);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000344} // namespace
345
346IteratorChecker::IteratorChecker() {
347 OutOfRangeBugType.reset(
348 new BugType(this, "Iterator out of range", "Misuse of STL APIs"));
349 OutOfRangeBugType->setSuppressOnSink(true);
Adam Balogh21583b72018-09-10 09:03:22 +0000350 MismatchedBugType.reset(
351 new BugType(this, "Iterator(s) mismatched", "Misuse of STL APIs"));
352 MismatchedBugType->setSuppressOnSink(true);
Adam Balogh2cfbe932018-08-28 08:41:15 +0000353 InvalidatedBugType.reset(
354 new BugType(this, "Iterator invalidated", "Misuse of STL APIs"));
355 InvalidatedBugType->setSuppressOnSink(true);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000356}
357
358void IteratorChecker::checkPreCall(const CallEvent &Call,
359 CheckerContext &C) const {
Adam Balogh2cfbe932018-08-28 08:41:15 +0000360 // Check for out of range access or access of invalidated position and
361 // iterator mismatches
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000362 const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
363 if (!Func)
364 return;
365
366 if (Func->isOverloadedOperator()) {
Adam Balogh2cfbe932018-08-28 08:41:15 +0000367 if (ChecksEnabled[CK_InvalidatedIteratorChecker] &&
368 isAccessOperator(Func->getOverloadedOperator())) {
369 // Check for any kind of access of invalidated iterator positions
370 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
371 verifyAccess(C, InstCall->getCXXThisVal());
372 } else {
373 verifyAccess(C, Call.getArgSVal(0));
374 }
375 }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000376 if (ChecksEnabled[CK_IteratorRangeChecker] &&
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000377 isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) {
378 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
379 // Check for out-of-range incrementions and decrementions
380 if (Call.getNumArgs() >= 1) {
381 verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(),
382 Call.getReturnValue(),
383 InstCall->getCXXThisVal(), Call.getArgSVal(0));
384 }
385 } else {
386 if (Call.getNumArgs() >= 2) {
387 verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(),
388 Call.getReturnValue(), Call.getArgSVal(0),
389 Call.getArgSVal(1));
390 }
391 }
392 } else if (ChecksEnabled[CK_IteratorRangeChecker] &&
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000393 isDereferenceOperator(Func->getOverloadedOperator())) {
394 // Check for dereference of out-of-range iterators
395 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
396 verifyDereference(C, InstCall->getCXXThisVal());
397 } else {
398 verifyDereference(C, Call.getArgSVal(0));
399 }
400 }
Adam Balogh21583b72018-09-10 09:03:22 +0000401 } else if (!isa<CXXConstructorCall>(&Call)) {
402 // The main purpose of iterators is to abstract away from different
403 // containers and provide a (maybe limited) uniform access to them.
404 // This implies that any correctly written template function that
405 // works on multiple containers using iterators takes different
406 // template parameters for different containers. So we can safely
407 // assume that passing iterators of different containers as arguments
408 // whose type replaces the same template parameter is a bug.
409 //
410 // Example:
411 // template<typename I1, typename I2>
412 // void f(I1 first1, I1 last1, I2 first2, I2 last2);
413 //
414 // In this case the first two arguments to f() must be iterators must belong
415 // to the same container and the last to also to the same container but
416 // not neccessarily to the same as the first two.
417
418 if (!ChecksEnabled[CK_MismatchedIteratorChecker])
419 return;
420
421 const auto *Templ = Func->getPrimaryTemplate();
422 if (!Templ)
423 return;
424
425 const auto *TParams = Templ->getTemplateParameters();
426 const auto *TArgs = Func->getTemplateSpecializationArgs();
427
428 // Iterate over all the template parameters
429 for (size_t I = 0; I < TParams->size(); ++I) {
430 const auto *TPDecl = dyn_cast<TemplateTypeParmDecl>(TParams->getParam(I));
431 if (!TPDecl)
432 continue;
433
434 if (TPDecl->isParameterPack())
435 continue;
436
437 const auto TAType = TArgs->get(I).getAsType();
438 if (!isIteratorType(TAType))
439 continue;
440
441 SVal LHS = UndefinedVal();
442
443 // For every template parameter which is an iterator type in the
444 // instantiation look for all functions' parameters' type by it and
445 // check whether they belong to the same container
446 for (auto J = 0U; J < Func->getNumParams(); ++J) {
447 const auto *Param = Func->getParamDecl(J);
448 const auto *ParamType =
449 Param->getType()->getAs<SubstTemplateTypeParmType>();
450 if (!ParamType ||
451 ParamType->getReplacedParameter()->getDecl() != TPDecl)
452 continue;
453 if (LHS.isUndef()) {
454 LHS = Call.getArgSVal(J);
455 } else {
456 verifyMatch(C, LHS, Call.getArgSVal(J));
457 }
458 }
459 }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000460 }
461}
462
463void IteratorChecker::checkPostCall(const CallEvent &Call,
464 CheckerContext &C) const {
465 // Record new iterator positions and iterator position changes
466 const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
467 if (!Func)
468 return;
469
470 if (Func->isOverloadedOperator()) {
471 const auto Op = Func->getOverloadedOperator();
Adam Balogh2cfbe932018-08-28 08:41:15 +0000472 if (isAssignmentOperator(Op)) {
473 const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call);
Adam Balogh6b23b1a2018-09-10 09:04:27 +0000474 if (Func->getParamDecl(0)->getType()->isRValueReferenceType()) {
475 handleAssign(C, InstCall->getCXXThisVal(), Call.getOriginExpr(),
476 Call.getArgSVal(0));
477 } else {
478 handleAssign(C, InstCall->getCXXThisVal());
479 }
Adam Balogh2cfbe932018-08-28 08:41:15 +0000480 } else if (isSimpleComparisonOperator(Op)) {
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000481 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
482 handleComparison(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
483 Call.getArgSVal(0), Op);
484 } else {
485 handleComparison(C, Call.getReturnValue(), Call.getArgSVal(0),
486 Call.getArgSVal(1), Op);
487 }
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000488 } else if (isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) {
489 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
490 if (Call.getNumArgs() >= 1) {
491 handleRandomIncrOrDecr(C, Func->getOverloadedOperator(),
492 Call.getReturnValue(),
493 InstCall->getCXXThisVal(), Call.getArgSVal(0));
494 }
495 } else {
496 if (Call.getNumArgs() >= 2) {
497 handleRandomIncrOrDecr(C, Func->getOverloadedOperator(),
498 Call.getReturnValue(), Call.getArgSVal(0),
499 Call.getArgSVal(1));
500 }
501 }
502 } else if (isIncrementOperator(Func->getOverloadedOperator())) {
503 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
504 handleIncrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
505 Call.getNumArgs());
506 } else {
507 handleIncrement(C, Call.getReturnValue(), Call.getArgSVal(0),
508 Call.getNumArgs());
509 }
510 } else if (isDecrementOperator(Func->getOverloadedOperator())) {
511 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
512 handleDecrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
513 Call.getNumArgs());
514 } else {
515 handleDecrement(C, Call.getReturnValue(), Call.getArgSVal(0),
516 Call.getNumArgs());
517 }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000518 }
519 } else {
520 const auto *OrigExpr = Call.getOriginExpr();
521 if (!OrigExpr)
522 return;
523
524 if (!isIteratorType(Call.getResultType()))
525 return;
526
527 auto State = C.getState();
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000528
529 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000530 if (isBeginCall(Func)) {
531 handleBegin(C, OrigExpr, Call.getReturnValue(),
532 InstCall->getCXXThisVal());
533 return;
534 }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000535 if (isEndCall(Func)) {
536 handleEnd(C, OrigExpr, Call.getReturnValue(),
537 InstCall->getCXXThisVal());
538 return;
539 }
540 }
541
Adam Balogh6b23b1a2018-09-10 09:04:27 +0000542 // Already bound to container?
543 if (getIteratorPosition(State, Call.getReturnValue()))
544 return;
545
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000546 // Copy-like and move constructors
547 if (isa<CXXConstructorCall>(&Call) && Call.getNumArgs() == 1) {
548 if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(0))) {
549 State = setIteratorPosition(State, Call.getReturnValue(), *Pos);
550 if (cast<CXXConstructorDecl>(Func)->isMoveConstructor()) {
551 State = removeIteratorPosition(State, Call.getArgSVal(0));
552 }
553 C.addTransition(State);
554 return;
555 }
556 }
557
558 // Assumption: if return value is an iterator which is not yet bound to a
559 // container, then look for the first iterator argument, and
560 // bind the return value to the same container. This approach
561 // works for STL algorithms.
562 // FIXME: Add a more conservative mode
563 for (unsigned i = 0; i < Call.getNumArgs(); ++i) {
564 if (isIteratorType(Call.getArgExpr(i)->getType())) {
565 if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(i))) {
566 assignToContainer(C, OrigExpr, Call.getReturnValue(),
567 Pos->getContainer());
568 return;
569 }
570 }
571 }
572 }
573}
574
575void IteratorChecker::checkPostStmt(const MaterializeTemporaryExpr *MTE,
576 CheckerContext &C) const {
577 /* Transfer iterator state to temporary objects */
578 auto State = C.getState();
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000579 const auto *Pos =
George Karpenkovd703ec92018-01-17 20:27:29 +0000580 getIteratorPosition(State, C.getSVal(MTE->GetTemporaryExpr()));
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000581 if (!Pos)
582 return;
George Karpenkovd703ec92018-01-17 20:27:29 +0000583 State = setIteratorPosition(State, C.getSVal(MTE), *Pos);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000584 C.addTransition(State);
585}
586
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000587void IteratorChecker::checkLiveSymbols(ProgramStateRef State,
588 SymbolReaper &SR) const {
589 // Keep symbolic expressions of iterator positions, container begins and ends
590 // alive
591 auto RegionMap = State->get<IteratorRegionMap>();
592 for (const auto Reg : RegionMap) {
Adam Balogh0a7592b2018-07-16 09:27:27 +0000593 const auto Offset = Reg.second.getOffset();
594 for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
595 if (isa<SymbolData>(*i))
596 SR.markLive(*i);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000597 }
598
599 auto SymbolMap = State->get<IteratorSymbolMap>();
600 for (const auto Sym : SymbolMap) {
Adam Balogh0a7592b2018-07-16 09:27:27 +0000601 const auto Offset = Sym.second.getOffset();
602 for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
603 if (isa<SymbolData>(*i))
604 SR.markLive(*i);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000605 }
606
607 auto ContMap = State->get<ContainerMap>();
608 for (const auto Cont : ContMap) {
609 const auto CData = Cont.second;
610 if (CData.getBegin()) {
611 SR.markLive(CData.getBegin());
612 }
613 if (CData.getEnd()) {
614 SR.markLive(CData.getEnd());
615 }
616 }
617}
618
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000619void IteratorChecker::checkDeadSymbols(SymbolReaper &SR,
620 CheckerContext &C) const {
621 // Cleanup
622 auto State = C.getState();
623
624 auto RegionMap = State->get<IteratorRegionMap>();
625 for (const auto Reg : RegionMap) {
626 if (!SR.isLiveRegion(Reg.first)) {
Adam Balogh21583b72018-09-10 09:03:22 +0000627 // The region behind the `LazyCompoundVal` is often cleaned up before
628 // the `LazyCompoundVal` itself. If there are iterator positions keyed
629 // by these regions their cleanup must be deferred.
630 if (!isBoundThroughLazyCompoundVal(State->getEnvironment(), Reg.first)) {
631 State = State->remove<IteratorRegionMap>(Reg.first);
632 }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000633 }
634 }
635
636 auto SymbolMap = State->get<IteratorSymbolMap>();
637 for (const auto Sym : SymbolMap) {
638 if (!SR.isLive(Sym.first)) {
639 State = State->remove<IteratorSymbolMap>(Sym.first);
640 }
641 }
642
643 auto ContMap = State->get<ContainerMap>();
644 for (const auto Cont : ContMap) {
645 if (!SR.isLiveRegion(Cont.first)) {
Adam Balogha6921202018-07-30 08:52:21 +0000646 // We must keep the container data while it has live iterators to be able
647 // to compare them to the begin and the end of the container.
648 if (!hasLiveIterators(State, Cont.first)) {
649 State = State->remove<ContainerMap>(Cont.first);
650 }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000651 }
652 }
653
654 auto ComparisonMap = State->get<IteratorComparisonMap>();
655 for (const auto Comp : ComparisonMap) {
656 if (!SR.isLive(Comp.first)) {
657 State = State->remove<IteratorComparisonMap>(Comp.first);
658 }
659 }
Reka Kovacse48ea892018-07-30 16:14:59 +0000660
661 C.addTransition(State);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000662}
663
664ProgramStateRef IteratorChecker::evalAssume(ProgramStateRef State, SVal Cond,
665 bool Assumption) const {
666 // Load recorded comparison and transfer iterator state between sides
667 // according to comparison operator and assumption
668 const auto *SE = Cond.getAsSymExpr();
669 if (!SE)
670 return State;
671
672 auto Opc = getOpcode(SE);
673 if (Opc != BO_EQ && Opc != BO_NE)
674 return State;
675
676 bool Negated = false;
677 const auto *Comp = loadComparison(State, SE);
678 if (!Comp) {
679 // Try negated comparison, which is a SymExpr to 0 integer comparison
680 const auto *SIE = dyn_cast<SymIntExpr>(SE);
681 if (!SIE)
682 return State;
683
684 if (SIE->getRHS() != 0)
685 return State;
686
687 SE = SIE->getLHS();
688 Negated = SIE->getOpcode() == BO_EQ; // Equal to zero means negation
689 Opc = getOpcode(SE);
690 if (Opc != BO_EQ && Opc != BO_NE)
691 return State;
692
693 Comp = loadComparison(State, SE);
694 if (!Comp)
695 return State;
696 }
697
698 return processComparison(State, Comp->getLeft(), Comp->getRight(),
699 (Comp->isEquality() == Assumption) != Negated);
700}
701
702void IteratorChecker::handleComparison(CheckerContext &C, const SVal &RetVal,
703 const SVal &LVal, const SVal &RVal,
704 OverloadedOperatorKind Op) const {
705 // Record the operands and the operator of the comparison for the next
706 // evalAssume, if the result is a symbolic expression. If it is a concrete
707 // value (only one branch is possible), then transfer the state between
708 // the operands according to the operator and the result
709 auto State = C.getState();
710 if (const auto *Condition = RetVal.getAsSymbolicExpression()) {
711 const auto *LPos = getIteratorPosition(State, LVal);
712 const auto *RPos = getIteratorPosition(State, RVal);
713 if (!LPos && !RPos)
714 return;
715 State = saveComparison(State, Condition, LVal, RVal, Op == OO_EqualEqual);
716 C.addTransition(State);
717 } else if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) {
718 if ((State = processComparison(
719 State, getRegionOrSymbol(LVal), getRegionOrSymbol(RVal),
720 (Op == OO_EqualEqual) == (TruthVal->getValue() != 0)))) {
721 C.addTransition(State);
722 } else {
723 C.generateSink(State, C.getPredecessor());
724 }
725 }
726}
727
728void IteratorChecker::verifyDereference(CheckerContext &C,
729 const SVal &Val) const {
730 auto State = C.getState();
731 const auto *Pos = getIteratorPosition(State, Val);
732 if (Pos && isOutOfRange(State, *Pos)) {
Adam Balogh2cfbe932018-08-28 08:41:15 +0000733 auto *N = C.generateNonFatalErrorNode(State);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000734 if (!N)
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000735 return;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000736 reportOutOfRangeBug("Iterator accessed outside of its range.", Val, C, N);
737 }
738}
739
Adam Balogh2cfbe932018-08-28 08:41:15 +0000740void IteratorChecker::verifyAccess(CheckerContext &C, const SVal &Val) const {
741 auto State = C.getState();
742 const auto *Pos = getIteratorPosition(State, Val);
743 if (Pos && !Pos->isValid()) {
744 auto *N = C.generateNonFatalErrorNode(State);
745 if (!N) {
746 return;
747 }
748 reportInvalidatedBug("Invalidated iterator accessed.", Val, C, N);
749 }
750}
751
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000752void IteratorChecker::handleIncrement(CheckerContext &C, const SVal &RetVal,
753 const SVal &Iter, bool Postfix) const {
754 // Increment 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_Add,
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
775void IteratorChecker::handleDecrement(CheckerContext &C, const SVal &RetVal,
776 const SVal &Iter, bool Postfix) const {
777 // Decrement the symbolic expressions which represents the position of the
778 // iterator
779 auto State = C.getState();
780 const auto *Pos = getIteratorPosition(State, Iter);
781 if (Pos) {
782 auto &SymMgr = C.getSymbolManager();
783 auto &BVF = SymMgr.getBasicVals();
784 auto &SVB = C.getSValBuilder();
785 const auto OldOffset = Pos->getOffset();
786 auto NewOffset =
787 SVB.evalBinOp(State, BO_Sub,
788 nonloc::SymbolVal(OldOffset),
789 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
790 SymMgr.getType(OldOffset)).getAsSymbol();
791 auto NewPos = Pos->setTo(NewOffset);
792 State = setIteratorPosition(State, Iter, NewPos);
793 State = setIteratorPosition(State, RetVal, Postfix ? *Pos : NewPos);
794 C.addTransition(State);
795 }
796}
797
798// This function tells the analyzer's engine that symbols produced by our
799// checker, most notably iterator positions, are relatively small.
800// A distance between items in the container should not be very large.
801// By assuming that it is within around 1/8 of the address space,
802// we can help the analyzer perform operations on these symbols
803// without being afraid of integer overflows.
804// FIXME: Should we provide it as an API, so that all checkers could use it?
805static ProgramStateRef assumeNoOverflow(ProgramStateRef State, SymbolRef Sym,
806 long Scale) {
807 SValBuilder &SVB = State->getStateManager().getSValBuilder();
808 BasicValueFactory &BV = SVB.getBasicValueFactory();
809
810 QualType T = Sym->getType();
811 assert(T->isSignedIntegerOrEnumerationType());
812 APSIntType AT = BV.getAPSIntType(T);
813
814 ProgramStateRef NewState = State;
815
816 llvm::APSInt Max = AT.getMaxValue() / AT.getValue(Scale);
817 SVal IsCappedFromAbove =
818 SVB.evalBinOpNN(State, BO_LE, nonloc::SymbolVal(Sym),
819 nonloc::ConcreteInt(Max), SVB.getConditionType());
820 if (auto DV = IsCappedFromAbove.getAs<DefinedSVal>()) {
821 NewState = NewState->assume(*DV, true);
822 if (!NewState)
823 return State;
824 }
825
826 llvm::APSInt Min = -Max;
827 SVal IsCappedFromBelow =
828 SVB.evalBinOpNN(State, BO_GE, nonloc::SymbolVal(Sym),
829 nonloc::ConcreteInt(Min), SVB.getConditionType());
830 if (auto DV = IsCappedFromBelow.getAs<DefinedSVal>()) {
831 NewState = NewState->assume(*DV, true);
832 if (!NewState)
833 return State;
834 }
835
836 return NewState;
837}
838
839void IteratorChecker::handleRandomIncrOrDecr(CheckerContext &C,
840 OverloadedOperatorKind Op,
841 const SVal &RetVal,
842 const SVal &LHS,
843 const SVal &RHS) const {
844 // Increment or decrement the symbolic expressions which represents the
845 // position of the iterator
846 auto State = C.getState();
847 const auto *Pos = getIteratorPosition(State, LHS);
848 if (!Pos)
849 return;
850
851 const auto *value = &RHS;
852 if (auto loc = RHS.getAs<Loc>()) {
853 const auto val = State->getRawSVal(*loc);
854 value = &val;
855 }
856
857 auto &SymMgr = C.getSymbolManager();
858 auto &SVB = C.getSValBuilder();
859 auto BinOp = (Op == OO_Plus || Op == OO_PlusEqual) ? BO_Add : BO_Sub;
860 const auto OldOffset = Pos->getOffset();
861 SymbolRef NewOffset;
862 if (const auto intValue = value->getAs<nonloc::ConcreteInt>()) {
863 // For concrete integers we can calculate the new position
864 NewOffset = SVB.evalBinOp(State, BinOp, nonloc::SymbolVal(OldOffset),
865 *intValue,
866 SymMgr.getType(OldOffset)).getAsSymbol();
867 } else {
868 // For other symbols create a new symbol to keep expressions simple
869 const auto &LCtx = C.getLocationContext();
870 NewOffset = SymMgr.conjureSymbol(nullptr, LCtx, SymMgr.getType(OldOffset),
871 C.blockCount());
872 State = assumeNoOverflow(State, NewOffset, 4);
873 }
874 auto NewPos = Pos->setTo(NewOffset);
875 auto &TgtVal = (Op == OO_PlusEqual || Op == OO_MinusEqual) ? LHS : RetVal;
876 State = setIteratorPosition(State, TgtVal, NewPos);
877 C.addTransition(State);
878}
879
880void IteratorChecker::verifyRandomIncrOrDecr(CheckerContext &C,
881 OverloadedOperatorKind Op,
882 const SVal &RetVal,
883 const SVal &LHS,
884 const SVal &RHS) const {
885 auto State = C.getState();
886
887 // If the iterator is initially inside its range, then the operation is valid
888 const auto *Pos = getIteratorPosition(State, LHS);
889 if (!Pos || !isOutOfRange(State, *Pos))
890 return;
891
892 auto value = RHS;
893 if (auto loc = RHS.getAs<Loc>()) {
894 value = State->getRawSVal(*loc);
895 }
896
897 // Incremention or decremention by 0 is never bug
898 if (isZero(State, value.castAs<NonLoc>()))
899 return;
900
901 auto &SymMgr = C.getSymbolManager();
902 auto &SVB = C.getSValBuilder();
903 auto BinOp = (Op == OO_Plus || Op == OO_PlusEqual) ? BO_Add : BO_Sub;
904 const auto OldOffset = Pos->getOffset();
905 const auto intValue = value.getAs<nonloc::ConcreteInt>();
906 if (!intValue)
907 return;
908
909 auto NewOffset = SVB.evalBinOp(State, BinOp, nonloc::SymbolVal(OldOffset),
910 *intValue,
911 SymMgr.getType(OldOffset)).getAsSymbol();
912 auto NewPos = Pos->setTo(NewOffset);
913
914 // If out of range, the only valid operation is to step into the range
915 if (isOutOfRange(State, NewPos)) {
916 auto *N = C.generateNonFatalErrorNode(State);
917 if (!N)
918 return;
919 reportOutOfRangeBug("Iterator accessed past its end.", LHS, C, N);
920 }
921}
922
Adam Balogh21583b72018-09-10 09:03:22 +0000923void IteratorChecker::verifyMatch(CheckerContext &C, const SVal &Iter1,
924 const SVal &Iter2) const {
925 // Verify match between the containers of two iterators
926 auto State = C.getState();
927 const auto *Pos1 = getIteratorPosition(State, Iter1);
928 const auto *Pos2 = getIteratorPosition(State, Iter2);
929 if (Pos1 && Pos2 && Pos1->getContainer() != Pos2->getContainer()) {
930 auto *N = C.generateNonFatalErrorNode(State);
931 if (!N)
932 return;
933 reportMismatchedBug("Iterators of different containers used where the "
934 "same container is expected.", Iter1, Iter2, C, N);
935 }
936}
937
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000938void IteratorChecker::handleBegin(CheckerContext &C, const Expr *CE,
939 const SVal &RetVal, const SVal &Cont) const {
940 const auto *ContReg = Cont.getAsRegion();
941 if (!ContReg)
942 return;
943
944 while (const auto *CBOR = ContReg->getAs<CXXBaseObjectRegion>()) {
945 ContReg = CBOR->getSuperRegion();
946 }
947
948 // If the container already has a begin symbol then use it. Otherwise first
949 // create a new one.
950 auto State = C.getState();
951 auto BeginSym = getContainerBegin(State, ContReg);
952 if (!BeginSym) {
953 auto &SymMgr = C.getSymbolManager();
954 BeginSym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
955 C.getASTContext().LongTy, C.blockCount());
956 State = assumeNoOverflow(State, BeginSym, 4);
957 State = createContainerBegin(State, ContReg, BeginSym);
958 }
959 State = setIteratorPosition(State, RetVal,
960 IteratorPosition::getPosition(ContReg, BeginSym));
961 C.addTransition(State);
962}
963
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000964void IteratorChecker::handleEnd(CheckerContext &C, const Expr *CE,
965 const SVal &RetVal, const SVal &Cont) const {
966 const auto *ContReg = Cont.getAsRegion();
967 if (!ContReg)
968 return;
969
970 while (const auto *CBOR = ContReg->getAs<CXXBaseObjectRegion>()) {
971 ContReg = CBOR->getSuperRegion();
972 }
973
974 // If the container already has an end symbol then use it. Otherwise first
975 // create a new one.
976 auto State = C.getState();
977 auto EndSym = getContainerEnd(State, ContReg);
978 if (!EndSym) {
979 auto &SymMgr = C.getSymbolManager();
980 EndSym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
981 C.getASTContext().LongTy, C.blockCount());
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000982 State = assumeNoOverflow(State, EndSym, 4);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000983 State = createContainerEnd(State, ContReg, EndSym);
984 }
985 State = setIteratorPosition(State, RetVal,
986 IteratorPosition::getPosition(ContReg, EndSym));
987 C.addTransition(State);
988}
989
990void IteratorChecker::assignToContainer(CheckerContext &C, const Expr *CE,
991 const SVal &RetVal,
992 const MemRegion *Cont) const {
993 while (const auto *CBOR = Cont->getAs<CXXBaseObjectRegion>()) {
994 Cont = CBOR->getSuperRegion();
995 }
996
997 auto State = C.getState();
998 auto &SymMgr = C.getSymbolManager();
999 auto Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
1000 C.getASTContext().LongTy, C.blockCount());
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001001 State = assumeNoOverflow(State, Sym, 4);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001002 State = setIteratorPosition(State, RetVal,
1003 IteratorPosition::getPosition(Cont, Sym));
1004 C.addTransition(State);
1005}
1006
Adam Balogh6b23b1a2018-09-10 09:04:27 +00001007void IteratorChecker::handleAssign(CheckerContext &C, const SVal &Cont,
1008 const Expr *CE, const SVal &OldCont) const {
Adam Balogh2cfbe932018-08-28 08:41:15 +00001009 const auto *ContReg = Cont.getAsRegion();
1010 if (!ContReg)
1011 return;
1012
1013 while (const auto *CBOR = ContReg->getAs<CXXBaseObjectRegion>()) {
1014 ContReg = CBOR->getSuperRegion();
1015 }
1016
1017 // Assignment of a new value to a container always invalidates all its
1018 // iterators
1019 auto State = C.getState();
1020 const auto CData = getContainerData(State, ContReg);
1021 if (CData) {
1022 State = invalidateAllIteratorPositions(State, ContReg);
1023 }
1024
Adam Balogh6b23b1a2018-09-10 09:04:27 +00001025 // In case of move, iterators of the old container (except the past-end
1026 // iterators) remain valid but refer to the new container
1027 if (!OldCont.isUndef()) {
1028 const auto *OldContReg = OldCont.getAsRegion();
1029 if (OldContReg) {
1030 while (const auto *CBOR = OldContReg->getAs<CXXBaseObjectRegion>()) {
1031 OldContReg = CBOR->getSuperRegion();
1032 }
1033 const auto OldCData = getContainerData(State, OldContReg);
1034 if (OldCData) {
1035 if (const auto OldEndSym = OldCData->getEnd()) {
1036 // If we already assigned an "end" symbol to the old conainer, then
1037 // first reassign all iterator positions to the new container which
1038 // are not past the container (thus not greater or equal to the
1039 // current "end" symbol).
1040 State = reassignAllIteratorPositionsUnless(State, OldContReg, ContReg,
1041 OldEndSym, BO_GE);
1042 auto &SymMgr = C.getSymbolManager();
1043 auto &SVB = C.getSValBuilder();
1044 // Then generate and assign a new "end" symbol for the new container.
1045 auto NewEndSym =
1046 SymMgr.conjureSymbol(CE, C.getLocationContext(),
1047 C.getASTContext().LongTy, C.blockCount());
1048 State = assumeNoOverflow(State, NewEndSym, 4);
1049 if (CData) {
1050 State = setContainerData(State, ContReg, CData->newEnd(NewEndSym));
1051 } else {
1052 State = setContainerData(State, ContReg,
1053 ContainerData::fromEnd(NewEndSym));
1054 }
1055 // Finally, replace the old "end" symbol in the already reassigned
1056 // iterator positions with the new "end" symbol.
1057 State = rebaseSymbolInIteratorPositionsIf(
1058 State, SVB, OldEndSym, NewEndSym, OldEndSym, BO_LT);
1059 } else {
1060 // There was no "end" symbol assigned yet to the old container,
1061 // so reassign all iterator positions to the new container.
1062 State = reassignAllIteratorPositions(State, OldContReg, ContReg);
1063 }
1064 if (const auto OldBeginSym = OldCData->getBegin()) {
1065 // If we already assigned a "begin" symbol to the old container, then
1066 // assign it to the new container and remove it from the old one.
1067 if (CData) {
1068 State =
1069 setContainerData(State, ContReg, CData->newBegin(OldBeginSym));
1070 } else {
1071 State = setContainerData(State, ContReg,
1072 ContainerData::fromBegin(OldBeginSym));
1073 }
1074 State =
1075 setContainerData(State, OldContReg, OldCData->newEnd(nullptr));
1076 }
1077 } else {
1078 // There was neither "begin" nor "end" symbol assigned yet to the old
1079 // container, so reassign all iterator positions to the new container.
1080 State = reassignAllIteratorPositions(State, OldContReg, ContReg);
1081 }
1082 }
1083 }
Adam Balogh2cfbe932018-08-28 08:41:15 +00001084 C.addTransition(State);
1085}
1086
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001087void IteratorChecker::reportOutOfRangeBug(const StringRef &Message,
1088 const SVal &Val, CheckerContext &C,
1089 ExplodedNode *ErrNode) const {
1090 auto R = llvm::make_unique<BugReport>(*OutOfRangeBugType, Message, ErrNode);
1091 R->markInteresting(Val);
1092 C.emitReport(std::move(R));
1093}
1094
Adam Balogh21583b72018-09-10 09:03:22 +00001095void IteratorChecker::reportMismatchedBug(const StringRef &Message,
1096 const SVal &Val1, const SVal &Val2,
1097 CheckerContext &C,
1098 ExplodedNode *ErrNode) const {
1099 auto R = llvm::make_unique<BugReport>(*MismatchedBugType, Message, ErrNode);
1100 R->markInteresting(Val1);
1101 R->markInteresting(Val2);
1102 C.emitReport(std::move(R));
1103}
1104
Adam Balogh2cfbe932018-08-28 08:41:15 +00001105void IteratorChecker::reportInvalidatedBug(const StringRef &Message,
1106 const SVal &Val, CheckerContext &C,
1107 ExplodedNode *ErrNode) const {
1108 auto R = llvm::make_unique<BugReport>(*InvalidatedBugType, Message, ErrNode);
1109 R->markInteresting(Val);
1110 C.emitReport(std::move(R));
1111}
1112
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001113namespace {
1114
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001115bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001116bool isGreaterOrEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
1117bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
1118 BinaryOperator::Opcode Opc);
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001119bool compare(ProgramStateRef State, NonLoc NL1, NonLoc NL2,
1120 BinaryOperator::Opcode Opc);
Adam Balogh6b23b1a2018-09-10 09:04:27 +00001121SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB, SymbolRef Expr,
1122 SymbolRef OldSym, SymbolRef NewSym);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001123
1124bool isIteratorType(const QualType &Type) {
1125 if (Type->isPointerType())
1126 return true;
1127
1128 const auto *CRD = Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
1129 return isIterator(CRD);
1130}
1131
1132bool isIterator(const CXXRecordDecl *CRD) {
1133 if (!CRD)
1134 return false;
1135
1136 const auto Name = CRD->getName();
1137 if (!(Name.endswith_lower("iterator") || Name.endswith_lower("iter") ||
1138 Name.endswith_lower("it")))
1139 return false;
1140
1141 bool HasCopyCtor = false, HasCopyAssign = true, HasDtor = false,
1142 HasPreIncrOp = false, HasPostIncrOp = false, HasDerefOp = false;
1143 for (const auto *Method : CRD->methods()) {
1144 if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(Method)) {
1145 if (Ctor->isCopyConstructor()) {
1146 HasCopyCtor = !Ctor->isDeleted() && Ctor->getAccess() == AS_public;
1147 }
1148 continue;
1149 }
1150 if (const auto *Dtor = dyn_cast<CXXDestructorDecl>(Method)) {
1151 HasDtor = !Dtor->isDeleted() && Dtor->getAccess() == AS_public;
1152 continue;
1153 }
1154 if (Method->isCopyAssignmentOperator()) {
1155 HasCopyAssign = !Method->isDeleted() && Method->getAccess() == AS_public;
1156 continue;
1157 }
1158 if (!Method->isOverloadedOperator())
1159 continue;
1160 const auto OPK = Method->getOverloadedOperator();
1161 if (OPK == OO_PlusPlus) {
1162 HasPreIncrOp = HasPreIncrOp || (Method->getNumParams() == 0);
1163 HasPostIncrOp = HasPostIncrOp || (Method->getNumParams() == 1);
1164 continue;
1165 }
1166 if (OPK == OO_Star) {
1167 HasDerefOp = (Method->getNumParams() == 0);
1168 continue;
1169 }
1170 }
1171
1172 return HasCopyCtor && HasCopyAssign && HasDtor && HasPreIncrOp &&
1173 HasPostIncrOp && HasDerefOp;
1174}
1175
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001176bool isBeginCall(const FunctionDecl *Func) {
1177 const auto *IdInfo = Func->getIdentifier();
1178 if (!IdInfo)
1179 return false;
1180 return IdInfo->getName().endswith_lower("begin");
1181}
1182
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001183bool isEndCall(const FunctionDecl *Func) {
1184 const auto *IdInfo = Func->getIdentifier();
1185 if (!IdInfo)
1186 return false;
1187 return IdInfo->getName().endswith_lower("end");
1188}
1189
Adam Balogh2cfbe932018-08-28 08:41:15 +00001190bool isAssignmentOperator(OverloadedOperatorKind OK) { return OK == OO_Equal; }
1191
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001192bool isSimpleComparisonOperator(OverloadedOperatorKind OK) {
1193 return OK == OO_EqualEqual || OK == OO_ExclaimEqual;
1194}
1195
Adam Balogh2cfbe932018-08-28 08:41:15 +00001196bool isAccessOperator(OverloadedOperatorKind OK) {
1197 return isDereferenceOperator(OK) || isIncrementOperator(OK) ||
1198 isDecrementOperator(OK) || isRandomIncrOrDecrOperator(OK);
1199}
1200
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001201bool isDereferenceOperator(OverloadedOperatorKind OK) {
1202 return OK == OO_Star || OK == OO_Arrow || OK == OO_ArrowStar ||
1203 OK == OO_Subscript;
1204}
1205
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001206bool isIncrementOperator(OverloadedOperatorKind OK) {
1207 return OK == OO_PlusPlus;
1208}
1209
1210bool isDecrementOperator(OverloadedOperatorKind OK) {
1211 return OK == OO_MinusMinus;
1212}
1213
1214bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK) {
1215 return OK == OO_Plus || OK == OO_PlusEqual || OK == OO_Minus ||
1216 OK == OO_MinusEqual;
1217}
1218
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001219BinaryOperator::Opcode getOpcode(const SymExpr *SE) {
1220 if (const auto *BSE = dyn_cast<BinarySymExpr>(SE)) {
1221 return BSE->getOpcode();
1222 } else if (const auto *SC = dyn_cast<SymbolConjured>(SE)) {
Henry Wong073d5f02018-03-20 09:27:02 +00001223 const auto *COE = dyn_cast_or_null<CXXOperatorCallExpr>(SC->getStmt());
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001224 if (!COE)
1225 return BO_Comma; // Extremal value, neither EQ nor NE
1226 if (COE->getOperator() == OO_EqualEqual) {
1227 return BO_EQ;
1228 } else if (COE->getOperator() == OO_ExclaimEqual) {
1229 return BO_NE;
1230 }
1231 return BO_Comma; // Extremal value, neither EQ nor NE
1232 }
1233 return BO_Comma; // Extremal value, neither EQ nor NE
1234}
1235
1236const RegionOrSymbol getRegionOrSymbol(const SVal &Val) {
1237 if (const auto Reg = Val.getAsRegion()) {
1238 return Reg;
1239 } else if (const auto Sym = Val.getAsSymbol()) {
1240 return Sym;
1241 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
1242 return LCVal->getRegion();
1243 }
1244 return RegionOrSymbol();
1245}
1246
1247const ProgramStateRef processComparison(ProgramStateRef State,
1248 RegionOrSymbol LVal,
1249 RegionOrSymbol RVal, bool Equal) {
1250 const auto *LPos = getIteratorPosition(State, LVal);
1251 const auto *RPos = getIteratorPosition(State, RVal);
1252 if (LPos && !RPos) {
1253 State = adjustIteratorPosition(State, RVal, *LPos, Equal);
1254 } else if (!LPos && RPos) {
1255 State = adjustIteratorPosition(State, LVal, *RPos, Equal);
1256 } else if (LPos && RPos) {
1257 State = relateIteratorPositions(State, *LPos, *RPos, Equal);
1258 }
1259 return State;
1260}
1261
1262const ProgramStateRef saveComparison(ProgramStateRef State,
1263 const SymExpr *Condition, const SVal &LVal,
1264 const SVal &RVal, bool Eq) {
1265 const auto Left = getRegionOrSymbol(LVal);
1266 const auto Right = getRegionOrSymbol(RVal);
1267 if (!Left || !Right)
1268 return State;
1269 return State->set<IteratorComparisonMap>(Condition,
1270 IteratorComparison(Left, Right, Eq));
1271}
1272
1273const IteratorComparison *loadComparison(ProgramStateRef State,
1274 const SymExpr *Condition) {
1275 return State->get<IteratorComparisonMap>(Condition);
1276}
1277
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001278SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont) {
1279 const auto *CDataPtr = getContainerData(State, Cont);
1280 if (!CDataPtr)
1281 return nullptr;
1282
1283 return CDataPtr->getBegin();
1284}
1285
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001286SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont) {
1287 const auto *CDataPtr = getContainerData(State, Cont);
1288 if (!CDataPtr)
1289 return nullptr;
1290
1291 return CDataPtr->getEnd();
1292}
1293
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001294ProgramStateRef createContainerBegin(ProgramStateRef State,
1295 const MemRegion *Cont,
1296 const SymbolRef Sym) {
1297 // Only create if it does not exist
1298 const auto *CDataPtr = getContainerData(State, Cont);
1299 if (CDataPtr) {
1300 if (CDataPtr->getBegin()) {
1301 return State;
1302 }
1303 const auto CData = CDataPtr->newBegin(Sym);
1304 return setContainerData(State, Cont, CData);
1305 }
1306 const auto CData = ContainerData::fromBegin(Sym);
1307 return setContainerData(State, Cont, CData);
1308}
1309
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001310ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
1311 const SymbolRef Sym) {
1312 // Only create if it does not exist
1313 const auto *CDataPtr = getContainerData(State, Cont);
1314 if (CDataPtr) {
1315 if (CDataPtr->getEnd()) {
1316 return State;
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001317 }
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001318 const auto CData = CDataPtr->newEnd(Sym);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001319 return setContainerData(State, Cont, CData);
1320 }
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001321 const auto CData = ContainerData::fromEnd(Sym);
1322 return setContainerData(State, Cont, CData);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001323}
1324
1325const ContainerData *getContainerData(ProgramStateRef State,
1326 const MemRegion *Cont) {
1327 return State->get<ContainerMap>(Cont);
1328}
1329
1330ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
1331 const ContainerData &CData) {
1332 return State->set<ContainerMap>(Cont, CData);
1333}
1334
1335const IteratorPosition *getIteratorPosition(ProgramStateRef State,
1336 const SVal &Val) {
1337 if (const auto Reg = Val.getAsRegion()) {
1338 return State->get<IteratorRegionMap>(Reg);
1339 } else if (const auto Sym = Val.getAsSymbol()) {
1340 return State->get<IteratorSymbolMap>(Sym);
1341 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
1342 return State->get<IteratorRegionMap>(LCVal->getRegion());
1343 }
1344 return nullptr;
1345}
1346
1347const IteratorPosition *getIteratorPosition(ProgramStateRef State,
1348 RegionOrSymbol RegOrSym) {
1349 if (RegOrSym.is<const MemRegion *>()) {
1350 return State->get<IteratorRegionMap>(RegOrSym.get<const MemRegion *>());
1351 } else if (RegOrSym.is<SymbolRef>()) {
1352 return State->get<IteratorSymbolMap>(RegOrSym.get<SymbolRef>());
1353 }
1354 return nullptr;
1355}
1356
1357ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
1358 const IteratorPosition &Pos) {
1359 if (const auto Reg = Val.getAsRegion()) {
1360 return State->set<IteratorRegionMap>(Reg, Pos);
1361 } else if (const auto Sym = Val.getAsSymbol()) {
1362 return State->set<IteratorSymbolMap>(Sym, Pos);
1363 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
1364 return State->set<IteratorRegionMap>(LCVal->getRegion(), Pos);
1365 }
1366 return nullptr;
1367}
1368
1369ProgramStateRef setIteratorPosition(ProgramStateRef State,
1370 RegionOrSymbol RegOrSym,
1371 const IteratorPosition &Pos) {
1372 if (RegOrSym.is<const MemRegion *>()) {
1373 return State->set<IteratorRegionMap>(RegOrSym.get<const MemRegion *>(),
1374 Pos);
1375 } else if (RegOrSym.is<SymbolRef>()) {
1376 return State->set<IteratorSymbolMap>(RegOrSym.get<SymbolRef>(), Pos);
1377 }
1378 return nullptr;
1379}
1380
1381ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val) {
1382 if (const auto Reg = Val.getAsRegion()) {
1383 return State->remove<IteratorRegionMap>(Reg);
1384 } else if (const auto Sym = Val.getAsSymbol()) {
1385 return State->remove<IteratorSymbolMap>(Sym);
1386 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
1387 return State->remove<IteratorRegionMap>(LCVal->getRegion());
1388 }
1389 return nullptr;
1390}
1391
1392ProgramStateRef adjustIteratorPosition(ProgramStateRef State,
1393 RegionOrSymbol RegOrSym,
1394 const IteratorPosition &Pos,
1395 bool Equal) {
1396 if (Equal) {
1397 return setIteratorPosition(State, RegOrSym, Pos);
1398 } else {
1399 return State;
1400 }
1401}
1402
1403ProgramStateRef relateIteratorPositions(ProgramStateRef State,
1404 const IteratorPosition &Pos1,
1405 const IteratorPosition &Pos2,
1406 bool Equal) {
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001407 auto &SVB = State->getStateManager().getSValBuilder();
Adam Balogh0a7592b2018-07-16 09:27:27 +00001408
1409 // FIXME: This code should be reworked as follows:
1410 // 1. Subtract the operands using evalBinOp().
1411 // 2. Assume that the result doesn't overflow.
1412 // 3. Compare the result to 0.
1413 // 4. Assume the result of the comparison.
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001414 const auto comparison =
1415 SVB.evalBinOp(State, BO_EQ, nonloc::SymbolVal(Pos1.getOffset()),
Adam Balogh0a7592b2018-07-16 09:27:27 +00001416 nonloc::SymbolVal(Pos2.getOffset()),
1417 SVB.getConditionType());
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001418
Adam Balogh0a7592b2018-07-16 09:27:27 +00001419 assert(comparison.getAs<DefinedSVal>() &&
1420 "Symbol comparison must be a `DefinedSVal`");
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001421
Adam Balogh0a7592b2018-07-16 09:27:27 +00001422 auto NewState = State->assume(comparison.castAs<DefinedSVal>(), Equal);
1423 if (const auto CompSym = comparison.getAsSymbol()) {
1424 assert(isa<SymIntExpr>(CompSym) &&
1425 "Symbol comparison must be a `SymIntExpr`");
1426 assert(BinaryOperator::isComparisonOp(
1427 cast<SymIntExpr>(CompSym)->getOpcode()) &&
1428 "Symbol comparison must be a comparison");
1429 return assumeNoOverflow(NewState, cast<SymIntExpr>(CompSym)->getLHS(), 2);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001430 }
1431
Adam Balogh0a7592b2018-07-16 09:27:27 +00001432 return NewState;
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001433}
1434
Adam Balogha6921202018-07-30 08:52:21 +00001435bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont) {
1436 auto RegionMap = State->get<IteratorRegionMap>();
1437 for (const auto Reg : RegionMap) {
1438 if (Reg.second.getContainer() == Cont)
1439 return true;
1440 }
1441
1442 auto SymbolMap = State->get<IteratorSymbolMap>();
1443 for (const auto Sym : SymbolMap) {
1444 if (Sym.second.getContainer() == Cont)
1445 return true;
1446 }
1447
1448 return false;
1449}
1450
Adam Balogh21583b72018-09-10 09:03:22 +00001451bool isBoundThroughLazyCompoundVal(const Environment &Env,
1452 const MemRegion *Reg) {
1453 for (const auto Binding: Env) {
1454 if (const auto LCVal = Binding.second.getAs<nonloc::LazyCompoundVal>()) {
1455 if (LCVal->getRegion() == Reg)
1456 return true;
1457 }
1458 }
1459
1460 return false;
1461}
1462
Adam Balogh2cfbe932018-08-28 08:41:15 +00001463template <typename Condition, typename Process>
1464ProgramStateRef processIteratorPositions(ProgramStateRef State, Condition Cond,
1465 Process Proc) {
1466 auto &RegionMapFactory = State->get_context<IteratorRegionMap>();
1467 auto RegionMap = State->get<IteratorRegionMap>();
1468 bool Changed = false;
1469 for (const auto Reg : RegionMap) {
1470 if (Cond(Reg.second)) {
1471 RegionMap = RegionMapFactory.add(RegionMap, Reg.first, Proc(Reg.second));
1472 Changed = true;
1473 }
1474 }
1475
1476 if (Changed)
1477 State = State->set<IteratorRegionMap>(RegionMap);
1478
1479 auto &SymbolMapFactory = State->get_context<IteratorSymbolMap>();
1480 auto SymbolMap = State->get<IteratorSymbolMap>();
1481 Changed = false;
1482 for (const auto Sym : SymbolMap) {
1483 if (Cond(Sym.second)) {
1484 SymbolMap = SymbolMapFactory.add(SymbolMap, Sym.first, Proc(Sym.second));
1485 Changed = true;
1486 }
1487 }
1488
1489 if (Changed)
1490 State = State->set<IteratorSymbolMap>(SymbolMap);
1491
1492 return State;
1493}
1494
1495ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State,
1496 const MemRegion *Cont) {
1497 auto MatchCont = [&](const IteratorPosition &Pos) {
1498 return Pos.getContainer() == Cont;
1499 };
1500 auto Invalidate = [&](const IteratorPosition &Pos) {
1501 return Pos.invalidate();
1502 };
1503 return processIteratorPositions(State, MatchCont, Invalidate);
1504}
1505
Adam Balogh6b23b1a2018-09-10 09:04:27 +00001506ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State,
1507 const MemRegion *Cont,
1508 const MemRegion *NewCont) {
1509 auto MatchCont = [&](const IteratorPosition &Pos) {
1510 return Pos.getContainer() == Cont;
1511 };
1512 auto ReAssign = [&](const IteratorPosition &Pos) {
1513 return Pos.reAssign(NewCont);
1514 };
1515 return processIteratorPositions(State, MatchCont, ReAssign);
1516}
1517
1518ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State,
1519 const MemRegion *Cont,
1520 const MemRegion *NewCont,
1521 SymbolRef Offset,
1522 BinaryOperator::Opcode Opc) {
1523 auto MatchContAndCompare = [&](const IteratorPosition &Pos) {
1524 return Pos.getContainer() == Cont &&
1525 !compare(State, Pos.getOffset(), Offset, Opc);
1526 };
1527 auto ReAssign = [&](const IteratorPosition &Pos) {
1528 return Pos.reAssign(NewCont);
1529 };
1530 return processIteratorPositions(State, MatchContAndCompare, ReAssign);
1531}
1532
1533// This function rebases symbolic expression `OldSym + Int` to `NewSym + Int`,
1534// `OldSym - Int` to `NewSym - Int` and `OldSym` to `NewSym` in any iterator
1535// position offsets where `CondSym` is true.
1536ProgramStateRef rebaseSymbolInIteratorPositionsIf(
1537 ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym,
1538 SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc) {
1539 auto LessThanEnd = [&](const IteratorPosition &Pos) {
1540 return compare(State, Pos.getOffset(), CondSym, Opc);
1541 };
1542 auto RebaseSymbol = [&](const IteratorPosition &Pos) {
1543 return Pos.setTo(rebaseSymbol(State, SVB, Pos.getOffset(), OldSym,
1544 NewSym));
1545 };
1546 return processIteratorPositions(State, LessThanEnd, RebaseSymbol);
1547}
1548
1549// This function rebases symbolic expression `OldExpr + Int` to `NewExpr + Int`,
1550// `OldExpr - Int` to `NewExpr - Int` and `OldExpr` to `NewExpr` in expression
1551// `OrigExpr`.
1552SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB,
1553 SymbolRef OrigExpr, SymbolRef OldExpr,
1554 SymbolRef NewSym) {
1555 auto &SymMgr = SVB.getSymbolManager();
1556 auto Diff = SVB.evalBinOpNN(State, BO_Sub, nonloc::SymbolVal(OrigExpr),
1557 nonloc::SymbolVal(OldExpr),
1558 SymMgr.getType(OrigExpr));
1559
1560 const auto DiffInt = Diff.getAs<nonloc::ConcreteInt>();
1561 if (!DiffInt)
1562 return OrigExpr;
1563
1564 return SVB.evalBinOpNN(State, BO_Add, *DiffInt, nonloc::SymbolVal(NewSym),
1565 SymMgr.getType(OrigExpr)).getAsSymbol();
1566}
1567
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001568bool isZero(ProgramStateRef State, const NonLoc &Val) {
1569 auto &BVF = State->getBasicVals();
1570 return compare(State, Val,
1571 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))),
1572 BO_EQ);
1573}
1574
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001575bool isOutOfRange(ProgramStateRef State, const IteratorPosition &Pos) {
1576 const auto *Cont = Pos.getContainer();
1577 const auto *CData = getContainerData(State, Cont);
1578 if (!CData)
1579 return false;
1580
1581 // Out of range means less than the begin symbol or greater or equal to the
1582 // end symbol.
1583
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001584 const auto Beg = CData->getBegin();
1585 if (Beg) {
1586 if (isLess(State, Pos.getOffset(), Beg)) {
1587 return true;
1588 }
1589 }
1590
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001591 const auto End = CData->getEnd();
1592 if (End) {
1593 if (isGreaterOrEqual(State, Pos.getOffset(), End)) {
1594 return true;
1595 }
1596 }
1597
1598 return false;
1599}
1600
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001601bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
1602 return compare(State, Sym1, Sym2, BO_LT);
1603}
1604
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001605bool isGreaterOrEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
1606 return compare(State, Sym1, Sym2, BO_GE);
1607}
1608
1609bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
1610 BinaryOperator::Opcode Opc) {
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001611 return compare(State, nonloc::SymbolVal(Sym1), nonloc::SymbolVal(Sym2), Opc);
1612}
1613
1614bool compare(ProgramStateRef State, NonLoc NL1, NonLoc NL2,
1615 BinaryOperator::Opcode Opc) {
1616 auto &SVB = State->getStateManager().getSValBuilder();
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001617
1618 const auto comparison =
Adam Balogh0a7592b2018-07-16 09:27:27 +00001619 SVB.evalBinOp(State, Opc, NL1, NL2, SVB.getConditionType());
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001620
Adam Balogh0a7592b2018-07-16 09:27:27 +00001621 assert(comparison.getAs<DefinedSVal>() &&
1622 "Symbol comparison must be a `DefinedSVal`");
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001623
Adam Balogh0a7592b2018-07-16 09:27:27 +00001624 return !State->assume(comparison.castAs<DefinedSVal>(), false);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001625}
1626
1627} // namespace
1628
1629#define REGISTER_CHECKER(name) \
1630 void ento::register##name(CheckerManager &Mgr) { \
1631 auto *checker = Mgr.registerChecker<IteratorChecker>(); \
1632 checker->ChecksEnabled[IteratorChecker::CK_##name] = true; \
1633 checker->CheckNames[IteratorChecker::CK_##name] = \
1634 Mgr.getCurrentCheckName(); \
1635 }
1636
1637REGISTER_CHECKER(IteratorRangeChecker)
Adam Balogh21583b72018-09-10 09:03:22 +00001638REGISTER_CHECKER(MismatchedIteratorChecker)
Adam Balogh2cfbe932018-08-28 08:41:15 +00001639REGISTER_CHECKER(InvalidatedIteratorChecker)