blob: e221252e5921533cb6fd8b543aac6c701205b603 [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"
Adam Balogh9a48ba62018-09-10 09:06:31 +000075#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h"
Artem Dergachev8fa639e2017-05-29 15:03:20 +000076
Adam Balogh2cfbe932018-08-28 08:41:15 +000077#include <utility>
78
Artem Dergachev8fa639e2017-05-29 15:03:20 +000079using namespace clang;
80using namespace ento;
81
82namespace {
83
84// Abstract position of an iterator. This helps to handle all three kinds
85// of operators in a common way by using a symbolic position.
86struct IteratorPosition {
87private:
88
89 // Container the iterator belongs to
90 const MemRegion *Cont;
91
Adam Balogh2cfbe932018-08-28 08:41:15 +000092 // Whether iterator is valid
93 const bool Valid;
94
Artem Dergachev8fa639e2017-05-29 15:03:20 +000095 // Abstract offset
Adam Baloghb03ed5e2018-06-28 10:58:53 +000096 const SymbolRef Offset;
Artem Dergachev8fa639e2017-05-29 15:03:20 +000097
Adam Balogh2cfbe932018-08-28 08:41:15 +000098 IteratorPosition(const MemRegion *C, bool V, SymbolRef Of)
99 : Cont(C), Valid(V), Offset(Of) {}
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000100
101public:
102 const MemRegion *getContainer() const { return Cont; }
Adam Balogh2cfbe932018-08-28 08:41:15 +0000103 bool isValid() const { return Valid; }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000104 SymbolRef getOffset() const { return Offset; }
105
Adam Balogh2cfbe932018-08-28 08:41:15 +0000106 IteratorPosition invalidate() const {
107 return IteratorPosition(Cont, false, Offset);
108 }
109
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000110 static IteratorPosition getPosition(const MemRegion *C, SymbolRef Of) {
Adam Balogh2cfbe932018-08-28 08:41:15 +0000111 return IteratorPosition(C, true, Of);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000112 }
113
114 IteratorPosition setTo(SymbolRef NewOf) const {
Adam Balogh2cfbe932018-08-28 08:41:15 +0000115 return IteratorPosition(Cont, Valid, NewOf);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000116 }
117
Adam Balogh6b23b1a2018-09-10 09:04:27 +0000118 IteratorPosition reAssign(const MemRegion *NewCont) const {
119 return IteratorPosition(NewCont, Valid, Offset);
120 }
121
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000122 bool operator==(const IteratorPosition &X) const {
Adam Balogh2cfbe932018-08-28 08:41:15 +0000123 return Cont == X.Cont && Valid == X.Valid && Offset == X.Offset;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000124 }
125
126 bool operator!=(const IteratorPosition &X) const {
Adam Balogh2cfbe932018-08-28 08:41:15 +0000127 return Cont != X.Cont || Valid != X.Valid || Offset != X.Offset;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000128 }
129
130 void Profile(llvm::FoldingSetNodeID &ID) const {
131 ID.AddPointer(Cont);
Adam Balogh2cfbe932018-08-28 08:41:15 +0000132 ID.AddInteger(Valid);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000133 ID.Add(Offset);
134 }
135};
136
137typedef llvm::PointerUnion<const MemRegion *, SymbolRef> RegionOrSymbol;
138
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000139// Structure to record the symbolic begin and end position of a container
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000140struct ContainerData {
141private:
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000142 const SymbolRef Begin, End;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000143
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000144 ContainerData(SymbolRef B, SymbolRef E) : Begin(B), End(E) {}
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000145
146public:
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000147 static ContainerData fromBegin(SymbolRef B) {
148 return ContainerData(B, nullptr);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000149 }
150
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000151 static ContainerData fromEnd(SymbolRef E) {
152 return ContainerData(nullptr, E);
153 }
154
155 SymbolRef getBegin() const { return Begin; }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000156 SymbolRef getEnd() const { return End; }
157
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000158 ContainerData newBegin(SymbolRef B) const { return ContainerData(B, End); }
159
160 ContainerData newEnd(SymbolRef E) const { return ContainerData(Begin, E); }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000161
162 bool operator==(const ContainerData &X) const {
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000163 return Begin == X.Begin && End == X.End;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000164 }
165
166 bool operator!=(const ContainerData &X) const {
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000167 return Begin != X.Begin || End != X.End;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000168 }
169
170 void Profile(llvm::FoldingSetNodeID &ID) const {
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000171 ID.Add(Begin);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000172 ID.Add(End);
173 }
174};
175
176// Structure fo recording iterator comparisons. We needed to retrieve the
177// original comparison expression in assumptions.
178struct IteratorComparison {
179private:
180 RegionOrSymbol Left, Right;
181 bool Equality;
182
183public:
184 IteratorComparison(RegionOrSymbol L, RegionOrSymbol R, bool Eq)
185 : Left(L), Right(R), Equality(Eq) {}
186
187 RegionOrSymbol getLeft() const { return Left; }
188 RegionOrSymbol getRight() const { return Right; }
189 bool isEquality() const { return Equality; }
190 bool operator==(const IteratorComparison &X) const {
191 return Left == X.Left && Right == X.Right && Equality == X.Equality;
192 }
193 bool operator!=(const IteratorComparison &X) const {
194 return Left != X.Left || Right != X.Right || Equality != X.Equality;
195 }
196 void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(Equality); }
197};
198
199class IteratorChecker
200 : public Checker<check::PreCall, check::PostCall,
Adam Balogh2e7cb342018-09-10 09:07:47 +0000201 check::PostStmt<MaterializeTemporaryExpr>, check::Bind,
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000202 check::LiveSymbols, check::DeadSymbols,
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000203 eval::Assume> {
204
205 std::unique_ptr<BugType> OutOfRangeBugType;
Adam Balogh21583b72018-09-10 09:03:22 +0000206 std::unique_ptr<BugType> MismatchedBugType;
Adam Balogh2cfbe932018-08-28 08:41:15 +0000207 std::unique_ptr<BugType> InvalidatedBugType;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000208
209 void handleComparison(CheckerContext &C, const SVal &RetVal, const SVal &LVal,
210 const SVal &RVal, OverloadedOperatorKind Op) const;
Adam Balogh2cfbe932018-08-28 08:41:15 +0000211 void verifyAccess(CheckerContext &C, const SVal &Val) const;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000212 void verifyDereference(CheckerContext &C, const SVal &Val) const;
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000213 void handleIncrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
214 bool Postfix) const;
215 void handleDecrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
216 bool Postfix) const;
217 void handleRandomIncrOrDecr(CheckerContext &C, OverloadedOperatorKind Op,
218 const SVal &RetVal, const SVal &LHS,
219 const SVal &RHS) const;
220 void handleBegin(CheckerContext &C, const Expr *CE, const SVal &RetVal,
221 const SVal &Cont) const;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000222 void handleEnd(CheckerContext &C, const Expr *CE, const SVal &RetVal,
223 const SVal &Cont) const;
224 void assignToContainer(CheckerContext &C, const Expr *CE, const SVal &RetVal,
225 const MemRegion *Cont) const;
Adam Balogh6b23b1a2018-09-10 09:04:27 +0000226 void handleAssign(CheckerContext &C, const SVal &Cont,
227 const Expr *CE = nullptr,
228 const SVal &OldCont = UndefinedVal()) const;
Adam Balogh2e7cb342018-09-10 09:07:47 +0000229 void handleClear(CheckerContext &C, const SVal &Cont) const;
Adam Balogh9a48ba62018-09-10 09:06:31 +0000230 void handlePushBack(CheckerContext &C, const SVal &Cont) const;
231 void handlePopBack(CheckerContext &C, const SVal &Cont) const;
232 void handlePushFront(CheckerContext &C, const SVal &Cont) const;
233 void handlePopFront(CheckerContext &C, const SVal &Cont) const;
Adam Balogh2e7cb342018-09-10 09:07:47 +0000234 void handleInsert(CheckerContext &C, const SVal &Iter) const;
235 void handleErase(CheckerContext &C, const SVal &Iter) const;
236 void handleErase(CheckerContext &C, const SVal &Iter1,
237 const SVal &Iter2) const;
238 void handleEraseAfter(CheckerContext &C, const SVal &Iter) const;
239 void handleEraseAfter(CheckerContext &C, const SVal &Iter1,
240 const SVal &Iter2) const;
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000241 void verifyRandomIncrOrDecr(CheckerContext &C, OverloadedOperatorKind Op,
242 const SVal &RetVal, const SVal &LHS,
243 const SVal &RHS) const;
Adam Balogh2e7cb342018-09-10 09:07:47 +0000244 void verifyMatch(CheckerContext &C, const SVal &Iter,
245 const MemRegion *Cont) const;
Adam Balogh21583b72018-09-10 09:03:22 +0000246 void verifyMatch(CheckerContext &C, const SVal &Iter1,
247 const SVal &Iter2) const;
248
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000249 void reportOutOfRangeBug(const StringRef &Message, const SVal &Val,
250 CheckerContext &C, ExplodedNode *ErrNode) const;
Adam Balogh21583b72018-09-10 09:03:22 +0000251 void reportMismatchedBug(const StringRef &Message, const SVal &Val1,
252 const SVal &Val2, CheckerContext &C,
253 ExplodedNode *ErrNode) const;
Adam Balogh3659f7a2018-09-10 09:05:31 +0000254 void reportMismatchedBug(const StringRef &Message, const SVal &Val,
255 const MemRegion *Reg, CheckerContext &C,
256 ExplodedNode *ErrNode) const;
Adam Balogh2cfbe932018-08-28 08:41:15 +0000257 void reportInvalidatedBug(const StringRef &Message, const SVal &Val,
258 CheckerContext &C, ExplodedNode *ErrNode) const;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000259
260public:
261 IteratorChecker();
262
263 enum CheckKind {
264 CK_IteratorRangeChecker,
Adam Balogh21583b72018-09-10 09:03:22 +0000265 CK_MismatchedIteratorChecker,
Adam Balogh2cfbe932018-08-28 08:41:15 +0000266 CK_InvalidatedIteratorChecker,
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000267 CK_NumCheckKinds
268 };
269
270 DefaultBool ChecksEnabled[CK_NumCheckKinds];
271 CheckName CheckNames[CK_NumCheckKinds];
272
273 void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
274 void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
Adam Balogh2e7cb342018-09-10 09:07:47 +0000275 void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const;
276 void checkPostStmt(const CXXConstructExpr *CCE, CheckerContext &C) const;
277 void checkPostStmt(const DeclStmt *DS, CheckerContext &C) const;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000278 void checkPostStmt(const MaterializeTemporaryExpr *MTE,
279 CheckerContext &C) const;
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000280 void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000281 void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
282 ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond,
283 bool Assumption) const;
284};
285} // namespace
286
287REGISTER_MAP_WITH_PROGRAMSTATE(IteratorSymbolMap, SymbolRef, IteratorPosition)
288REGISTER_MAP_WITH_PROGRAMSTATE(IteratorRegionMap, const MemRegion *,
289 IteratorPosition)
290
291REGISTER_MAP_WITH_PROGRAMSTATE(ContainerMap, const MemRegion *, ContainerData)
292
293REGISTER_MAP_WITH_PROGRAMSTATE(IteratorComparisonMap, const SymExpr *,
294 IteratorComparison)
295
296namespace {
297
298bool isIteratorType(const QualType &Type);
299bool isIterator(const CXXRecordDecl *CRD);
Adam Balogh3659f7a2018-09-10 09:05:31 +0000300bool isComparisonOperator(OverloadedOperatorKind OK);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000301bool isBeginCall(const FunctionDecl *Func);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000302bool isEndCall(const FunctionDecl *Func);
Adam Balogh2e7cb342018-09-10 09:07:47 +0000303bool isAssignCall(const FunctionDecl *Func);
304bool isClearCall(const FunctionDecl *Func);
Adam Balogh9a48ba62018-09-10 09:06:31 +0000305bool isPushBackCall(const FunctionDecl *Func);
306bool isEmplaceBackCall(const FunctionDecl *Func);
307bool isPopBackCall(const FunctionDecl *Func);
308bool isPushFrontCall(const FunctionDecl *Func);
309bool isEmplaceFrontCall(const FunctionDecl *Func);
310bool isPopFrontCall(const FunctionDecl *Func);
Adam Balogh2e7cb342018-09-10 09:07:47 +0000311bool isInsertCall(const FunctionDecl *Func);
312bool isEraseCall(const FunctionDecl *Func);
313bool isEraseAfterCall(const FunctionDecl *Func);
314bool isEmplaceCall(const FunctionDecl *Func);
Adam Balogh2cfbe932018-08-28 08:41:15 +0000315bool isAssignmentOperator(OverloadedOperatorKind OK);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000316bool isSimpleComparisonOperator(OverloadedOperatorKind OK);
Adam Balogh2cfbe932018-08-28 08:41:15 +0000317bool isAccessOperator(OverloadedOperatorKind OK);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000318bool isDereferenceOperator(OverloadedOperatorKind OK);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000319bool isIncrementOperator(OverloadedOperatorKind OK);
320bool isDecrementOperator(OverloadedOperatorKind OK);
321bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK);
Adam Balogh9a48ba62018-09-10 09:06:31 +0000322bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg);
323bool frontModifiable(ProgramStateRef State, const MemRegion *Reg);
324bool backModifiable(ProgramStateRef State, const MemRegion *Reg);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000325BinaryOperator::Opcode getOpcode(const SymExpr *SE);
326const RegionOrSymbol getRegionOrSymbol(const SVal &Val);
327const ProgramStateRef processComparison(ProgramStateRef State,
328 RegionOrSymbol LVal,
329 RegionOrSymbol RVal, bool Equal);
330const ProgramStateRef saveComparison(ProgramStateRef State,
331 const SymExpr *Condition, const SVal &LVal,
332 const SVal &RVal, bool Eq);
333const IteratorComparison *loadComparison(ProgramStateRef State,
334 const SymExpr *Condition);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000335SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000336SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000337ProgramStateRef createContainerBegin(ProgramStateRef State,
338 const MemRegion *Cont,
339 const SymbolRef Sym);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000340ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
341 const SymbolRef Sym);
342const IteratorPosition *getIteratorPosition(ProgramStateRef State,
343 const SVal &Val);
344const IteratorPosition *getIteratorPosition(ProgramStateRef State,
345 RegionOrSymbol RegOrSym);
346ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
347 const IteratorPosition &Pos);
348ProgramStateRef setIteratorPosition(ProgramStateRef State,
349 RegionOrSymbol RegOrSym,
350 const IteratorPosition &Pos);
351ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val);
352ProgramStateRef adjustIteratorPosition(ProgramStateRef State,
353 RegionOrSymbol RegOrSym,
354 const IteratorPosition &Pos, bool Equal);
355ProgramStateRef relateIteratorPositions(ProgramStateRef State,
356 const IteratorPosition &Pos1,
357 const IteratorPosition &Pos2,
358 bool Equal);
Adam Balogh2cfbe932018-08-28 08:41:15 +0000359ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State,
360 const MemRegion *Cont);
Adam Balogh2e7cb342018-09-10 09:07:47 +0000361ProgramStateRef
362invalidateAllIteratorPositionsExcept(ProgramStateRef State,
363 const MemRegion *Cont, SymbolRef Offset,
364 BinaryOperator::Opcode Opc);
Adam Balogh9a48ba62018-09-10 09:06:31 +0000365ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
366 SymbolRef Offset,
367 BinaryOperator::Opcode Opc);
Adam Balogh2e7cb342018-09-10 09:07:47 +0000368ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
369 SymbolRef Offset1,
370 BinaryOperator::Opcode Opc1,
371 SymbolRef Offset2,
372 BinaryOperator::Opcode Opc2);
Adam Balogh6b23b1a2018-09-10 09:04:27 +0000373ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State,
374 const MemRegion *Cont,
375 const MemRegion *NewCont);
376ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State,
377 const MemRegion *Cont,
378 const MemRegion *NewCont,
379 SymbolRef Offset,
380 BinaryOperator::Opcode Opc);
381ProgramStateRef rebaseSymbolInIteratorPositionsIf(
382 ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym,
383 SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000384const ContainerData *getContainerData(ProgramStateRef State,
385 const MemRegion *Cont);
386ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
387 const ContainerData &CData);
Adam Balogha6921202018-07-30 08:52:21 +0000388bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont);
Adam Balogh21583b72018-09-10 09:03:22 +0000389bool isBoundThroughLazyCompoundVal(const Environment &Env,
390 const MemRegion *Reg);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000391bool isOutOfRange(ProgramStateRef State, const IteratorPosition &Pos);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000392bool isZero(ProgramStateRef State, const NonLoc &Val);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000393} // namespace
394
395IteratorChecker::IteratorChecker() {
396 OutOfRangeBugType.reset(
397 new BugType(this, "Iterator out of range", "Misuse of STL APIs"));
398 OutOfRangeBugType->setSuppressOnSink(true);
Adam Balogh21583b72018-09-10 09:03:22 +0000399 MismatchedBugType.reset(
400 new BugType(this, "Iterator(s) mismatched", "Misuse of STL APIs"));
401 MismatchedBugType->setSuppressOnSink(true);
Adam Balogh2cfbe932018-08-28 08:41:15 +0000402 InvalidatedBugType.reset(
403 new BugType(this, "Iterator invalidated", "Misuse of STL APIs"));
404 InvalidatedBugType->setSuppressOnSink(true);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000405}
406
407void IteratorChecker::checkPreCall(const CallEvent &Call,
408 CheckerContext &C) const {
Adam Balogh2cfbe932018-08-28 08:41:15 +0000409 // Check for out of range access or access of invalidated position and
410 // iterator mismatches
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000411 const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
412 if (!Func)
413 return;
414
415 if (Func->isOverloadedOperator()) {
Adam Balogh2cfbe932018-08-28 08:41:15 +0000416 if (ChecksEnabled[CK_InvalidatedIteratorChecker] &&
417 isAccessOperator(Func->getOverloadedOperator())) {
418 // Check for any kind of access of invalidated iterator positions
419 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
420 verifyAccess(C, InstCall->getCXXThisVal());
421 } else {
422 verifyAccess(C, Call.getArgSVal(0));
423 }
424 }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000425 if (ChecksEnabled[CK_IteratorRangeChecker] &&
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000426 isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) {
427 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
428 // Check for out-of-range incrementions and decrementions
429 if (Call.getNumArgs() >= 1) {
430 verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(),
431 Call.getReturnValue(),
432 InstCall->getCXXThisVal(), Call.getArgSVal(0));
433 }
434 } else {
435 if (Call.getNumArgs() >= 2) {
436 verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(),
437 Call.getReturnValue(), Call.getArgSVal(0),
438 Call.getArgSVal(1));
439 }
440 }
441 } else if (ChecksEnabled[CK_IteratorRangeChecker] &&
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000442 isDereferenceOperator(Func->getOverloadedOperator())) {
443 // Check for dereference of out-of-range iterators
444 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
445 verifyDereference(C, InstCall->getCXXThisVal());
446 } else {
447 verifyDereference(C, Call.getArgSVal(0));
448 }
Adam Balogh3659f7a2018-09-10 09:05:31 +0000449 } else if (ChecksEnabled[CK_MismatchedIteratorChecker] &&
450 isComparisonOperator(Func->getOverloadedOperator())) {
451 // Check for comparisons of iterators of different containers
452 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
453 if (Call.getNumArgs() < 1)
454 return;
455
456 if (!isIteratorType(InstCall->getCXXThisExpr()->getType()) ||
457 !isIteratorType(Call.getArgExpr(0)->getType()))
458 return;
459
460 verifyMatch(C, InstCall->getCXXThisVal(), Call.getArgSVal(0));
461 } else {
462 if (Call.getNumArgs() < 2)
463 return;
464
465 if (!isIteratorType(Call.getArgExpr(0)->getType()) ||
466 !isIteratorType(Call.getArgExpr(1)->getType()))
467 return;
468
469 verifyMatch(C, Call.getArgSVal(0), Call.getArgSVal(1));
470 }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000471 }
Adam Balogh2e7cb342018-09-10 09:07:47 +0000472 } else if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
473 if (!ChecksEnabled[CK_MismatchedIteratorChecker])
474 return;
475
476 const auto *ContReg = InstCall->getCXXThisVal().getAsRegion();
477 if (!ContReg)
478 return;
479 // Check for erase, insert and emplace using iterator of another container
480 if (isEraseCall(Func) || isEraseAfterCall(Func)) {
481 verifyMatch(C, Call.getArgSVal(0),
482 InstCall->getCXXThisVal().getAsRegion());
483 if (Call.getNumArgs() == 2) {
484 verifyMatch(C, Call.getArgSVal(1),
485 InstCall->getCXXThisVal().getAsRegion());
486 }
487 } else if (isInsertCall(Func)) {
488 verifyMatch(C, Call.getArgSVal(0),
489 InstCall->getCXXThisVal().getAsRegion());
490 if (Call.getNumArgs() == 3 &&
491 isIteratorType(Call.getArgExpr(1)->getType()) &&
492 isIteratorType(Call.getArgExpr(2)->getType())) {
493 verifyMatch(C, Call.getArgSVal(1), Call.getArgSVal(2));
494 }
495 } else if (isEmplaceCall(Func)) {
496 verifyMatch(C, Call.getArgSVal(0),
497 InstCall->getCXXThisVal().getAsRegion());
498 }
Adam Balogh3659f7a2018-09-10 09:05:31 +0000499 } else if (isa<CXXConstructorCall>(&Call)) {
500 // Check match of first-last iterator pair in a constructor of a container
501 if (Call.getNumArgs() < 2)
502 return;
503
504 const auto *Ctr = cast<CXXConstructorDecl>(Call.getDecl());
505 if (Ctr->getNumParams() < 2)
506 return;
507
508 if (Ctr->getParamDecl(0)->getName() != "first" ||
509 Ctr->getParamDecl(1)->getName() != "last")
510 return;
511
512 if (!isIteratorType(Call.getArgExpr(0)->getType()) ||
513 !isIteratorType(Call.getArgExpr(1)->getType()))
514 return;
515
516 verifyMatch(C, Call.getArgSVal(0), Call.getArgSVal(1));
517 } else {
Adam Balogh21583b72018-09-10 09:03:22 +0000518 // The main purpose of iterators is to abstract away from different
519 // containers and provide a (maybe limited) uniform access to them.
520 // This implies that any correctly written template function that
521 // works on multiple containers using iterators takes different
522 // template parameters for different containers. So we can safely
523 // assume that passing iterators of different containers as arguments
524 // whose type replaces the same template parameter is a bug.
525 //
526 // Example:
527 // template<typename I1, typename I2>
528 // void f(I1 first1, I1 last1, I2 first2, I2 last2);
529 //
530 // In this case the first two arguments to f() must be iterators must belong
531 // to the same container and the last to also to the same container but
532 // not neccessarily to the same as the first two.
533
534 if (!ChecksEnabled[CK_MismatchedIteratorChecker])
535 return;
536
537 const auto *Templ = Func->getPrimaryTemplate();
538 if (!Templ)
539 return;
540
541 const auto *TParams = Templ->getTemplateParameters();
542 const auto *TArgs = Func->getTemplateSpecializationArgs();
543
544 // Iterate over all the template parameters
545 for (size_t I = 0; I < TParams->size(); ++I) {
546 const auto *TPDecl = dyn_cast<TemplateTypeParmDecl>(TParams->getParam(I));
547 if (!TPDecl)
548 continue;
549
550 if (TPDecl->isParameterPack())
551 continue;
552
553 const auto TAType = TArgs->get(I).getAsType();
554 if (!isIteratorType(TAType))
555 continue;
556
557 SVal LHS = UndefinedVal();
558
559 // For every template parameter which is an iterator type in the
560 // instantiation look for all functions' parameters' type by it and
561 // check whether they belong to the same container
562 for (auto J = 0U; J < Func->getNumParams(); ++J) {
563 const auto *Param = Func->getParamDecl(J);
564 const auto *ParamType =
565 Param->getType()->getAs<SubstTemplateTypeParmType>();
566 if (!ParamType ||
567 ParamType->getReplacedParameter()->getDecl() != TPDecl)
568 continue;
569 if (LHS.isUndef()) {
570 LHS = Call.getArgSVal(J);
571 } else {
572 verifyMatch(C, LHS, Call.getArgSVal(J));
573 }
574 }
575 }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000576 }
577}
578
579void IteratorChecker::checkPostCall(const CallEvent &Call,
580 CheckerContext &C) const {
581 // Record new iterator positions and iterator position changes
582 const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
583 if (!Func)
584 return;
585
586 if (Func->isOverloadedOperator()) {
587 const auto Op = Func->getOverloadedOperator();
Adam Balogh2cfbe932018-08-28 08:41:15 +0000588 if (isAssignmentOperator(Op)) {
589 const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call);
Adam Balogh6b23b1a2018-09-10 09:04:27 +0000590 if (Func->getParamDecl(0)->getType()->isRValueReferenceType()) {
591 handleAssign(C, InstCall->getCXXThisVal(), Call.getOriginExpr(),
592 Call.getArgSVal(0));
593 } else {
594 handleAssign(C, InstCall->getCXXThisVal());
595 }
Adam Balogh2cfbe932018-08-28 08:41:15 +0000596 } else if (isSimpleComparisonOperator(Op)) {
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000597 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
598 handleComparison(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
599 Call.getArgSVal(0), Op);
600 } else {
601 handleComparison(C, Call.getReturnValue(), Call.getArgSVal(0),
602 Call.getArgSVal(1), Op);
603 }
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000604 } else if (isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) {
605 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
606 if (Call.getNumArgs() >= 1) {
607 handleRandomIncrOrDecr(C, Func->getOverloadedOperator(),
608 Call.getReturnValue(),
609 InstCall->getCXXThisVal(), Call.getArgSVal(0));
610 }
611 } else {
612 if (Call.getNumArgs() >= 2) {
613 handleRandomIncrOrDecr(C, Func->getOverloadedOperator(),
614 Call.getReturnValue(), Call.getArgSVal(0),
615 Call.getArgSVal(1));
616 }
617 }
618 } else if (isIncrementOperator(Func->getOverloadedOperator())) {
619 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
620 handleIncrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
621 Call.getNumArgs());
622 } else {
623 handleIncrement(C, Call.getReturnValue(), Call.getArgSVal(0),
624 Call.getNumArgs());
625 }
626 } else if (isDecrementOperator(Func->getOverloadedOperator())) {
627 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
628 handleDecrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
629 Call.getNumArgs());
630 } else {
631 handleDecrement(C, Call.getReturnValue(), Call.getArgSVal(0),
632 Call.getNumArgs());
633 }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000634 }
635 } else {
Adam Balogh9a48ba62018-09-10 09:06:31 +0000636 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
Adam Balogh2e7cb342018-09-10 09:07:47 +0000637 if (isAssignCall(Func)) {
638 handleAssign(C, InstCall->getCXXThisVal());
639 } else if (isClearCall(Func)) {
640 handleClear(C, InstCall->getCXXThisVal());
641 } else if (isPushBackCall(Func) || isEmplaceBackCall(Func)) {
Adam Balogh9a48ba62018-09-10 09:06:31 +0000642 handlePushBack(C, InstCall->getCXXThisVal());
643 } else if (isPopBackCall(Func)) {
644 handlePopBack(C, InstCall->getCXXThisVal());
645 } else if (isPushFrontCall(Func) || isEmplaceFrontCall(Func)) {
646 handlePushFront(C, InstCall->getCXXThisVal());
647 } else if (isPopFrontCall(Func)) {
648 handlePopFront(C, InstCall->getCXXThisVal());
Adam Balogh2e7cb342018-09-10 09:07:47 +0000649 } else if (isInsertCall(Func) || isEmplaceCall(Func)) {
650 handleInsert(C, Call.getArgSVal(0));
651 } else if (isEraseCall(Func)) {
652 if (Call.getNumArgs() == 1) {
653 handleErase(C, Call.getArgSVal(0));
654 } else if (Call.getNumArgs() == 2) {
655 handleErase(C, Call.getArgSVal(0), Call.getArgSVal(1));
656 }
657 } else if (isEraseAfterCall(Func)) {
658 if (Call.getNumArgs() == 1) {
659 handleEraseAfter(C, Call.getArgSVal(0));
660 } else if (Call.getNumArgs() == 2) {
661 handleEraseAfter(C, Call.getArgSVal(0), Call.getArgSVal(1));
662 }
Adam Balogh9a48ba62018-09-10 09:06:31 +0000663 }
664 }
665
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000666 const auto *OrigExpr = Call.getOriginExpr();
667 if (!OrigExpr)
668 return;
669
670 if (!isIteratorType(Call.getResultType()))
671 return;
672
673 auto State = C.getState();
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000674
675 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000676 if (isBeginCall(Func)) {
677 handleBegin(C, OrigExpr, Call.getReturnValue(),
678 InstCall->getCXXThisVal());
679 return;
680 }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000681 if (isEndCall(Func)) {
682 handleEnd(C, OrigExpr, Call.getReturnValue(),
683 InstCall->getCXXThisVal());
684 return;
685 }
686 }
687
Adam Balogh6b23b1a2018-09-10 09:04:27 +0000688 // Already bound to container?
689 if (getIteratorPosition(State, Call.getReturnValue()))
690 return;
691
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000692 // Copy-like and move constructors
693 if (isa<CXXConstructorCall>(&Call) && Call.getNumArgs() == 1) {
694 if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(0))) {
695 State = setIteratorPosition(State, Call.getReturnValue(), *Pos);
696 if (cast<CXXConstructorDecl>(Func)->isMoveConstructor()) {
697 State = removeIteratorPosition(State, Call.getArgSVal(0));
698 }
699 C.addTransition(State);
700 return;
701 }
702 }
703
704 // Assumption: if return value is an iterator which is not yet bound to a
705 // container, then look for the first iterator argument, and
706 // bind the return value to the same container. This approach
707 // works for STL algorithms.
708 // FIXME: Add a more conservative mode
709 for (unsigned i = 0; i < Call.getNumArgs(); ++i) {
710 if (isIteratorType(Call.getArgExpr(i)->getType())) {
711 if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(i))) {
712 assignToContainer(C, OrigExpr, Call.getReturnValue(),
713 Pos->getContainer());
714 return;
715 }
716 }
717 }
718 }
719}
720
Adam Balogh2e7cb342018-09-10 09:07:47 +0000721void IteratorChecker::checkBind(SVal Loc, SVal Val, const Stmt *S,
722 CheckerContext &C) const {
723 auto State = C.getState();
724 const auto *Pos = getIteratorPosition(State, Val);
725 if (Pos) {
726 State = setIteratorPosition(State, Loc, *Pos);
727 C.addTransition(State);
728 } else {
729 const auto *OldPos = getIteratorPosition(State, Loc);
730 if (OldPos) {
731 State = removeIteratorPosition(State, Loc);
732 C.addTransition(State);
733 }
734 }
735}
736
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000737void IteratorChecker::checkPostStmt(const MaterializeTemporaryExpr *MTE,
738 CheckerContext &C) const {
739 /* Transfer iterator state to temporary objects */
740 auto State = C.getState();
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000741 const auto *Pos =
George Karpenkovd703ec92018-01-17 20:27:29 +0000742 getIteratorPosition(State, C.getSVal(MTE->GetTemporaryExpr()));
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000743 if (!Pos)
744 return;
George Karpenkovd703ec92018-01-17 20:27:29 +0000745 State = setIteratorPosition(State, C.getSVal(MTE), *Pos);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000746 C.addTransition(State);
747}
748
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000749void IteratorChecker::checkLiveSymbols(ProgramStateRef State,
750 SymbolReaper &SR) const {
751 // Keep symbolic expressions of iterator positions, container begins and ends
752 // alive
753 auto RegionMap = State->get<IteratorRegionMap>();
754 for (const auto Reg : RegionMap) {
Adam Balogh0a7592b2018-07-16 09:27:27 +0000755 const auto Offset = Reg.second.getOffset();
756 for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
757 if (isa<SymbolData>(*i))
758 SR.markLive(*i);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000759 }
760
761 auto SymbolMap = State->get<IteratorSymbolMap>();
762 for (const auto Sym : SymbolMap) {
Adam Balogh0a7592b2018-07-16 09:27:27 +0000763 const auto Offset = Sym.second.getOffset();
764 for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
765 if (isa<SymbolData>(*i))
766 SR.markLive(*i);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000767 }
768
769 auto ContMap = State->get<ContainerMap>();
770 for (const auto Cont : ContMap) {
771 const auto CData = Cont.second;
772 if (CData.getBegin()) {
773 SR.markLive(CData.getBegin());
Adam Balogh9a48ba62018-09-10 09:06:31 +0000774 if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getBegin()))
775 SR.markLive(SIE->getLHS());
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000776 }
777 if (CData.getEnd()) {
778 SR.markLive(CData.getEnd());
Adam Balogh9a48ba62018-09-10 09:06:31 +0000779 if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getEnd()))
780 SR.markLive(SIE->getLHS());
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000781 }
782 }
783}
784
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000785void IteratorChecker::checkDeadSymbols(SymbolReaper &SR,
786 CheckerContext &C) const {
787 // Cleanup
788 auto State = C.getState();
789
790 auto RegionMap = State->get<IteratorRegionMap>();
791 for (const auto Reg : RegionMap) {
792 if (!SR.isLiveRegion(Reg.first)) {
Adam Balogh21583b72018-09-10 09:03:22 +0000793 // The region behind the `LazyCompoundVal` is often cleaned up before
794 // the `LazyCompoundVal` itself. If there are iterator positions keyed
795 // by these regions their cleanup must be deferred.
796 if (!isBoundThroughLazyCompoundVal(State->getEnvironment(), Reg.first)) {
797 State = State->remove<IteratorRegionMap>(Reg.first);
798 }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000799 }
800 }
801
802 auto SymbolMap = State->get<IteratorSymbolMap>();
803 for (const auto Sym : SymbolMap) {
804 if (!SR.isLive(Sym.first)) {
805 State = State->remove<IteratorSymbolMap>(Sym.first);
806 }
807 }
808
809 auto ContMap = State->get<ContainerMap>();
810 for (const auto Cont : ContMap) {
811 if (!SR.isLiveRegion(Cont.first)) {
Adam Balogha6921202018-07-30 08:52:21 +0000812 // We must keep the container data while it has live iterators to be able
813 // to compare them to the begin and the end of the container.
814 if (!hasLiveIterators(State, Cont.first)) {
815 State = State->remove<ContainerMap>(Cont.first);
816 }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000817 }
818 }
819
820 auto ComparisonMap = State->get<IteratorComparisonMap>();
821 for (const auto Comp : ComparisonMap) {
822 if (!SR.isLive(Comp.first)) {
823 State = State->remove<IteratorComparisonMap>(Comp.first);
824 }
825 }
Reka Kovacse48ea892018-07-30 16:14:59 +0000826
827 C.addTransition(State);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000828}
829
830ProgramStateRef IteratorChecker::evalAssume(ProgramStateRef State, SVal Cond,
831 bool Assumption) const {
832 // Load recorded comparison and transfer iterator state between sides
833 // according to comparison operator and assumption
834 const auto *SE = Cond.getAsSymExpr();
835 if (!SE)
836 return State;
837
838 auto Opc = getOpcode(SE);
839 if (Opc != BO_EQ && Opc != BO_NE)
840 return State;
841
842 bool Negated = false;
843 const auto *Comp = loadComparison(State, SE);
844 if (!Comp) {
845 // Try negated comparison, which is a SymExpr to 0 integer comparison
846 const auto *SIE = dyn_cast<SymIntExpr>(SE);
847 if (!SIE)
848 return State;
849
850 if (SIE->getRHS() != 0)
851 return State;
852
853 SE = SIE->getLHS();
854 Negated = SIE->getOpcode() == BO_EQ; // Equal to zero means negation
855 Opc = getOpcode(SE);
856 if (Opc != BO_EQ && Opc != BO_NE)
857 return State;
858
859 Comp = loadComparison(State, SE);
860 if (!Comp)
861 return State;
862 }
863
864 return processComparison(State, Comp->getLeft(), Comp->getRight(),
865 (Comp->isEquality() == Assumption) != Negated);
866}
867
868void IteratorChecker::handleComparison(CheckerContext &C, const SVal &RetVal,
869 const SVal &LVal, const SVal &RVal,
870 OverloadedOperatorKind Op) const {
871 // Record the operands and the operator of the comparison for the next
872 // evalAssume, if the result is a symbolic expression. If it is a concrete
873 // value (only one branch is possible), then transfer the state between
874 // the operands according to the operator and the result
875 auto State = C.getState();
876 if (const auto *Condition = RetVal.getAsSymbolicExpression()) {
877 const auto *LPos = getIteratorPosition(State, LVal);
878 const auto *RPos = getIteratorPosition(State, RVal);
879 if (!LPos && !RPos)
880 return;
881 State = saveComparison(State, Condition, LVal, RVal, Op == OO_EqualEqual);
882 C.addTransition(State);
883 } else if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) {
884 if ((State = processComparison(
885 State, getRegionOrSymbol(LVal), getRegionOrSymbol(RVal),
886 (Op == OO_EqualEqual) == (TruthVal->getValue() != 0)))) {
887 C.addTransition(State);
888 } else {
889 C.generateSink(State, C.getPredecessor());
890 }
891 }
892}
893
894void IteratorChecker::verifyDereference(CheckerContext &C,
895 const SVal &Val) const {
896 auto State = C.getState();
897 const auto *Pos = getIteratorPosition(State, Val);
898 if (Pos && isOutOfRange(State, *Pos)) {
Adam Balogh2cfbe932018-08-28 08:41:15 +0000899 auto *N = C.generateNonFatalErrorNode(State);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000900 if (!N)
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000901 return;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000902 reportOutOfRangeBug("Iterator accessed outside of its range.", Val, C, N);
903 }
904}
905
Adam Balogh2cfbe932018-08-28 08:41:15 +0000906void IteratorChecker::verifyAccess(CheckerContext &C, const SVal &Val) const {
907 auto State = C.getState();
908 const auto *Pos = getIteratorPosition(State, Val);
909 if (Pos && !Pos->isValid()) {
910 auto *N = C.generateNonFatalErrorNode(State);
911 if (!N) {
912 return;
913 }
914 reportInvalidatedBug("Invalidated iterator accessed.", Val, C, N);
915 }
916}
917
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000918void IteratorChecker::handleIncrement(CheckerContext &C, const SVal &RetVal,
919 const SVal &Iter, bool Postfix) const {
920 // Increment the symbolic expressions which represents the position of the
921 // iterator
922 auto State = C.getState();
923 const auto *Pos = getIteratorPosition(State, Iter);
924 if (Pos) {
925 auto &SymMgr = C.getSymbolManager();
926 auto &BVF = SymMgr.getBasicVals();
927 auto &SVB = C.getSValBuilder();
928 const auto OldOffset = Pos->getOffset();
929 auto NewOffset =
930 SVB.evalBinOp(State, BO_Add,
931 nonloc::SymbolVal(OldOffset),
932 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
933 SymMgr.getType(OldOffset)).getAsSymbol();
934 auto NewPos = Pos->setTo(NewOffset);
935 State = setIteratorPosition(State, Iter, NewPos);
936 State = setIteratorPosition(State, RetVal, Postfix ? *Pos : NewPos);
937 C.addTransition(State);
938 }
939}
940
941void IteratorChecker::handleDecrement(CheckerContext &C, const SVal &RetVal,
942 const SVal &Iter, bool Postfix) const {
943 // Decrement the symbolic expressions which represents the position of the
944 // iterator
945 auto State = C.getState();
946 const auto *Pos = getIteratorPosition(State, Iter);
947 if (Pos) {
948 auto &SymMgr = C.getSymbolManager();
949 auto &BVF = SymMgr.getBasicVals();
950 auto &SVB = C.getSValBuilder();
951 const auto OldOffset = Pos->getOffset();
952 auto NewOffset =
953 SVB.evalBinOp(State, BO_Sub,
954 nonloc::SymbolVal(OldOffset),
955 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
956 SymMgr.getType(OldOffset)).getAsSymbol();
957 auto NewPos = Pos->setTo(NewOffset);
958 State = setIteratorPosition(State, Iter, NewPos);
959 State = setIteratorPosition(State, RetVal, Postfix ? *Pos : NewPos);
960 C.addTransition(State);
961 }
962}
963
964// This function tells the analyzer's engine that symbols produced by our
965// checker, most notably iterator positions, are relatively small.
966// A distance between items in the container should not be very large.
967// By assuming that it is within around 1/8 of the address space,
968// we can help the analyzer perform operations on these symbols
969// without being afraid of integer overflows.
970// FIXME: Should we provide it as an API, so that all checkers could use it?
971static ProgramStateRef assumeNoOverflow(ProgramStateRef State, SymbolRef Sym,
972 long Scale) {
973 SValBuilder &SVB = State->getStateManager().getSValBuilder();
974 BasicValueFactory &BV = SVB.getBasicValueFactory();
975
976 QualType T = Sym->getType();
977 assert(T->isSignedIntegerOrEnumerationType());
978 APSIntType AT = BV.getAPSIntType(T);
979
980 ProgramStateRef NewState = State;
981
982 llvm::APSInt Max = AT.getMaxValue() / AT.getValue(Scale);
983 SVal IsCappedFromAbove =
984 SVB.evalBinOpNN(State, BO_LE, nonloc::SymbolVal(Sym),
985 nonloc::ConcreteInt(Max), SVB.getConditionType());
986 if (auto DV = IsCappedFromAbove.getAs<DefinedSVal>()) {
987 NewState = NewState->assume(*DV, true);
988 if (!NewState)
989 return State;
990 }
991
992 llvm::APSInt Min = -Max;
993 SVal IsCappedFromBelow =
994 SVB.evalBinOpNN(State, BO_GE, nonloc::SymbolVal(Sym),
995 nonloc::ConcreteInt(Min), SVB.getConditionType());
996 if (auto DV = IsCappedFromBelow.getAs<DefinedSVal>()) {
997 NewState = NewState->assume(*DV, true);
998 if (!NewState)
999 return State;
1000 }
1001
1002 return NewState;
1003}
1004
1005void IteratorChecker::handleRandomIncrOrDecr(CheckerContext &C,
1006 OverloadedOperatorKind Op,
1007 const SVal &RetVal,
1008 const SVal &LHS,
1009 const SVal &RHS) const {
1010 // Increment or decrement the symbolic expressions which represents the
1011 // position of the iterator
1012 auto State = C.getState();
1013 const auto *Pos = getIteratorPosition(State, LHS);
1014 if (!Pos)
1015 return;
1016
1017 const auto *value = &RHS;
1018 if (auto loc = RHS.getAs<Loc>()) {
1019 const auto val = State->getRawSVal(*loc);
1020 value = &val;
1021 }
1022
1023 auto &SymMgr = C.getSymbolManager();
1024 auto &SVB = C.getSValBuilder();
1025 auto BinOp = (Op == OO_Plus || Op == OO_PlusEqual) ? BO_Add : BO_Sub;
1026 const auto OldOffset = Pos->getOffset();
1027 SymbolRef NewOffset;
1028 if (const auto intValue = value->getAs<nonloc::ConcreteInt>()) {
1029 // For concrete integers we can calculate the new position
1030 NewOffset = SVB.evalBinOp(State, BinOp, nonloc::SymbolVal(OldOffset),
1031 *intValue,
1032 SymMgr.getType(OldOffset)).getAsSymbol();
1033 } else {
1034 // For other symbols create a new symbol to keep expressions simple
1035 const auto &LCtx = C.getLocationContext();
1036 NewOffset = SymMgr.conjureSymbol(nullptr, LCtx, SymMgr.getType(OldOffset),
1037 C.blockCount());
1038 State = assumeNoOverflow(State, NewOffset, 4);
1039 }
1040 auto NewPos = Pos->setTo(NewOffset);
1041 auto &TgtVal = (Op == OO_PlusEqual || Op == OO_MinusEqual) ? LHS : RetVal;
1042 State = setIteratorPosition(State, TgtVal, NewPos);
1043 C.addTransition(State);
1044}
1045
1046void IteratorChecker::verifyRandomIncrOrDecr(CheckerContext &C,
1047 OverloadedOperatorKind Op,
1048 const SVal &RetVal,
1049 const SVal &LHS,
1050 const SVal &RHS) const {
1051 auto State = C.getState();
1052
1053 // If the iterator is initially inside its range, then the operation is valid
1054 const auto *Pos = getIteratorPosition(State, LHS);
1055 if (!Pos || !isOutOfRange(State, *Pos))
1056 return;
1057
1058 auto value = RHS;
1059 if (auto loc = RHS.getAs<Loc>()) {
1060 value = State->getRawSVal(*loc);
1061 }
1062
1063 // Incremention or decremention by 0 is never bug
1064 if (isZero(State, value.castAs<NonLoc>()))
1065 return;
1066
1067 auto &SymMgr = C.getSymbolManager();
1068 auto &SVB = C.getSValBuilder();
1069 auto BinOp = (Op == OO_Plus || Op == OO_PlusEqual) ? BO_Add : BO_Sub;
1070 const auto OldOffset = Pos->getOffset();
1071 const auto intValue = value.getAs<nonloc::ConcreteInt>();
1072 if (!intValue)
1073 return;
1074
1075 auto NewOffset = SVB.evalBinOp(State, BinOp, nonloc::SymbolVal(OldOffset),
1076 *intValue,
1077 SymMgr.getType(OldOffset)).getAsSymbol();
1078 auto NewPos = Pos->setTo(NewOffset);
1079
1080 // If out of range, the only valid operation is to step into the range
1081 if (isOutOfRange(State, NewPos)) {
1082 auto *N = C.generateNonFatalErrorNode(State);
1083 if (!N)
1084 return;
1085 reportOutOfRangeBug("Iterator accessed past its end.", LHS, C, N);
1086 }
1087}
1088
Adam Balogh2e7cb342018-09-10 09:07:47 +00001089void IteratorChecker::verifyMatch(CheckerContext &C, const SVal &Iter,
1090 const MemRegion *Cont) const {
1091 // Verify match between a container and the container of an iterator
Adam Balogh42d241f2018-12-04 10:22:28 +00001092 Cont = Cont->getMostDerivedObjectRegion();
Adam Balogh2e7cb342018-09-10 09:07:47 +00001093
1094 auto State = C.getState();
1095 const auto *Pos = getIteratorPosition(State, Iter);
1096 if (Pos && Pos->getContainer() != Cont) {
1097 auto *N = C.generateNonFatalErrorNode(State);
1098 if (!N) {
1099 return;
1100 }
1101 reportMismatchedBug("Container accessed using foreign iterator argument.", Iter, Cont, C, N);
1102 }
1103}
1104
Adam Balogh21583b72018-09-10 09:03:22 +00001105void IteratorChecker::verifyMatch(CheckerContext &C, const SVal &Iter1,
1106 const SVal &Iter2) const {
1107 // Verify match between the containers of two iterators
1108 auto State = C.getState();
1109 const auto *Pos1 = getIteratorPosition(State, Iter1);
1110 const auto *Pos2 = getIteratorPosition(State, Iter2);
1111 if (Pos1 && Pos2 && Pos1->getContainer() != Pos2->getContainer()) {
1112 auto *N = C.generateNonFatalErrorNode(State);
1113 if (!N)
1114 return;
1115 reportMismatchedBug("Iterators of different containers used where the "
1116 "same container is expected.", Iter1, Iter2, C, N);
1117 }
1118}
1119
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001120void IteratorChecker::handleBegin(CheckerContext &C, const Expr *CE,
1121 const SVal &RetVal, const SVal &Cont) const {
1122 const auto *ContReg = Cont.getAsRegion();
1123 if (!ContReg)
1124 return;
1125
Adam Balogh42d241f2018-12-04 10:22:28 +00001126 ContReg = ContReg->getMostDerivedObjectRegion();
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001127
1128 // If the container already has a begin symbol then use it. Otherwise first
1129 // create a new one.
1130 auto State = C.getState();
1131 auto BeginSym = getContainerBegin(State, ContReg);
1132 if (!BeginSym) {
1133 auto &SymMgr = C.getSymbolManager();
1134 BeginSym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
1135 C.getASTContext().LongTy, C.blockCount());
1136 State = assumeNoOverflow(State, BeginSym, 4);
1137 State = createContainerBegin(State, ContReg, BeginSym);
1138 }
1139 State = setIteratorPosition(State, RetVal,
1140 IteratorPosition::getPosition(ContReg, BeginSym));
1141 C.addTransition(State);
1142}
1143
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001144void IteratorChecker::handleEnd(CheckerContext &C, const Expr *CE,
1145 const SVal &RetVal, const SVal &Cont) const {
1146 const auto *ContReg = Cont.getAsRegion();
1147 if (!ContReg)
1148 return;
1149
Adam Balogh42d241f2018-12-04 10:22:28 +00001150 ContReg = ContReg->getMostDerivedObjectRegion();
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001151
1152 // If the container already has an end symbol then use it. Otherwise first
1153 // create a new one.
1154 auto State = C.getState();
1155 auto EndSym = getContainerEnd(State, ContReg);
1156 if (!EndSym) {
1157 auto &SymMgr = C.getSymbolManager();
1158 EndSym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
1159 C.getASTContext().LongTy, C.blockCount());
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001160 State = assumeNoOverflow(State, EndSym, 4);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001161 State = createContainerEnd(State, ContReg, EndSym);
1162 }
1163 State = setIteratorPosition(State, RetVal,
1164 IteratorPosition::getPosition(ContReg, EndSym));
1165 C.addTransition(State);
1166}
1167
1168void IteratorChecker::assignToContainer(CheckerContext &C, const Expr *CE,
1169 const SVal &RetVal,
1170 const MemRegion *Cont) const {
Adam Balogh42d241f2018-12-04 10:22:28 +00001171 Cont = Cont->getMostDerivedObjectRegion();
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001172
1173 auto State = C.getState();
1174 auto &SymMgr = C.getSymbolManager();
1175 auto Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
1176 C.getASTContext().LongTy, C.blockCount());
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001177 State = assumeNoOverflow(State, Sym, 4);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001178 State = setIteratorPosition(State, RetVal,
1179 IteratorPosition::getPosition(Cont, Sym));
1180 C.addTransition(State);
1181}
1182
Adam Balogh6b23b1a2018-09-10 09:04:27 +00001183void IteratorChecker::handleAssign(CheckerContext &C, const SVal &Cont,
1184 const Expr *CE, const SVal &OldCont) const {
Adam Balogh2cfbe932018-08-28 08:41:15 +00001185 const auto *ContReg = Cont.getAsRegion();
1186 if (!ContReg)
1187 return;
1188
Adam Balogh42d241f2018-12-04 10:22:28 +00001189 ContReg = ContReg->getMostDerivedObjectRegion();
Adam Balogh2cfbe932018-08-28 08:41:15 +00001190
1191 // Assignment of a new value to a container always invalidates all its
1192 // iterators
1193 auto State = C.getState();
1194 const auto CData = getContainerData(State, ContReg);
1195 if (CData) {
1196 State = invalidateAllIteratorPositions(State, ContReg);
1197 }
1198
Adam Balogh6b23b1a2018-09-10 09:04:27 +00001199 // In case of move, iterators of the old container (except the past-end
1200 // iterators) remain valid but refer to the new container
1201 if (!OldCont.isUndef()) {
1202 const auto *OldContReg = OldCont.getAsRegion();
1203 if (OldContReg) {
Adam Balogh42d241f2018-12-04 10:22:28 +00001204 OldContReg = OldContReg->getMostDerivedObjectRegion();
Adam Balogh6b23b1a2018-09-10 09:04:27 +00001205 const auto OldCData = getContainerData(State, OldContReg);
1206 if (OldCData) {
1207 if (const auto OldEndSym = OldCData->getEnd()) {
1208 // If we already assigned an "end" symbol to the old conainer, then
1209 // first reassign all iterator positions to the new container which
1210 // are not past the container (thus not greater or equal to the
1211 // current "end" symbol).
1212 State = reassignAllIteratorPositionsUnless(State, OldContReg, ContReg,
1213 OldEndSym, BO_GE);
1214 auto &SymMgr = C.getSymbolManager();
1215 auto &SVB = C.getSValBuilder();
1216 // Then generate and assign a new "end" symbol for the new container.
1217 auto NewEndSym =
1218 SymMgr.conjureSymbol(CE, C.getLocationContext(),
1219 C.getASTContext().LongTy, C.blockCount());
1220 State = assumeNoOverflow(State, NewEndSym, 4);
1221 if (CData) {
1222 State = setContainerData(State, ContReg, CData->newEnd(NewEndSym));
1223 } else {
1224 State = setContainerData(State, ContReg,
1225 ContainerData::fromEnd(NewEndSym));
1226 }
1227 // Finally, replace the old "end" symbol in the already reassigned
1228 // iterator positions with the new "end" symbol.
1229 State = rebaseSymbolInIteratorPositionsIf(
1230 State, SVB, OldEndSym, NewEndSym, OldEndSym, BO_LT);
1231 } else {
1232 // There was no "end" symbol assigned yet to the old container,
1233 // so reassign all iterator positions to the new container.
1234 State = reassignAllIteratorPositions(State, OldContReg, ContReg);
1235 }
1236 if (const auto OldBeginSym = OldCData->getBegin()) {
1237 // If we already assigned a "begin" symbol to the old container, then
1238 // assign it to the new container and remove it from the old one.
1239 if (CData) {
1240 State =
1241 setContainerData(State, ContReg, CData->newBegin(OldBeginSym));
1242 } else {
1243 State = setContainerData(State, ContReg,
1244 ContainerData::fromBegin(OldBeginSym));
1245 }
1246 State =
1247 setContainerData(State, OldContReg, OldCData->newEnd(nullptr));
1248 }
1249 } else {
1250 // There was neither "begin" nor "end" symbol assigned yet to the old
1251 // container, so reassign all iterator positions to the new container.
1252 State = reassignAllIteratorPositions(State, OldContReg, ContReg);
1253 }
1254 }
1255 }
Adam Balogh2cfbe932018-08-28 08:41:15 +00001256 C.addTransition(State);
1257}
1258
Adam Balogh2e7cb342018-09-10 09:07:47 +00001259void IteratorChecker::handleClear(CheckerContext &C, const SVal &Cont) const {
1260 const auto *ContReg = Cont.getAsRegion();
1261 if (!ContReg)
1262 return;
1263
Adam Balogh42d241f2018-12-04 10:22:28 +00001264 ContReg = ContReg->getMostDerivedObjectRegion();
Adam Balogh2e7cb342018-09-10 09:07:47 +00001265
1266 // The clear() operation invalidates all the iterators, except the past-end
1267 // iterators of list-like containers
1268 auto State = C.getState();
1269 if (!hasSubscriptOperator(State, ContReg) ||
1270 !backModifiable(State, ContReg)) {
1271 const auto CData = getContainerData(State, ContReg);
1272 if (CData) {
1273 if (const auto EndSym = CData->getEnd()) {
1274 State =
1275 invalidateAllIteratorPositionsExcept(State, ContReg, EndSym, BO_GE);
1276 C.addTransition(State);
1277 return;
1278 }
1279 }
1280 }
1281 State = invalidateAllIteratorPositions(State, ContReg);
1282 C.addTransition(State);
1283}
1284
Adam Balogh9a48ba62018-09-10 09:06:31 +00001285void IteratorChecker::handlePushBack(CheckerContext &C,
1286 const SVal &Cont) const {
1287 const auto *ContReg = Cont.getAsRegion();
1288 if (!ContReg)
1289 return;
1290
Adam Balogh42d241f2018-12-04 10:22:28 +00001291 ContReg = ContReg->getMostDerivedObjectRegion();
Adam Balogh9a48ba62018-09-10 09:06:31 +00001292
1293 // For deque-like containers invalidate all iterator positions
1294 auto State = C.getState();
1295 if (hasSubscriptOperator(State, ContReg) && frontModifiable(State, ContReg)) {
1296 State = invalidateAllIteratorPositions(State, ContReg);
1297 C.addTransition(State);
1298 return;
1299 }
1300
1301 const auto CData = getContainerData(State, ContReg);
1302 if (!CData)
1303 return;
1304
1305 // For vector-like containers invalidate the past-end iterator positions
1306 if (const auto EndSym = CData->getEnd()) {
1307 if (hasSubscriptOperator(State, ContReg)) {
1308 State = invalidateIteratorPositions(State, EndSym, BO_GE);
1309 }
1310 auto &SymMgr = C.getSymbolManager();
1311 auto &BVF = SymMgr.getBasicVals();
1312 auto &SVB = C.getSValBuilder();
1313 const auto newEndSym =
1314 SVB.evalBinOp(State, BO_Add,
1315 nonloc::SymbolVal(EndSym),
1316 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
1317 SymMgr.getType(EndSym)).getAsSymbol();
1318 State = setContainerData(State, ContReg, CData->newEnd(newEndSym));
1319 }
1320 C.addTransition(State);
1321}
1322
1323void IteratorChecker::handlePopBack(CheckerContext &C, const SVal &Cont) const {
1324 const auto *ContReg = Cont.getAsRegion();
1325 if (!ContReg)
1326 return;
1327
Adam Balogh42d241f2018-12-04 10:22:28 +00001328 ContReg = ContReg->getMostDerivedObjectRegion();
Adam Balogh9a48ba62018-09-10 09:06:31 +00001329
1330 auto State = C.getState();
1331 const auto CData = getContainerData(State, ContReg);
1332 if (!CData)
1333 return;
1334
1335 if (const auto EndSym = CData->getEnd()) {
1336 auto &SymMgr = C.getSymbolManager();
1337 auto &BVF = SymMgr.getBasicVals();
1338 auto &SVB = C.getSValBuilder();
1339 const auto BackSym =
1340 SVB.evalBinOp(State, BO_Sub,
1341 nonloc::SymbolVal(EndSym),
1342 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
1343 SymMgr.getType(EndSym)).getAsSymbol();
1344 // For vector-like and deque-like containers invalidate the last and the
1345 // past-end iterator positions. For list-like containers only invalidate
1346 // the last position
1347 if (hasSubscriptOperator(State, ContReg) &&
1348 backModifiable(State, ContReg)) {
1349 State = invalidateIteratorPositions(State, BackSym, BO_GE);
1350 State = setContainerData(State, ContReg, CData->newEnd(nullptr));
1351 } else {
1352 State = invalidateIteratorPositions(State, BackSym, BO_EQ);
1353 }
1354 auto newEndSym = BackSym;
1355 State = setContainerData(State, ContReg, CData->newEnd(newEndSym));
1356 C.addTransition(State);
1357 }
1358}
1359
1360void IteratorChecker::handlePushFront(CheckerContext &C,
1361 const SVal &Cont) const {
1362 const auto *ContReg = Cont.getAsRegion();
1363 if (!ContReg)
1364 return;
1365
Adam Balogh42d241f2018-12-04 10:22:28 +00001366 ContReg = ContReg->getMostDerivedObjectRegion();
Adam Balogh9a48ba62018-09-10 09:06:31 +00001367
1368 // For deque-like containers invalidate all iterator positions
1369 auto State = C.getState();
1370 if (hasSubscriptOperator(State, ContReg)) {
1371 State = invalidateAllIteratorPositions(State, ContReg);
1372 C.addTransition(State);
1373 } else {
1374 const auto CData = getContainerData(State, ContReg);
1375 if (!CData)
1376 return;
1377
1378 if (const auto BeginSym = CData->getBegin()) {
1379 auto &SymMgr = C.getSymbolManager();
1380 auto &BVF = SymMgr.getBasicVals();
1381 auto &SVB = C.getSValBuilder();
1382 const auto newBeginSym =
1383 SVB.evalBinOp(State, BO_Sub,
1384 nonloc::SymbolVal(BeginSym),
1385 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
1386 SymMgr.getType(BeginSym)).getAsSymbol();
1387 State = setContainerData(State, ContReg, CData->newBegin(newBeginSym));
1388 C.addTransition(State);
1389 }
1390 }
1391}
1392
1393void IteratorChecker::handlePopFront(CheckerContext &C,
1394 const SVal &Cont) const {
1395 const auto *ContReg = Cont.getAsRegion();
1396 if (!ContReg)
1397 return;
1398
Adam Balogh42d241f2018-12-04 10:22:28 +00001399 ContReg = ContReg->getMostDerivedObjectRegion();
Adam Balogh9a48ba62018-09-10 09:06:31 +00001400
1401 auto State = C.getState();
1402 const auto CData = getContainerData(State, ContReg);
1403 if (!CData)
1404 return;
1405
1406 // For deque-like containers invalidate all iterator positions. For list-like
1407 // iterators only invalidate the first position
1408 if (const auto BeginSym = CData->getBegin()) {
1409 if (hasSubscriptOperator(State, ContReg)) {
1410 State = invalidateIteratorPositions(State, BeginSym, BO_LE);
1411 } else {
1412 State = invalidateIteratorPositions(State, BeginSym, BO_EQ);
1413 }
1414 auto &SymMgr = C.getSymbolManager();
1415 auto &BVF = SymMgr.getBasicVals();
1416 auto &SVB = C.getSValBuilder();
1417 const auto newBeginSym =
1418 SVB.evalBinOp(State, BO_Add,
1419 nonloc::SymbolVal(BeginSym),
1420 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
1421 SymMgr.getType(BeginSym)).getAsSymbol();
1422 State = setContainerData(State, ContReg, CData->newBegin(newBeginSym));
1423 C.addTransition(State);
1424 }
1425}
1426
Adam Balogh2e7cb342018-09-10 09:07:47 +00001427void IteratorChecker::handleInsert(CheckerContext &C, const SVal &Iter) const {
1428 auto State = C.getState();
1429 const auto *Pos = getIteratorPosition(State, Iter);
1430 if (!Pos)
1431 return;
1432
1433 // For deque-like containers invalidate all iterator positions. For
1434 // vector-like containers invalidate iterator positions after the insertion.
1435 const auto *Cont = Pos->getContainer();
1436 if (hasSubscriptOperator(State, Cont) && backModifiable(State, Cont)) {
1437 if (frontModifiable(State, Cont)) {
1438 State = invalidateAllIteratorPositions(State, Cont);
1439 } else {
1440 State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE);
1441 }
1442 if (const auto *CData = getContainerData(State, Cont)) {
1443 if (const auto EndSym = CData->getEnd()) {
1444 State = invalidateIteratorPositions(State, EndSym, BO_GE);
1445 State = setContainerData(State, Cont, CData->newEnd(nullptr));
1446 }
1447 }
1448 C.addTransition(State);
1449 }
1450}
1451
1452void IteratorChecker::handleErase(CheckerContext &C, const SVal &Iter) const {
1453 auto State = C.getState();
1454 const auto *Pos = getIteratorPosition(State, Iter);
1455 if (!Pos)
1456 return;
1457
1458 // For deque-like containers invalidate all iterator positions. For
1459 // vector-like containers invalidate iterator positions at and after the
1460 // deletion. For list-like containers only invalidate the deleted position.
1461 const auto *Cont = Pos->getContainer();
1462 if (hasSubscriptOperator(State, Cont) && backModifiable(State, Cont)) {
1463 if (frontModifiable(State, Cont)) {
1464 State = invalidateAllIteratorPositions(State, Cont);
1465 } else {
1466 State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE);
1467 }
1468 if (const auto *CData = getContainerData(State, Cont)) {
1469 if (const auto EndSym = CData->getEnd()) {
1470 State = invalidateIteratorPositions(State, EndSym, BO_GE);
1471 State = setContainerData(State, Cont, CData->newEnd(nullptr));
1472 }
1473 }
1474 } else {
1475 State = invalidateIteratorPositions(State, Pos->getOffset(), BO_EQ);
1476 }
1477 C.addTransition(State);
1478}
1479
1480void IteratorChecker::handleErase(CheckerContext &C, const SVal &Iter1,
1481 const SVal &Iter2) const {
1482 auto State = C.getState();
1483 const auto *Pos1 = getIteratorPosition(State, Iter1);
1484 const auto *Pos2 = getIteratorPosition(State, Iter2);
1485 if (!Pos1 || !Pos2)
1486 return;
1487
1488 // For deque-like containers invalidate all iterator positions. For
1489 // vector-like containers invalidate iterator positions at and after the
1490 // deletion range. For list-like containers only invalidate the deleted
1491 // position range [first..last].
1492 const auto *Cont = Pos1->getContainer();
1493 if (hasSubscriptOperator(State, Cont) && backModifiable(State, Cont)) {
1494 if (frontModifiable(State, Cont)) {
1495 State = invalidateAllIteratorPositions(State, Cont);
1496 } else {
1497 State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE);
1498 }
1499 if (const auto *CData = getContainerData(State, Cont)) {
1500 if (const auto EndSym = CData->getEnd()) {
1501 State = invalidateIteratorPositions(State, EndSym, BO_GE);
1502 State = setContainerData(State, Cont, CData->newEnd(nullptr));
1503 }
1504 }
1505 } else {
1506 State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE,
1507 Pos2->getOffset(), BO_LT);
1508 }
1509 C.addTransition(State);
1510}
1511
1512void IteratorChecker::handleEraseAfter(CheckerContext &C,
1513 const SVal &Iter) const {
1514 auto State = C.getState();
1515 const auto *Pos = getIteratorPosition(State, Iter);
1516 if (!Pos)
1517 return;
1518
1519 // Invalidate the deleted iterator position, which is the position of the
1520 // parameter plus one.
1521 auto &SymMgr = C.getSymbolManager();
1522 auto &BVF = SymMgr.getBasicVals();
1523 auto &SVB = C.getSValBuilder();
1524 const auto NextSym =
1525 SVB.evalBinOp(State, BO_Add,
1526 nonloc::SymbolVal(Pos->getOffset()),
1527 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
1528 SymMgr.getType(Pos->getOffset())).getAsSymbol();
1529 State = invalidateIteratorPositions(State, NextSym, BO_EQ);
1530 C.addTransition(State);
1531}
1532
1533void IteratorChecker::handleEraseAfter(CheckerContext &C, const SVal &Iter1,
1534 const SVal &Iter2) const {
1535 auto State = C.getState();
1536 const auto *Pos1 = getIteratorPosition(State, Iter1);
1537 const auto *Pos2 = getIteratorPosition(State, Iter2);
1538 if (!Pos1 || !Pos2)
1539 return;
1540
1541 // Invalidate the deleted iterator position range (first..last)
1542 State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GT,
1543 Pos2->getOffset(), BO_LT);
1544 C.addTransition(State);
1545}
1546
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001547void IteratorChecker::reportOutOfRangeBug(const StringRef &Message,
1548 const SVal &Val, CheckerContext &C,
1549 ExplodedNode *ErrNode) const {
1550 auto R = llvm::make_unique<BugReport>(*OutOfRangeBugType, Message, ErrNode);
1551 R->markInteresting(Val);
1552 C.emitReport(std::move(R));
1553}
1554
Adam Balogh21583b72018-09-10 09:03:22 +00001555void IteratorChecker::reportMismatchedBug(const StringRef &Message,
1556 const SVal &Val1, const SVal &Val2,
1557 CheckerContext &C,
1558 ExplodedNode *ErrNode) const {
1559 auto R = llvm::make_unique<BugReport>(*MismatchedBugType, Message, ErrNode);
1560 R->markInteresting(Val1);
1561 R->markInteresting(Val2);
1562 C.emitReport(std::move(R));
1563}
1564
Adam Balogh3659f7a2018-09-10 09:05:31 +00001565void IteratorChecker::reportMismatchedBug(const StringRef &Message,
1566 const SVal &Val, const MemRegion *Reg,
1567 CheckerContext &C,
1568 ExplodedNode *ErrNode) const {
1569 auto R = llvm::make_unique<BugReport>(*MismatchedBugType, Message, ErrNode);
1570 R->markInteresting(Val);
1571 R->markInteresting(Reg);
1572 C.emitReport(std::move(R));
1573}
1574
Adam Balogh2cfbe932018-08-28 08:41:15 +00001575void IteratorChecker::reportInvalidatedBug(const StringRef &Message,
1576 const SVal &Val, CheckerContext &C,
1577 ExplodedNode *ErrNode) const {
1578 auto R = llvm::make_unique<BugReport>(*InvalidatedBugType, Message, ErrNode);
1579 R->markInteresting(Val);
1580 C.emitReport(std::move(R));
1581}
1582
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001583namespace {
1584
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001585bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001586bool isGreaterOrEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
1587bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
1588 BinaryOperator::Opcode Opc);
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001589bool compare(ProgramStateRef State, NonLoc NL1, NonLoc NL2,
1590 BinaryOperator::Opcode Opc);
Adam Balogh9a48ba62018-09-10 09:06:31 +00001591const CXXRecordDecl *getCXXRecordDecl(ProgramStateRef State,
1592 const MemRegion *Reg);
Adam Balogh6b23b1a2018-09-10 09:04:27 +00001593SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB, SymbolRef Expr,
1594 SymbolRef OldSym, SymbolRef NewSym);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001595
1596bool isIteratorType(const QualType &Type) {
1597 if (Type->isPointerType())
1598 return true;
1599
1600 const auto *CRD = Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
1601 return isIterator(CRD);
1602}
1603
1604bool isIterator(const CXXRecordDecl *CRD) {
1605 if (!CRD)
1606 return false;
1607
1608 const auto Name = CRD->getName();
1609 if (!(Name.endswith_lower("iterator") || Name.endswith_lower("iter") ||
1610 Name.endswith_lower("it")))
1611 return false;
1612
1613 bool HasCopyCtor = false, HasCopyAssign = true, HasDtor = false,
1614 HasPreIncrOp = false, HasPostIncrOp = false, HasDerefOp = false;
1615 for (const auto *Method : CRD->methods()) {
1616 if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(Method)) {
1617 if (Ctor->isCopyConstructor()) {
1618 HasCopyCtor = !Ctor->isDeleted() && Ctor->getAccess() == AS_public;
1619 }
1620 continue;
1621 }
1622 if (const auto *Dtor = dyn_cast<CXXDestructorDecl>(Method)) {
1623 HasDtor = !Dtor->isDeleted() && Dtor->getAccess() == AS_public;
1624 continue;
1625 }
1626 if (Method->isCopyAssignmentOperator()) {
1627 HasCopyAssign = !Method->isDeleted() && Method->getAccess() == AS_public;
1628 continue;
1629 }
1630 if (!Method->isOverloadedOperator())
1631 continue;
1632 const auto OPK = Method->getOverloadedOperator();
1633 if (OPK == OO_PlusPlus) {
1634 HasPreIncrOp = HasPreIncrOp || (Method->getNumParams() == 0);
1635 HasPostIncrOp = HasPostIncrOp || (Method->getNumParams() == 1);
1636 continue;
1637 }
1638 if (OPK == OO_Star) {
1639 HasDerefOp = (Method->getNumParams() == 0);
1640 continue;
1641 }
1642 }
1643
1644 return HasCopyCtor && HasCopyAssign && HasDtor && HasPreIncrOp &&
1645 HasPostIncrOp && HasDerefOp;
1646}
1647
Adam Balogh3659f7a2018-09-10 09:05:31 +00001648bool isComparisonOperator(OverloadedOperatorKind OK) {
1649 return OK == OO_EqualEqual || OK == OO_ExclaimEqual || OK == OO_Less ||
1650 OK == OO_LessEqual || OK == OO_Greater || OK == OO_GreaterEqual;
1651}
1652
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001653bool isBeginCall(const FunctionDecl *Func) {
1654 const auto *IdInfo = Func->getIdentifier();
1655 if (!IdInfo)
1656 return false;
1657 return IdInfo->getName().endswith_lower("begin");
1658}
1659
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001660bool isEndCall(const FunctionDecl *Func) {
1661 const auto *IdInfo = Func->getIdentifier();
1662 if (!IdInfo)
1663 return false;
1664 return IdInfo->getName().endswith_lower("end");
1665}
1666
Adam Balogh2e7cb342018-09-10 09:07:47 +00001667bool isAssignCall(const FunctionDecl *Func) {
1668 const auto *IdInfo = Func->getIdentifier();
1669 if (!IdInfo)
1670 return false;
1671 if (Func->getNumParams() > 2)
1672 return false;
1673 return IdInfo->getName() == "assign";
1674}
1675
1676bool isClearCall(const FunctionDecl *Func) {
1677 const auto *IdInfo = Func->getIdentifier();
1678 if (!IdInfo)
1679 return false;
1680 if (Func->getNumParams() > 0)
1681 return false;
1682 return IdInfo->getName() == "clear";
1683}
1684
Adam Balogh9a48ba62018-09-10 09:06:31 +00001685bool isPushBackCall(const FunctionDecl *Func) {
1686 const auto *IdInfo = Func->getIdentifier();
1687 if (!IdInfo)
1688 return false;
1689 if (Func->getNumParams() != 1)
1690 return false;
1691 return IdInfo->getName() == "push_back";
1692}
1693
1694bool isEmplaceBackCall(const FunctionDecl *Func) {
1695 const auto *IdInfo = Func->getIdentifier();
1696 if (!IdInfo)
1697 return false;
1698 if (Func->getNumParams() < 1)
1699 return false;
1700 return IdInfo->getName() == "emplace_back";
1701}
1702
1703bool isPopBackCall(const FunctionDecl *Func) {
1704 const auto *IdInfo = Func->getIdentifier();
1705 if (!IdInfo)
1706 return false;
1707 if (Func->getNumParams() > 0)
1708 return false;
1709 return IdInfo->getName() == "pop_back";
1710}
1711
1712bool isPushFrontCall(const FunctionDecl *Func) {
1713 const auto *IdInfo = Func->getIdentifier();
1714 if (!IdInfo)
1715 return false;
1716 if (Func->getNumParams() != 1)
1717 return false;
1718 return IdInfo->getName() == "push_front";
1719}
1720
1721bool isEmplaceFrontCall(const FunctionDecl *Func) {
1722 const auto *IdInfo = Func->getIdentifier();
1723 if (!IdInfo)
1724 return false;
1725 if (Func->getNumParams() < 1)
1726 return false;
1727 return IdInfo->getName() == "emplace_front";
1728}
1729
1730bool isPopFrontCall(const FunctionDecl *Func) {
1731 const auto *IdInfo = Func->getIdentifier();
1732 if (!IdInfo)
1733 return false;
1734 if (Func->getNumParams() > 0)
1735 return false;
1736 return IdInfo->getName() == "pop_front";
1737}
1738
Adam Balogh2e7cb342018-09-10 09:07:47 +00001739bool isInsertCall(const FunctionDecl *Func) {
1740 const auto *IdInfo = Func->getIdentifier();
1741 if (!IdInfo)
1742 return false;
1743 if (Func->getNumParams() < 2 || Func->getNumParams() > 3)
1744 return false;
1745 if (!isIteratorType(Func->getParamDecl(0)->getType()))
1746 return false;
1747 return IdInfo->getName() == "insert";
1748}
1749
1750bool isEmplaceCall(const FunctionDecl *Func) {
1751 const auto *IdInfo = Func->getIdentifier();
1752 if (!IdInfo)
1753 return false;
1754 if (Func->getNumParams() < 2)
1755 return false;
1756 if (!isIteratorType(Func->getParamDecl(0)->getType()))
1757 return false;
1758 return IdInfo->getName() == "emplace";
1759}
1760
1761bool isEraseCall(const FunctionDecl *Func) {
1762 const auto *IdInfo = Func->getIdentifier();
1763 if (!IdInfo)
1764 return false;
1765 if (Func->getNumParams() < 1 || Func->getNumParams() > 2)
1766 return false;
1767 if (!isIteratorType(Func->getParamDecl(0)->getType()))
1768 return false;
1769 if (Func->getNumParams() == 2 &&
1770 !isIteratorType(Func->getParamDecl(1)->getType()))
1771 return false;
1772 return IdInfo->getName() == "erase";
1773}
1774
1775bool isEraseAfterCall(const FunctionDecl *Func) {
1776 const auto *IdInfo = Func->getIdentifier();
1777 if (!IdInfo)
1778 return false;
1779 if (Func->getNumParams() < 1 || Func->getNumParams() > 2)
1780 return false;
1781 if (!isIteratorType(Func->getParamDecl(0)->getType()))
1782 return false;
1783 if (Func->getNumParams() == 2 &&
1784 !isIteratorType(Func->getParamDecl(1)->getType()))
1785 return false;
1786 return IdInfo->getName() == "erase_after";
1787}
1788
Adam Balogh2cfbe932018-08-28 08:41:15 +00001789bool isAssignmentOperator(OverloadedOperatorKind OK) { return OK == OO_Equal; }
1790
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001791bool isSimpleComparisonOperator(OverloadedOperatorKind OK) {
1792 return OK == OO_EqualEqual || OK == OO_ExclaimEqual;
1793}
1794
Adam Balogh2cfbe932018-08-28 08:41:15 +00001795bool isAccessOperator(OverloadedOperatorKind OK) {
1796 return isDereferenceOperator(OK) || isIncrementOperator(OK) ||
1797 isDecrementOperator(OK) || isRandomIncrOrDecrOperator(OK);
1798}
1799
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001800bool isDereferenceOperator(OverloadedOperatorKind OK) {
1801 return OK == OO_Star || OK == OO_Arrow || OK == OO_ArrowStar ||
1802 OK == OO_Subscript;
1803}
1804
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001805bool isIncrementOperator(OverloadedOperatorKind OK) {
1806 return OK == OO_PlusPlus;
1807}
1808
1809bool isDecrementOperator(OverloadedOperatorKind OK) {
1810 return OK == OO_MinusMinus;
1811}
1812
1813bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK) {
1814 return OK == OO_Plus || OK == OO_PlusEqual || OK == OO_Minus ||
1815 OK == OO_MinusEqual;
1816}
1817
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001818BinaryOperator::Opcode getOpcode(const SymExpr *SE) {
1819 if (const auto *BSE = dyn_cast<BinarySymExpr>(SE)) {
1820 return BSE->getOpcode();
1821 } else if (const auto *SC = dyn_cast<SymbolConjured>(SE)) {
Henry Wong073d5f02018-03-20 09:27:02 +00001822 const auto *COE = dyn_cast_or_null<CXXOperatorCallExpr>(SC->getStmt());
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001823 if (!COE)
1824 return BO_Comma; // Extremal value, neither EQ nor NE
1825 if (COE->getOperator() == OO_EqualEqual) {
1826 return BO_EQ;
1827 } else if (COE->getOperator() == OO_ExclaimEqual) {
1828 return BO_NE;
1829 }
1830 return BO_Comma; // Extremal value, neither EQ nor NE
1831 }
1832 return BO_Comma; // Extremal value, neither EQ nor NE
1833}
1834
Adam Balogh9a48ba62018-09-10 09:06:31 +00001835bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg) {
1836 const auto *CRD = getCXXRecordDecl(State, Reg);
1837 if (!CRD)
1838 return false;
1839
1840 for (const auto *Method : CRD->methods()) {
1841 if (!Method->isOverloadedOperator())
1842 continue;
1843 const auto OPK = Method->getOverloadedOperator();
1844 if (OPK == OO_Subscript) {
1845 return true;
1846 }
1847 }
1848 return false;
1849}
1850
1851bool frontModifiable(ProgramStateRef State, const MemRegion *Reg) {
1852 const auto *CRD = getCXXRecordDecl(State, Reg);
1853 if (!CRD)
1854 return false;
1855
1856 for (const auto *Method : CRD->methods()) {
1857 if (!Method->getDeclName().isIdentifier())
1858 continue;
1859 if (Method->getName() == "push_front" || Method->getName() == "pop_front") {
1860 return true;
1861 }
1862 }
1863 return false;
1864}
1865
1866bool backModifiable(ProgramStateRef State, const MemRegion *Reg) {
1867 const auto *CRD = getCXXRecordDecl(State, Reg);
1868 if (!CRD)
1869 return false;
1870
1871 for (const auto *Method : CRD->methods()) {
1872 if (!Method->getDeclName().isIdentifier())
1873 continue;
1874 if (Method->getName() == "push_back" || Method->getName() == "pop_back") {
1875 return true;
1876 }
1877 }
1878 return false;
1879}
1880
1881const CXXRecordDecl *getCXXRecordDecl(ProgramStateRef State,
1882 const MemRegion *Reg) {
1883 auto TI = getDynamicTypeInfo(State, Reg);
1884 if (!TI.isValid())
1885 return nullptr;
1886
1887 auto Type = TI.getType();
1888 if (const auto *RefT = Type->getAs<ReferenceType>()) {
1889 Type = RefT->getPointeeType();
1890 }
1891
1892 return Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
1893}
1894
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001895const RegionOrSymbol getRegionOrSymbol(const SVal &Val) {
1896 if (const auto Reg = Val.getAsRegion()) {
1897 return Reg;
1898 } else if (const auto Sym = Val.getAsSymbol()) {
1899 return Sym;
1900 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
1901 return LCVal->getRegion();
1902 }
1903 return RegionOrSymbol();
1904}
1905
1906const ProgramStateRef processComparison(ProgramStateRef State,
1907 RegionOrSymbol LVal,
1908 RegionOrSymbol RVal, bool Equal) {
1909 const auto *LPos = getIteratorPosition(State, LVal);
1910 const auto *RPos = getIteratorPosition(State, RVal);
1911 if (LPos && !RPos) {
1912 State = adjustIteratorPosition(State, RVal, *LPos, Equal);
1913 } else if (!LPos && RPos) {
1914 State = adjustIteratorPosition(State, LVal, *RPos, Equal);
1915 } else if (LPos && RPos) {
1916 State = relateIteratorPositions(State, *LPos, *RPos, Equal);
1917 }
1918 return State;
1919}
1920
1921const ProgramStateRef saveComparison(ProgramStateRef State,
1922 const SymExpr *Condition, const SVal &LVal,
1923 const SVal &RVal, bool Eq) {
1924 const auto Left = getRegionOrSymbol(LVal);
1925 const auto Right = getRegionOrSymbol(RVal);
1926 if (!Left || !Right)
1927 return State;
1928 return State->set<IteratorComparisonMap>(Condition,
1929 IteratorComparison(Left, Right, Eq));
1930}
1931
1932const IteratorComparison *loadComparison(ProgramStateRef State,
1933 const SymExpr *Condition) {
1934 return State->get<IteratorComparisonMap>(Condition);
1935}
1936
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001937SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont) {
1938 const auto *CDataPtr = getContainerData(State, Cont);
1939 if (!CDataPtr)
1940 return nullptr;
1941
1942 return CDataPtr->getBegin();
1943}
1944
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001945SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont) {
1946 const auto *CDataPtr = getContainerData(State, Cont);
1947 if (!CDataPtr)
1948 return nullptr;
1949
1950 return CDataPtr->getEnd();
1951}
1952
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001953ProgramStateRef createContainerBegin(ProgramStateRef State,
1954 const MemRegion *Cont,
1955 const SymbolRef Sym) {
1956 // Only create if it does not exist
1957 const auto *CDataPtr = getContainerData(State, Cont);
1958 if (CDataPtr) {
1959 if (CDataPtr->getBegin()) {
1960 return State;
1961 }
1962 const auto CData = CDataPtr->newBegin(Sym);
1963 return setContainerData(State, Cont, CData);
1964 }
1965 const auto CData = ContainerData::fromBegin(Sym);
1966 return setContainerData(State, Cont, CData);
1967}
1968
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001969ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
1970 const SymbolRef Sym) {
1971 // Only create if it does not exist
1972 const auto *CDataPtr = getContainerData(State, Cont);
1973 if (CDataPtr) {
1974 if (CDataPtr->getEnd()) {
1975 return State;
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001976 }
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001977 const auto CData = CDataPtr->newEnd(Sym);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001978 return setContainerData(State, Cont, CData);
1979 }
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001980 const auto CData = ContainerData::fromEnd(Sym);
1981 return setContainerData(State, Cont, CData);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001982}
1983
1984const ContainerData *getContainerData(ProgramStateRef State,
1985 const MemRegion *Cont) {
1986 return State->get<ContainerMap>(Cont);
1987}
1988
1989ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
1990 const ContainerData &CData) {
1991 return State->set<ContainerMap>(Cont, CData);
1992}
1993
1994const IteratorPosition *getIteratorPosition(ProgramStateRef State,
1995 const SVal &Val) {
Adam Balogh42d241f2018-12-04 10:22:28 +00001996 if (auto Reg = Val.getAsRegion()) {
1997 Reg = Reg->getMostDerivedObjectRegion();
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001998 return State->get<IteratorRegionMap>(Reg);
1999 } else if (const auto Sym = Val.getAsSymbol()) {
2000 return State->get<IteratorSymbolMap>(Sym);
2001 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
2002 return State->get<IteratorRegionMap>(LCVal->getRegion());
2003 }
2004 return nullptr;
2005}
2006
2007const IteratorPosition *getIteratorPosition(ProgramStateRef State,
2008 RegionOrSymbol RegOrSym) {
2009 if (RegOrSym.is<const MemRegion *>()) {
Adam Balogh42d241f2018-12-04 10:22:28 +00002010 auto Reg = RegOrSym.get<const MemRegion *>()->getMostDerivedObjectRegion();
2011 return State->get<IteratorRegionMap>(Reg);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002012 } else if (RegOrSym.is<SymbolRef>()) {
2013 return State->get<IteratorSymbolMap>(RegOrSym.get<SymbolRef>());
2014 }
2015 return nullptr;
2016}
2017
2018ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
2019 const IteratorPosition &Pos) {
Adam Balogh42d241f2018-12-04 10:22:28 +00002020 if (auto Reg = Val.getAsRegion()) {
2021 Reg = Reg->getMostDerivedObjectRegion();
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002022 return State->set<IteratorRegionMap>(Reg, Pos);
2023 } else if (const auto Sym = Val.getAsSymbol()) {
2024 return State->set<IteratorSymbolMap>(Sym, Pos);
2025 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
2026 return State->set<IteratorRegionMap>(LCVal->getRegion(), Pos);
2027 }
2028 return nullptr;
2029}
2030
2031ProgramStateRef setIteratorPosition(ProgramStateRef State,
2032 RegionOrSymbol RegOrSym,
2033 const IteratorPosition &Pos) {
2034 if (RegOrSym.is<const MemRegion *>()) {
Adam Balogh42d241f2018-12-04 10:22:28 +00002035 auto Reg = RegOrSym.get<const MemRegion *>()->getMostDerivedObjectRegion();
2036 return State->set<IteratorRegionMap>(Reg, Pos);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002037 } else if (RegOrSym.is<SymbolRef>()) {
2038 return State->set<IteratorSymbolMap>(RegOrSym.get<SymbolRef>(), Pos);
2039 }
2040 return nullptr;
2041}
2042
2043ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val) {
Adam Balogh42d241f2018-12-04 10:22:28 +00002044 if (auto Reg = Val.getAsRegion()) {
2045 Reg = Reg->getMostDerivedObjectRegion();
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002046 return State->remove<IteratorRegionMap>(Reg);
2047 } else if (const auto Sym = Val.getAsSymbol()) {
2048 return State->remove<IteratorSymbolMap>(Sym);
2049 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
2050 return State->remove<IteratorRegionMap>(LCVal->getRegion());
2051 }
2052 return nullptr;
2053}
2054
2055ProgramStateRef adjustIteratorPosition(ProgramStateRef State,
2056 RegionOrSymbol RegOrSym,
2057 const IteratorPosition &Pos,
2058 bool Equal) {
2059 if (Equal) {
2060 return setIteratorPosition(State, RegOrSym, Pos);
2061 } else {
2062 return State;
2063 }
2064}
2065
2066ProgramStateRef relateIteratorPositions(ProgramStateRef State,
2067 const IteratorPosition &Pos1,
2068 const IteratorPosition &Pos2,
2069 bool Equal) {
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002070 auto &SVB = State->getStateManager().getSValBuilder();
Adam Balogh0a7592b2018-07-16 09:27:27 +00002071
2072 // FIXME: This code should be reworked as follows:
2073 // 1. Subtract the operands using evalBinOp().
2074 // 2. Assume that the result doesn't overflow.
2075 // 3. Compare the result to 0.
2076 // 4. Assume the result of the comparison.
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002077 const auto comparison =
2078 SVB.evalBinOp(State, BO_EQ, nonloc::SymbolVal(Pos1.getOffset()),
Adam Balogh0a7592b2018-07-16 09:27:27 +00002079 nonloc::SymbolVal(Pos2.getOffset()),
2080 SVB.getConditionType());
Adam Baloghb03ed5e2018-06-28 10:58:53 +00002081
Adam Balogh0a7592b2018-07-16 09:27:27 +00002082 assert(comparison.getAs<DefinedSVal>() &&
2083 "Symbol comparison must be a `DefinedSVal`");
Adam Baloghb03ed5e2018-06-28 10:58:53 +00002084
Adam Balogh0a7592b2018-07-16 09:27:27 +00002085 auto NewState = State->assume(comparison.castAs<DefinedSVal>(), Equal);
2086 if (const auto CompSym = comparison.getAsSymbol()) {
2087 assert(isa<SymIntExpr>(CompSym) &&
2088 "Symbol comparison must be a `SymIntExpr`");
2089 assert(BinaryOperator::isComparisonOp(
2090 cast<SymIntExpr>(CompSym)->getOpcode()) &&
2091 "Symbol comparison must be a comparison");
2092 return assumeNoOverflow(NewState, cast<SymIntExpr>(CompSym)->getLHS(), 2);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002093 }
2094
Adam Balogh0a7592b2018-07-16 09:27:27 +00002095 return NewState;
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002096}
2097
Adam Balogha6921202018-07-30 08:52:21 +00002098bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont) {
2099 auto RegionMap = State->get<IteratorRegionMap>();
2100 for (const auto Reg : RegionMap) {
2101 if (Reg.second.getContainer() == Cont)
2102 return true;
2103 }
2104
2105 auto SymbolMap = State->get<IteratorSymbolMap>();
2106 for (const auto Sym : SymbolMap) {
2107 if (Sym.second.getContainer() == Cont)
2108 return true;
2109 }
2110
2111 return false;
2112}
2113
Adam Balogh21583b72018-09-10 09:03:22 +00002114bool isBoundThroughLazyCompoundVal(const Environment &Env,
2115 const MemRegion *Reg) {
2116 for (const auto Binding: Env) {
2117 if (const auto LCVal = Binding.second.getAs<nonloc::LazyCompoundVal>()) {
2118 if (LCVal->getRegion() == Reg)
2119 return true;
2120 }
2121 }
2122
2123 return false;
2124}
2125
Adam Balogh2cfbe932018-08-28 08:41:15 +00002126template <typename Condition, typename Process>
2127ProgramStateRef processIteratorPositions(ProgramStateRef State, Condition Cond,
2128 Process Proc) {
2129 auto &RegionMapFactory = State->get_context<IteratorRegionMap>();
2130 auto RegionMap = State->get<IteratorRegionMap>();
2131 bool Changed = false;
2132 for (const auto Reg : RegionMap) {
2133 if (Cond(Reg.second)) {
2134 RegionMap = RegionMapFactory.add(RegionMap, Reg.first, Proc(Reg.second));
2135 Changed = true;
2136 }
2137 }
2138
2139 if (Changed)
2140 State = State->set<IteratorRegionMap>(RegionMap);
2141
2142 auto &SymbolMapFactory = State->get_context<IteratorSymbolMap>();
2143 auto SymbolMap = State->get<IteratorSymbolMap>();
2144 Changed = false;
2145 for (const auto Sym : SymbolMap) {
2146 if (Cond(Sym.second)) {
2147 SymbolMap = SymbolMapFactory.add(SymbolMap, Sym.first, Proc(Sym.second));
2148 Changed = true;
2149 }
2150 }
2151
2152 if (Changed)
2153 State = State->set<IteratorSymbolMap>(SymbolMap);
2154
2155 return State;
2156}
2157
2158ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State,
2159 const MemRegion *Cont) {
2160 auto MatchCont = [&](const IteratorPosition &Pos) {
2161 return Pos.getContainer() == Cont;
2162 };
2163 auto Invalidate = [&](const IteratorPosition &Pos) {
2164 return Pos.invalidate();
2165 };
2166 return processIteratorPositions(State, MatchCont, Invalidate);
2167}
2168
Adam Balogh2e7cb342018-09-10 09:07:47 +00002169ProgramStateRef
2170invalidateAllIteratorPositionsExcept(ProgramStateRef State,
2171 const MemRegion *Cont, SymbolRef Offset,
2172 BinaryOperator::Opcode Opc) {
2173 auto MatchContAndCompare = [&](const IteratorPosition &Pos) {
2174 return Pos.getContainer() == Cont &&
2175 !compare(State, Pos.getOffset(), Offset, Opc);
2176 };
2177 auto Invalidate = [&](const IteratorPosition &Pos) {
2178 return Pos.invalidate();
2179 };
2180 return processIteratorPositions(State, MatchContAndCompare, Invalidate);
2181}
2182
Adam Balogh9a48ba62018-09-10 09:06:31 +00002183ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
2184 SymbolRef Offset,
2185 BinaryOperator::Opcode Opc) {
2186 auto Compare = [&](const IteratorPosition &Pos) {
2187 return compare(State, Pos.getOffset(), Offset, Opc);
2188 };
2189 auto Invalidate = [&](const IteratorPosition &Pos) {
2190 return Pos.invalidate();
2191 };
2192 return processIteratorPositions(State, Compare, Invalidate);
2193}
2194
Adam Balogh2e7cb342018-09-10 09:07:47 +00002195ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
2196 SymbolRef Offset1,
2197 BinaryOperator::Opcode Opc1,
2198 SymbolRef Offset2,
2199 BinaryOperator::Opcode Opc2) {
2200 auto Compare = [&](const IteratorPosition &Pos) {
2201 return compare(State, Pos.getOffset(), Offset1, Opc1) &&
2202 compare(State, Pos.getOffset(), Offset2, Opc2);
2203 };
2204 auto Invalidate = [&](const IteratorPosition &Pos) {
2205 return Pos.invalidate();
2206 };
2207 return processIteratorPositions(State, Compare, Invalidate);
2208}
2209
Adam Balogh6b23b1a2018-09-10 09:04:27 +00002210ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State,
2211 const MemRegion *Cont,
2212 const MemRegion *NewCont) {
2213 auto MatchCont = [&](const IteratorPosition &Pos) {
2214 return Pos.getContainer() == Cont;
2215 };
2216 auto ReAssign = [&](const IteratorPosition &Pos) {
2217 return Pos.reAssign(NewCont);
2218 };
2219 return processIteratorPositions(State, MatchCont, ReAssign);
2220}
2221
2222ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State,
2223 const MemRegion *Cont,
2224 const MemRegion *NewCont,
2225 SymbolRef Offset,
2226 BinaryOperator::Opcode Opc) {
2227 auto MatchContAndCompare = [&](const IteratorPosition &Pos) {
2228 return Pos.getContainer() == Cont &&
2229 !compare(State, Pos.getOffset(), Offset, Opc);
2230 };
2231 auto ReAssign = [&](const IteratorPosition &Pos) {
2232 return Pos.reAssign(NewCont);
2233 };
2234 return processIteratorPositions(State, MatchContAndCompare, ReAssign);
2235}
2236
2237// This function rebases symbolic expression `OldSym + Int` to `NewSym + Int`,
2238// `OldSym - Int` to `NewSym - Int` and `OldSym` to `NewSym` in any iterator
2239// position offsets where `CondSym` is true.
2240ProgramStateRef rebaseSymbolInIteratorPositionsIf(
2241 ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym,
2242 SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc) {
2243 auto LessThanEnd = [&](const IteratorPosition &Pos) {
2244 return compare(State, Pos.getOffset(), CondSym, Opc);
2245 };
2246 auto RebaseSymbol = [&](const IteratorPosition &Pos) {
2247 return Pos.setTo(rebaseSymbol(State, SVB, Pos.getOffset(), OldSym,
2248 NewSym));
2249 };
2250 return processIteratorPositions(State, LessThanEnd, RebaseSymbol);
2251}
2252
2253// This function rebases symbolic expression `OldExpr + Int` to `NewExpr + Int`,
2254// `OldExpr - Int` to `NewExpr - Int` and `OldExpr` to `NewExpr` in expression
2255// `OrigExpr`.
2256SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB,
2257 SymbolRef OrigExpr, SymbolRef OldExpr,
2258 SymbolRef NewSym) {
2259 auto &SymMgr = SVB.getSymbolManager();
2260 auto Diff = SVB.evalBinOpNN(State, BO_Sub, nonloc::SymbolVal(OrigExpr),
2261 nonloc::SymbolVal(OldExpr),
2262 SymMgr.getType(OrigExpr));
2263
2264 const auto DiffInt = Diff.getAs<nonloc::ConcreteInt>();
2265 if (!DiffInt)
2266 return OrigExpr;
2267
2268 return SVB.evalBinOpNN(State, BO_Add, *DiffInt, nonloc::SymbolVal(NewSym),
2269 SymMgr.getType(OrigExpr)).getAsSymbol();
2270}
2271
Adam Baloghb03ed5e2018-06-28 10:58:53 +00002272bool isZero(ProgramStateRef State, const NonLoc &Val) {
2273 auto &BVF = State->getBasicVals();
2274 return compare(State, Val,
2275 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))),
2276 BO_EQ);
2277}
2278
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002279bool isOutOfRange(ProgramStateRef State, const IteratorPosition &Pos) {
2280 const auto *Cont = Pos.getContainer();
2281 const auto *CData = getContainerData(State, Cont);
2282 if (!CData)
2283 return false;
2284
2285 // Out of range means less than the begin symbol or greater or equal to the
2286 // end symbol.
2287
Adam Baloghb03ed5e2018-06-28 10:58:53 +00002288 const auto Beg = CData->getBegin();
2289 if (Beg) {
2290 if (isLess(State, Pos.getOffset(), Beg)) {
2291 return true;
2292 }
2293 }
2294
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002295 const auto End = CData->getEnd();
2296 if (End) {
2297 if (isGreaterOrEqual(State, Pos.getOffset(), End)) {
2298 return true;
2299 }
2300 }
2301
2302 return false;
2303}
2304
Adam Baloghb03ed5e2018-06-28 10:58:53 +00002305bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
2306 return compare(State, Sym1, Sym2, BO_LT);
2307}
2308
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002309bool isGreaterOrEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
2310 return compare(State, Sym1, Sym2, BO_GE);
2311}
2312
2313bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
2314 BinaryOperator::Opcode Opc) {
Adam Baloghb03ed5e2018-06-28 10:58:53 +00002315 return compare(State, nonloc::SymbolVal(Sym1), nonloc::SymbolVal(Sym2), Opc);
2316}
2317
Adam Balogh9a48ba62018-09-10 09:06:31 +00002318
Adam Baloghb03ed5e2018-06-28 10:58:53 +00002319bool compare(ProgramStateRef State, NonLoc NL1, NonLoc NL2,
2320 BinaryOperator::Opcode Opc) {
2321 auto &SVB = State->getStateManager().getSValBuilder();
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002322
2323 const auto comparison =
Adam Balogh0a7592b2018-07-16 09:27:27 +00002324 SVB.evalBinOp(State, Opc, NL1, NL2, SVB.getConditionType());
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002325
Adam Balogh0a7592b2018-07-16 09:27:27 +00002326 assert(comparison.getAs<DefinedSVal>() &&
2327 "Symbol comparison must be a `DefinedSVal`");
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002328
Adam Balogh0a7592b2018-07-16 09:27:27 +00002329 return !State->assume(comparison.castAs<DefinedSVal>(), false);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002330}
2331
2332} // namespace
2333
2334#define REGISTER_CHECKER(name) \
2335 void ento::register##name(CheckerManager &Mgr) { \
2336 auto *checker = Mgr.registerChecker<IteratorChecker>(); \
2337 checker->ChecksEnabled[IteratorChecker::CK_##name] = true; \
2338 checker->CheckNames[IteratorChecker::CK_##name] = \
2339 Mgr.getCurrentCheckName(); \
2340 }
2341
2342REGISTER_CHECKER(IteratorRangeChecker)
Adam Balogh21583b72018-09-10 09:03:22 +00002343REGISTER_CHECKER(MismatchedIteratorChecker)
Adam Balogh2cfbe932018-08-28 08:41:15 +00002344REGISTER_CHECKER(InvalidatedIteratorChecker)