blob: 64bac736bf364d346846ad5a6fe4d8f811b24588 [file] [log] [blame]
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001//===-- IteratorChecker.cpp ---------------------------------------*- C++ -*--//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// Defines a checker for using iterators outside their range (past end). Usage
11// means here dereferencing, incrementing etc.
12//
13//===----------------------------------------------------------------------===//
14//
15// In the code, iterator can be represented as a:
16// * type-I: typedef-ed pointer. Operations over such iterator, such as
17// comparisons or increments, are modeled straightforwardly by the
18// analyzer.
19// * type-II: structure with its method bodies available. Operations over such
20// iterator are inlined by the analyzer, and results of modeling
21// these operations are exposing implementation details of the
22// iterators, which is not necessarily helping.
23// * type-III: completely opaque structure. Operations over such iterator are
24// modeled conservatively, producing conjured symbols everywhere.
25//
26// To handle all these types in a common way we introduce a structure called
27// IteratorPosition which is an abstraction of the position the iterator
28// represents using symbolic expressions. The checker handles all the
29// operations on this structure.
30//
31// Additionally, depending on the circumstances, operators of types II and III
32// can be represented as:
33// * type-IIa, type-IIIa: conjured structure symbols - when returned by value
34// from conservatively evaluated methods such as
35// `.begin()`.
36// * type-IIb, type-IIIb: memory regions of iterator-typed objects, such as
37// variables or temporaries, when the iterator object is
38// currently treated as an lvalue.
39// * type-IIc, type-IIIc: compound values of iterator-typed objects, when the
40// iterator object is treated as an rvalue taken of a
41// particular lvalue, eg. a copy of "type-a" iterator
42// object, or an iterator that existed before the
43// analysis has started.
44//
45// To handle any of these three different representations stored in an SVal we
46// use setter and getters functions which separate the three cases. To store
47// them we use a pointer union of symbol and memory region.
48//
Adam Baloghb03ed5e2018-06-28 10:58:53 +000049// The checker works the following way: We record the begin and the
50// past-end iterator for all containers whenever their `.begin()` and `.end()`
51// are called. Since the Constraint Manager cannot handle such SVals we need
52// to take over its role. We post-check equality and non-equality comparisons
53// and record that the two sides are equal if we are in the 'equal' branch
54// (true-branch for `==` and false-branch for `!=`).
Artem Dergachev8fa639e2017-05-29 15:03:20 +000055//
56// In case of type-I or type-II iterators we get a concrete integer as a result
57// of the comparison (1 or 0) but in case of type-III we only get a Symbol. In
58// this latter case we record the symbol and reload it in evalAssume() and do
59// the propagation there. We also handle (maybe double) negated comparisons
Adam Baloghb03ed5e2018-06-28 10:58:53 +000060// which are represented in the form of (x == 0 or x != 0) where x is the
Artem Dergachev8fa639e2017-05-29 15:03:20 +000061// comparison itself.
Adam Baloghb03ed5e2018-06-28 10:58:53 +000062//
63// Since `SimpleConstraintManager` cannot handle complex symbolic expressions
64// we only use expressions of the format S, S+n or S-n for iterator positions
65// where S is a conjured symbol and n is an unsigned concrete integer. When
66// making an assumption e.g. `S1 + n == S2 + m` we store `S1 - S2 == m - n` as
67// a constraint which we later retrieve when doing an actual comparison.
Artem Dergachev8fa639e2017-05-29 15:03:20 +000068
69#include "ClangSACheckers.h"
Adam Balogh2cfbe932018-08-28 08:41:15 +000070#include "clang/AST/DeclTemplate.h"
Artem Dergachev8fa639e2017-05-29 15:03:20 +000071#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
72#include "clang/StaticAnalyzer/Core/Checker.h"
73#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
74#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
75
Adam Balogh2cfbe932018-08-28 08:41:15 +000076#include <utility>
77
Artem Dergachev8fa639e2017-05-29 15:03:20 +000078using namespace clang;
79using namespace ento;
80
81namespace {
82
83// Abstract position of an iterator. This helps to handle all three kinds
84// of operators in a common way by using a symbolic position.
85struct IteratorPosition {
86private:
87
88 // Container the iterator belongs to
89 const MemRegion *Cont;
90
Adam Balogh2cfbe932018-08-28 08:41:15 +000091 // Whether iterator is valid
92 const bool Valid;
93
Artem Dergachev8fa639e2017-05-29 15:03:20 +000094 // Abstract offset
Adam Baloghb03ed5e2018-06-28 10:58:53 +000095 const SymbolRef Offset;
Artem Dergachev8fa639e2017-05-29 15:03:20 +000096
Adam Balogh2cfbe932018-08-28 08:41:15 +000097 IteratorPosition(const MemRegion *C, bool V, SymbolRef Of)
98 : Cont(C), Valid(V), Offset(Of) {}
Artem Dergachev8fa639e2017-05-29 15:03:20 +000099
100public:
101 const MemRegion *getContainer() const { return Cont; }
Adam Balogh2cfbe932018-08-28 08:41:15 +0000102 bool isValid() const { return Valid; }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000103 SymbolRef getOffset() const { return Offset; }
104
Adam Balogh2cfbe932018-08-28 08:41:15 +0000105 IteratorPosition invalidate() const {
106 return IteratorPosition(Cont, false, Offset);
107 }
108
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000109 static IteratorPosition getPosition(const MemRegion *C, SymbolRef Of) {
Adam Balogh2cfbe932018-08-28 08:41:15 +0000110 return IteratorPosition(C, true, Of);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000111 }
112
113 IteratorPosition setTo(SymbolRef NewOf) const {
Adam Balogh2cfbe932018-08-28 08:41:15 +0000114 return IteratorPosition(Cont, Valid, NewOf);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000115 }
116
117 bool operator==(const IteratorPosition &X) const {
Adam Balogh2cfbe932018-08-28 08:41:15 +0000118 return Cont == X.Cont && Valid == X.Valid && Offset == X.Offset;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000119 }
120
121 bool operator!=(const IteratorPosition &X) const {
Adam Balogh2cfbe932018-08-28 08:41:15 +0000122 return Cont != X.Cont || Valid != X.Valid || Offset != X.Offset;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000123 }
124
125 void Profile(llvm::FoldingSetNodeID &ID) const {
126 ID.AddPointer(Cont);
Adam Balogh2cfbe932018-08-28 08:41:15 +0000127 ID.AddInteger(Valid);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000128 ID.Add(Offset);
129 }
130};
131
132typedef llvm::PointerUnion<const MemRegion *, SymbolRef> RegionOrSymbol;
133
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000134// Structure to record the symbolic begin and end position of a container
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000135struct ContainerData {
136private:
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000137 const SymbolRef Begin, End;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000138
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000139 ContainerData(SymbolRef B, SymbolRef E) : Begin(B), End(E) {}
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000140
141public:
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000142 static ContainerData fromBegin(SymbolRef B) {
143 return ContainerData(B, nullptr);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000144 }
145
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000146 static ContainerData fromEnd(SymbolRef E) {
147 return ContainerData(nullptr, E);
148 }
149
150 SymbolRef getBegin() const { return Begin; }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000151 SymbolRef getEnd() const { return End; }
152
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000153 ContainerData newBegin(SymbolRef B) const { return ContainerData(B, End); }
154
155 ContainerData newEnd(SymbolRef E) const { return ContainerData(Begin, E); }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000156
157 bool operator==(const ContainerData &X) const {
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000158 return Begin == X.Begin && End == X.End;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000159 }
160
161 bool operator!=(const ContainerData &X) const {
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000162 return Begin != X.Begin || End != X.End;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000163 }
164
165 void Profile(llvm::FoldingSetNodeID &ID) const {
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000166 ID.Add(Begin);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000167 ID.Add(End);
168 }
169};
170
171// Structure fo recording iterator comparisons. We needed to retrieve the
172// original comparison expression in assumptions.
173struct IteratorComparison {
174private:
175 RegionOrSymbol Left, Right;
176 bool Equality;
177
178public:
179 IteratorComparison(RegionOrSymbol L, RegionOrSymbol R, bool Eq)
180 : Left(L), Right(R), Equality(Eq) {}
181
182 RegionOrSymbol getLeft() const { return Left; }
183 RegionOrSymbol getRight() const { return Right; }
184 bool isEquality() const { return Equality; }
185 bool operator==(const IteratorComparison &X) const {
186 return Left == X.Left && Right == X.Right && Equality == X.Equality;
187 }
188 bool operator!=(const IteratorComparison &X) const {
189 return Left != X.Left || Right != X.Right || Equality != X.Equality;
190 }
191 void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(Equality); }
192};
193
194class IteratorChecker
195 : public Checker<check::PreCall, check::PostCall,
196 check::PostStmt<MaterializeTemporaryExpr>,
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000197 check::LiveSymbols, check::DeadSymbols,
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000198 eval::Assume> {
199
200 std::unique_ptr<BugType> OutOfRangeBugType;
Adam Balogh2cfbe932018-08-28 08:41:15 +0000201 std::unique_ptr<BugType> InvalidatedBugType;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000202
203 void handleComparison(CheckerContext &C, const SVal &RetVal, const SVal &LVal,
204 const SVal &RVal, OverloadedOperatorKind Op) const;
Adam Balogh2cfbe932018-08-28 08:41:15 +0000205 void verifyAccess(CheckerContext &C, const SVal &Val) const;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000206 void verifyDereference(CheckerContext &C, const SVal &Val) const;
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000207 void handleIncrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
208 bool Postfix) const;
209 void handleDecrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
210 bool Postfix) const;
211 void handleRandomIncrOrDecr(CheckerContext &C, OverloadedOperatorKind Op,
212 const SVal &RetVal, const SVal &LHS,
213 const SVal &RHS) const;
214 void handleBegin(CheckerContext &C, const Expr *CE, const SVal &RetVal,
215 const SVal &Cont) const;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000216 void handleEnd(CheckerContext &C, const Expr *CE, const SVal &RetVal,
217 const SVal &Cont) const;
218 void assignToContainer(CheckerContext &C, const Expr *CE, const SVal &RetVal,
219 const MemRegion *Cont) const;
Adam Balogh2cfbe932018-08-28 08:41:15 +0000220 void handleAssign(CheckerContext &C, const SVal &Cont) const;
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000221 void verifyRandomIncrOrDecr(CheckerContext &C, OverloadedOperatorKind Op,
222 const SVal &RetVal, const SVal &LHS,
223 const SVal &RHS) const;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000224 void reportOutOfRangeBug(const StringRef &Message, const SVal &Val,
225 CheckerContext &C, ExplodedNode *ErrNode) const;
Adam Balogh2cfbe932018-08-28 08:41:15 +0000226 void reportInvalidatedBug(const StringRef &Message, const SVal &Val,
227 CheckerContext &C, ExplodedNode *ErrNode) const;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000228
229public:
230 IteratorChecker();
231
232 enum CheckKind {
233 CK_IteratorRangeChecker,
Adam Balogh2cfbe932018-08-28 08:41:15 +0000234 CK_InvalidatedIteratorChecker,
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000235 CK_NumCheckKinds
236 };
237
238 DefaultBool ChecksEnabled[CK_NumCheckKinds];
239 CheckName CheckNames[CK_NumCheckKinds];
240
241 void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
242 void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
243 void checkPostStmt(const MaterializeTemporaryExpr *MTE,
244 CheckerContext &C) const;
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000245 void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000246 void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
247 ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond,
248 bool Assumption) const;
249};
250} // namespace
251
252REGISTER_MAP_WITH_PROGRAMSTATE(IteratorSymbolMap, SymbolRef, IteratorPosition)
253REGISTER_MAP_WITH_PROGRAMSTATE(IteratorRegionMap, const MemRegion *,
254 IteratorPosition)
255
256REGISTER_MAP_WITH_PROGRAMSTATE(ContainerMap, const MemRegion *, ContainerData)
257
258REGISTER_MAP_WITH_PROGRAMSTATE(IteratorComparisonMap, const SymExpr *,
259 IteratorComparison)
260
261namespace {
262
263bool isIteratorType(const QualType &Type);
264bool isIterator(const CXXRecordDecl *CRD);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000265bool isBeginCall(const FunctionDecl *Func);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000266bool isEndCall(const FunctionDecl *Func);
Adam Balogh2cfbe932018-08-28 08:41:15 +0000267bool isAssignmentOperator(OverloadedOperatorKind OK);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000268bool isSimpleComparisonOperator(OverloadedOperatorKind OK);
Adam Balogh2cfbe932018-08-28 08:41:15 +0000269bool isAccessOperator(OverloadedOperatorKind OK);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000270bool isDereferenceOperator(OverloadedOperatorKind OK);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000271bool isIncrementOperator(OverloadedOperatorKind OK);
272bool isDecrementOperator(OverloadedOperatorKind OK);
273bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000274BinaryOperator::Opcode getOpcode(const SymExpr *SE);
275const RegionOrSymbol getRegionOrSymbol(const SVal &Val);
276const ProgramStateRef processComparison(ProgramStateRef State,
277 RegionOrSymbol LVal,
278 RegionOrSymbol RVal, bool Equal);
279const ProgramStateRef saveComparison(ProgramStateRef State,
280 const SymExpr *Condition, const SVal &LVal,
281 const SVal &RVal, bool Eq);
282const IteratorComparison *loadComparison(ProgramStateRef State,
283 const SymExpr *Condition);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000284SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000285SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000286ProgramStateRef createContainerBegin(ProgramStateRef State,
287 const MemRegion *Cont,
288 const SymbolRef Sym);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000289ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
290 const SymbolRef Sym);
291const IteratorPosition *getIteratorPosition(ProgramStateRef State,
292 const SVal &Val);
293const IteratorPosition *getIteratorPosition(ProgramStateRef State,
294 RegionOrSymbol RegOrSym);
295ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
296 const IteratorPosition &Pos);
297ProgramStateRef setIteratorPosition(ProgramStateRef State,
298 RegionOrSymbol RegOrSym,
299 const IteratorPosition &Pos);
300ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val);
301ProgramStateRef adjustIteratorPosition(ProgramStateRef State,
302 RegionOrSymbol RegOrSym,
303 const IteratorPosition &Pos, bool Equal);
304ProgramStateRef relateIteratorPositions(ProgramStateRef State,
305 const IteratorPosition &Pos1,
306 const IteratorPosition &Pos2,
307 bool Equal);
Adam Balogh2cfbe932018-08-28 08:41:15 +0000308ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State,
309 const MemRegion *Cont);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000310const ContainerData *getContainerData(ProgramStateRef State,
311 const MemRegion *Cont);
312ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
313 const ContainerData &CData);
Adam Balogha6921202018-07-30 08:52:21 +0000314bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000315bool isOutOfRange(ProgramStateRef State, const IteratorPosition &Pos);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000316bool isZero(ProgramStateRef State, const NonLoc &Val);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000317} // namespace
318
319IteratorChecker::IteratorChecker() {
320 OutOfRangeBugType.reset(
321 new BugType(this, "Iterator out of range", "Misuse of STL APIs"));
322 OutOfRangeBugType->setSuppressOnSink(true);
Adam Balogh2cfbe932018-08-28 08:41:15 +0000323 InvalidatedBugType.reset(
324 new BugType(this, "Iterator invalidated", "Misuse of STL APIs"));
325 InvalidatedBugType->setSuppressOnSink(true);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000326}
327
328void IteratorChecker::checkPreCall(const CallEvent &Call,
329 CheckerContext &C) const {
Adam Balogh2cfbe932018-08-28 08:41:15 +0000330 // Check for out of range access or access of invalidated position and
331 // iterator mismatches
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000332 const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
333 if (!Func)
334 return;
335
336 if (Func->isOverloadedOperator()) {
Adam Balogh2cfbe932018-08-28 08:41:15 +0000337 if (ChecksEnabled[CK_InvalidatedIteratorChecker] &&
338 isAccessOperator(Func->getOverloadedOperator())) {
339 // Check for any kind of access of invalidated iterator positions
340 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
341 verifyAccess(C, InstCall->getCXXThisVal());
342 } else {
343 verifyAccess(C, Call.getArgSVal(0));
344 }
345 }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000346 if (ChecksEnabled[CK_IteratorRangeChecker] &&
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000347 isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) {
348 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
349 // Check for out-of-range incrementions and decrementions
350 if (Call.getNumArgs() >= 1) {
351 verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(),
352 Call.getReturnValue(),
353 InstCall->getCXXThisVal(), Call.getArgSVal(0));
354 }
355 } else {
356 if (Call.getNumArgs() >= 2) {
357 verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(),
358 Call.getReturnValue(), Call.getArgSVal(0),
359 Call.getArgSVal(1));
360 }
361 }
362 } else if (ChecksEnabled[CK_IteratorRangeChecker] &&
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000363 isDereferenceOperator(Func->getOverloadedOperator())) {
364 // Check for dereference of out-of-range iterators
365 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
366 verifyDereference(C, InstCall->getCXXThisVal());
367 } else {
368 verifyDereference(C, Call.getArgSVal(0));
369 }
370 }
371 }
372}
373
374void IteratorChecker::checkPostCall(const CallEvent &Call,
375 CheckerContext &C) const {
376 // Record new iterator positions and iterator position changes
377 const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
378 if (!Func)
379 return;
380
381 if (Func->isOverloadedOperator()) {
382 const auto Op = Func->getOverloadedOperator();
Adam Balogh2cfbe932018-08-28 08:41:15 +0000383 if (isAssignmentOperator(Op)) {
384 const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call);
385 handleAssign(C, InstCall->getCXXThisVal());
386 } else if (isSimpleComparisonOperator(Op)) {
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000387 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
388 handleComparison(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
389 Call.getArgSVal(0), Op);
390 } else {
391 handleComparison(C, Call.getReturnValue(), Call.getArgSVal(0),
392 Call.getArgSVal(1), Op);
393 }
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000394 } else if (isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) {
395 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
396 if (Call.getNumArgs() >= 1) {
397 handleRandomIncrOrDecr(C, Func->getOverloadedOperator(),
398 Call.getReturnValue(),
399 InstCall->getCXXThisVal(), Call.getArgSVal(0));
400 }
401 } else {
402 if (Call.getNumArgs() >= 2) {
403 handleRandomIncrOrDecr(C, Func->getOverloadedOperator(),
404 Call.getReturnValue(), Call.getArgSVal(0),
405 Call.getArgSVal(1));
406 }
407 }
408 } else if (isIncrementOperator(Func->getOverloadedOperator())) {
409 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
410 handleIncrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
411 Call.getNumArgs());
412 } else {
413 handleIncrement(C, Call.getReturnValue(), Call.getArgSVal(0),
414 Call.getNumArgs());
415 }
416 } else if (isDecrementOperator(Func->getOverloadedOperator())) {
417 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
418 handleDecrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
419 Call.getNumArgs());
420 } else {
421 handleDecrement(C, Call.getReturnValue(), Call.getArgSVal(0),
422 Call.getNumArgs());
423 }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000424 }
425 } else {
426 const auto *OrigExpr = Call.getOriginExpr();
427 if (!OrigExpr)
428 return;
429
430 if (!isIteratorType(Call.getResultType()))
431 return;
432
433 auto State = C.getState();
434 // Already bound to container?
435 if (getIteratorPosition(State, Call.getReturnValue()))
436 return;
437
438 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000439 if (isBeginCall(Func)) {
440 handleBegin(C, OrigExpr, Call.getReturnValue(),
441 InstCall->getCXXThisVal());
442 return;
443 }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000444 if (isEndCall(Func)) {
445 handleEnd(C, OrigExpr, Call.getReturnValue(),
446 InstCall->getCXXThisVal());
447 return;
448 }
449 }
450
451 // Copy-like and move constructors
452 if (isa<CXXConstructorCall>(&Call) && Call.getNumArgs() == 1) {
453 if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(0))) {
454 State = setIteratorPosition(State, Call.getReturnValue(), *Pos);
455 if (cast<CXXConstructorDecl>(Func)->isMoveConstructor()) {
456 State = removeIteratorPosition(State, Call.getArgSVal(0));
457 }
458 C.addTransition(State);
459 return;
460 }
461 }
462
463 // Assumption: if return value is an iterator which is not yet bound to a
464 // container, then look for the first iterator argument, and
465 // bind the return value to the same container. This approach
466 // works for STL algorithms.
467 // FIXME: Add a more conservative mode
468 for (unsigned i = 0; i < Call.getNumArgs(); ++i) {
469 if (isIteratorType(Call.getArgExpr(i)->getType())) {
470 if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(i))) {
471 assignToContainer(C, OrigExpr, Call.getReturnValue(),
472 Pos->getContainer());
473 return;
474 }
475 }
476 }
477 }
478}
479
480void IteratorChecker::checkPostStmt(const MaterializeTemporaryExpr *MTE,
481 CheckerContext &C) const {
482 /* Transfer iterator state to temporary objects */
483 auto State = C.getState();
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000484 const auto *Pos =
George Karpenkovd703ec92018-01-17 20:27:29 +0000485 getIteratorPosition(State, C.getSVal(MTE->GetTemporaryExpr()));
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000486 if (!Pos)
487 return;
George Karpenkovd703ec92018-01-17 20:27:29 +0000488 State = setIteratorPosition(State, C.getSVal(MTE), *Pos);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000489 C.addTransition(State);
490}
491
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000492void IteratorChecker::checkLiveSymbols(ProgramStateRef State,
493 SymbolReaper &SR) const {
494 // Keep symbolic expressions of iterator positions, container begins and ends
495 // alive
496 auto RegionMap = State->get<IteratorRegionMap>();
497 for (const auto Reg : RegionMap) {
Adam Balogh0a7592b2018-07-16 09:27:27 +0000498 const auto Offset = Reg.second.getOffset();
499 for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
500 if (isa<SymbolData>(*i))
501 SR.markLive(*i);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000502 }
503
504 auto SymbolMap = State->get<IteratorSymbolMap>();
505 for (const auto Sym : SymbolMap) {
Adam Balogh0a7592b2018-07-16 09:27:27 +0000506 const auto Offset = Sym.second.getOffset();
507 for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
508 if (isa<SymbolData>(*i))
509 SR.markLive(*i);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000510 }
511
512 auto ContMap = State->get<ContainerMap>();
513 for (const auto Cont : ContMap) {
514 const auto CData = Cont.second;
515 if (CData.getBegin()) {
516 SR.markLive(CData.getBegin());
517 }
518 if (CData.getEnd()) {
519 SR.markLive(CData.getEnd());
520 }
521 }
522}
523
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000524void IteratorChecker::checkDeadSymbols(SymbolReaper &SR,
525 CheckerContext &C) const {
526 // Cleanup
527 auto State = C.getState();
528
529 auto RegionMap = State->get<IteratorRegionMap>();
530 for (const auto Reg : RegionMap) {
531 if (!SR.isLiveRegion(Reg.first)) {
532 State = State->remove<IteratorRegionMap>(Reg.first);
533 }
534 }
535
536 auto SymbolMap = State->get<IteratorSymbolMap>();
537 for (const auto Sym : SymbolMap) {
538 if (!SR.isLive(Sym.first)) {
539 State = State->remove<IteratorSymbolMap>(Sym.first);
540 }
541 }
542
543 auto ContMap = State->get<ContainerMap>();
544 for (const auto Cont : ContMap) {
545 if (!SR.isLiveRegion(Cont.first)) {
Adam Balogha6921202018-07-30 08:52:21 +0000546 // We must keep the container data while it has live iterators to be able
547 // to compare them to the begin and the end of the container.
548 if (!hasLiveIterators(State, Cont.first)) {
549 State = State->remove<ContainerMap>(Cont.first);
550 }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000551 }
552 }
553
554 auto ComparisonMap = State->get<IteratorComparisonMap>();
555 for (const auto Comp : ComparisonMap) {
556 if (!SR.isLive(Comp.first)) {
557 State = State->remove<IteratorComparisonMap>(Comp.first);
558 }
559 }
Reka Kovacse48ea892018-07-30 16:14:59 +0000560
561 C.addTransition(State);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000562}
563
564ProgramStateRef IteratorChecker::evalAssume(ProgramStateRef State, SVal Cond,
565 bool Assumption) const {
566 // Load recorded comparison and transfer iterator state between sides
567 // according to comparison operator and assumption
568 const auto *SE = Cond.getAsSymExpr();
569 if (!SE)
570 return State;
571
572 auto Opc = getOpcode(SE);
573 if (Opc != BO_EQ && Opc != BO_NE)
574 return State;
575
576 bool Negated = false;
577 const auto *Comp = loadComparison(State, SE);
578 if (!Comp) {
579 // Try negated comparison, which is a SymExpr to 0 integer comparison
580 const auto *SIE = dyn_cast<SymIntExpr>(SE);
581 if (!SIE)
582 return State;
583
584 if (SIE->getRHS() != 0)
585 return State;
586
587 SE = SIE->getLHS();
588 Negated = SIE->getOpcode() == BO_EQ; // Equal to zero means negation
589 Opc = getOpcode(SE);
590 if (Opc != BO_EQ && Opc != BO_NE)
591 return State;
592
593 Comp = loadComparison(State, SE);
594 if (!Comp)
595 return State;
596 }
597
598 return processComparison(State, Comp->getLeft(), Comp->getRight(),
599 (Comp->isEquality() == Assumption) != Negated);
600}
601
602void IteratorChecker::handleComparison(CheckerContext &C, const SVal &RetVal,
603 const SVal &LVal, const SVal &RVal,
604 OverloadedOperatorKind Op) const {
605 // Record the operands and the operator of the comparison for the next
606 // evalAssume, if the result is a symbolic expression. If it is a concrete
607 // value (only one branch is possible), then transfer the state between
608 // the operands according to the operator and the result
609 auto State = C.getState();
610 if (const auto *Condition = RetVal.getAsSymbolicExpression()) {
611 const auto *LPos = getIteratorPosition(State, LVal);
612 const auto *RPos = getIteratorPosition(State, RVal);
613 if (!LPos && !RPos)
614 return;
615 State = saveComparison(State, Condition, LVal, RVal, Op == OO_EqualEqual);
616 C.addTransition(State);
617 } else if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) {
618 if ((State = processComparison(
619 State, getRegionOrSymbol(LVal), getRegionOrSymbol(RVal),
620 (Op == OO_EqualEqual) == (TruthVal->getValue() != 0)))) {
621 C.addTransition(State);
622 } else {
623 C.generateSink(State, C.getPredecessor());
624 }
625 }
626}
627
628void IteratorChecker::verifyDereference(CheckerContext &C,
629 const SVal &Val) const {
630 auto State = C.getState();
631 const auto *Pos = getIteratorPosition(State, Val);
632 if (Pos && isOutOfRange(State, *Pos)) {
Adam Balogh2cfbe932018-08-28 08:41:15 +0000633 auto *N = C.generateNonFatalErrorNode(State);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000634 if (!N)
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000635 return;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000636 reportOutOfRangeBug("Iterator accessed outside of its range.", Val, C, N);
637 }
638}
639
Adam Balogh2cfbe932018-08-28 08:41:15 +0000640void IteratorChecker::verifyAccess(CheckerContext &C, const SVal &Val) const {
641 auto State = C.getState();
642 const auto *Pos = getIteratorPosition(State, Val);
643 if (Pos && !Pos->isValid()) {
644 auto *N = C.generateNonFatalErrorNode(State);
645 if (!N) {
646 return;
647 }
648 reportInvalidatedBug("Invalidated iterator accessed.", Val, C, N);
649 }
650}
651
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000652void IteratorChecker::handleIncrement(CheckerContext &C, const SVal &RetVal,
653 const SVal &Iter, bool Postfix) const {
654 // Increment the symbolic expressions which represents the position of the
655 // iterator
656 auto State = C.getState();
657 const auto *Pos = getIteratorPosition(State, Iter);
658 if (Pos) {
659 auto &SymMgr = C.getSymbolManager();
660 auto &BVF = SymMgr.getBasicVals();
661 auto &SVB = C.getSValBuilder();
662 const auto OldOffset = Pos->getOffset();
663 auto NewOffset =
664 SVB.evalBinOp(State, BO_Add,
665 nonloc::SymbolVal(OldOffset),
666 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
667 SymMgr.getType(OldOffset)).getAsSymbol();
668 auto NewPos = Pos->setTo(NewOffset);
669 State = setIteratorPosition(State, Iter, NewPos);
670 State = setIteratorPosition(State, RetVal, Postfix ? *Pos : NewPos);
671 C.addTransition(State);
672 }
673}
674
675void IteratorChecker::handleDecrement(CheckerContext &C, const SVal &RetVal,
676 const SVal &Iter, bool Postfix) const {
677 // Decrement the symbolic expressions which represents the position of the
678 // iterator
679 auto State = C.getState();
680 const auto *Pos = getIteratorPosition(State, Iter);
681 if (Pos) {
682 auto &SymMgr = C.getSymbolManager();
683 auto &BVF = SymMgr.getBasicVals();
684 auto &SVB = C.getSValBuilder();
685 const auto OldOffset = Pos->getOffset();
686 auto NewOffset =
687 SVB.evalBinOp(State, BO_Sub,
688 nonloc::SymbolVal(OldOffset),
689 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
690 SymMgr.getType(OldOffset)).getAsSymbol();
691 auto NewPos = Pos->setTo(NewOffset);
692 State = setIteratorPosition(State, Iter, NewPos);
693 State = setIteratorPosition(State, RetVal, Postfix ? *Pos : NewPos);
694 C.addTransition(State);
695 }
696}
697
698// This function tells the analyzer's engine that symbols produced by our
699// checker, most notably iterator positions, are relatively small.
700// A distance between items in the container should not be very large.
701// By assuming that it is within around 1/8 of the address space,
702// we can help the analyzer perform operations on these symbols
703// without being afraid of integer overflows.
704// FIXME: Should we provide it as an API, so that all checkers could use it?
705static ProgramStateRef assumeNoOverflow(ProgramStateRef State, SymbolRef Sym,
706 long Scale) {
707 SValBuilder &SVB = State->getStateManager().getSValBuilder();
708 BasicValueFactory &BV = SVB.getBasicValueFactory();
709
710 QualType T = Sym->getType();
711 assert(T->isSignedIntegerOrEnumerationType());
712 APSIntType AT = BV.getAPSIntType(T);
713
714 ProgramStateRef NewState = State;
715
716 llvm::APSInt Max = AT.getMaxValue() / AT.getValue(Scale);
717 SVal IsCappedFromAbove =
718 SVB.evalBinOpNN(State, BO_LE, nonloc::SymbolVal(Sym),
719 nonloc::ConcreteInt(Max), SVB.getConditionType());
720 if (auto DV = IsCappedFromAbove.getAs<DefinedSVal>()) {
721 NewState = NewState->assume(*DV, true);
722 if (!NewState)
723 return State;
724 }
725
726 llvm::APSInt Min = -Max;
727 SVal IsCappedFromBelow =
728 SVB.evalBinOpNN(State, BO_GE, nonloc::SymbolVal(Sym),
729 nonloc::ConcreteInt(Min), SVB.getConditionType());
730 if (auto DV = IsCappedFromBelow.getAs<DefinedSVal>()) {
731 NewState = NewState->assume(*DV, true);
732 if (!NewState)
733 return State;
734 }
735
736 return NewState;
737}
738
739void IteratorChecker::handleRandomIncrOrDecr(CheckerContext &C,
740 OverloadedOperatorKind Op,
741 const SVal &RetVal,
742 const SVal &LHS,
743 const SVal &RHS) const {
744 // Increment or decrement the symbolic expressions which represents the
745 // position of the iterator
746 auto State = C.getState();
747 const auto *Pos = getIteratorPosition(State, LHS);
748 if (!Pos)
749 return;
750
751 const auto *value = &RHS;
752 if (auto loc = RHS.getAs<Loc>()) {
753 const auto val = State->getRawSVal(*loc);
754 value = &val;
755 }
756
757 auto &SymMgr = C.getSymbolManager();
758 auto &SVB = C.getSValBuilder();
759 auto BinOp = (Op == OO_Plus || Op == OO_PlusEqual) ? BO_Add : BO_Sub;
760 const auto OldOffset = Pos->getOffset();
761 SymbolRef NewOffset;
762 if (const auto intValue = value->getAs<nonloc::ConcreteInt>()) {
763 // For concrete integers we can calculate the new position
764 NewOffset = SVB.evalBinOp(State, BinOp, nonloc::SymbolVal(OldOffset),
765 *intValue,
766 SymMgr.getType(OldOffset)).getAsSymbol();
767 } else {
768 // For other symbols create a new symbol to keep expressions simple
769 const auto &LCtx = C.getLocationContext();
770 NewOffset = SymMgr.conjureSymbol(nullptr, LCtx, SymMgr.getType(OldOffset),
771 C.blockCount());
772 State = assumeNoOverflow(State, NewOffset, 4);
773 }
774 auto NewPos = Pos->setTo(NewOffset);
775 auto &TgtVal = (Op == OO_PlusEqual || Op == OO_MinusEqual) ? LHS : RetVal;
776 State = setIteratorPosition(State, TgtVal, NewPos);
777 C.addTransition(State);
778}
779
780void IteratorChecker::verifyRandomIncrOrDecr(CheckerContext &C,
781 OverloadedOperatorKind Op,
782 const SVal &RetVal,
783 const SVal &LHS,
784 const SVal &RHS) const {
785 auto State = C.getState();
786
787 // If the iterator is initially inside its range, then the operation is valid
788 const auto *Pos = getIteratorPosition(State, LHS);
789 if (!Pos || !isOutOfRange(State, *Pos))
790 return;
791
792 auto value = RHS;
793 if (auto loc = RHS.getAs<Loc>()) {
794 value = State->getRawSVal(*loc);
795 }
796
797 // Incremention or decremention by 0 is never bug
798 if (isZero(State, value.castAs<NonLoc>()))
799 return;
800
801 auto &SymMgr = C.getSymbolManager();
802 auto &SVB = C.getSValBuilder();
803 auto BinOp = (Op == OO_Plus || Op == OO_PlusEqual) ? BO_Add : BO_Sub;
804 const auto OldOffset = Pos->getOffset();
805 const auto intValue = value.getAs<nonloc::ConcreteInt>();
806 if (!intValue)
807 return;
808
809 auto NewOffset = SVB.evalBinOp(State, BinOp, nonloc::SymbolVal(OldOffset),
810 *intValue,
811 SymMgr.getType(OldOffset)).getAsSymbol();
812 auto NewPos = Pos->setTo(NewOffset);
813
814 // If out of range, the only valid operation is to step into the range
815 if (isOutOfRange(State, NewPos)) {
816 auto *N = C.generateNonFatalErrorNode(State);
817 if (!N)
818 return;
819 reportOutOfRangeBug("Iterator accessed past its end.", LHS, C, N);
820 }
821}
822
823void IteratorChecker::handleBegin(CheckerContext &C, const Expr *CE,
824 const SVal &RetVal, const SVal &Cont) const {
825 const auto *ContReg = Cont.getAsRegion();
826 if (!ContReg)
827 return;
828
829 while (const auto *CBOR = ContReg->getAs<CXXBaseObjectRegion>()) {
830 ContReg = CBOR->getSuperRegion();
831 }
832
833 // If the container already has a begin symbol then use it. Otherwise first
834 // create a new one.
835 auto State = C.getState();
836 auto BeginSym = getContainerBegin(State, ContReg);
837 if (!BeginSym) {
838 auto &SymMgr = C.getSymbolManager();
839 BeginSym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
840 C.getASTContext().LongTy, C.blockCount());
841 State = assumeNoOverflow(State, BeginSym, 4);
842 State = createContainerBegin(State, ContReg, BeginSym);
843 }
844 State = setIteratorPosition(State, RetVal,
845 IteratorPosition::getPosition(ContReg, BeginSym));
846 C.addTransition(State);
847}
848
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000849void IteratorChecker::handleEnd(CheckerContext &C, const Expr *CE,
850 const SVal &RetVal, const SVal &Cont) const {
851 const auto *ContReg = Cont.getAsRegion();
852 if (!ContReg)
853 return;
854
855 while (const auto *CBOR = ContReg->getAs<CXXBaseObjectRegion>()) {
856 ContReg = CBOR->getSuperRegion();
857 }
858
859 // If the container already has an end symbol then use it. Otherwise first
860 // create a new one.
861 auto State = C.getState();
862 auto EndSym = getContainerEnd(State, ContReg);
863 if (!EndSym) {
864 auto &SymMgr = C.getSymbolManager();
865 EndSym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
866 C.getASTContext().LongTy, C.blockCount());
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000867 State = assumeNoOverflow(State, EndSym, 4);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000868 State = createContainerEnd(State, ContReg, EndSym);
869 }
870 State = setIteratorPosition(State, RetVal,
871 IteratorPosition::getPosition(ContReg, EndSym));
872 C.addTransition(State);
873}
874
875void IteratorChecker::assignToContainer(CheckerContext &C, const Expr *CE,
876 const SVal &RetVal,
877 const MemRegion *Cont) const {
878 while (const auto *CBOR = Cont->getAs<CXXBaseObjectRegion>()) {
879 Cont = CBOR->getSuperRegion();
880 }
881
882 auto State = C.getState();
883 auto &SymMgr = C.getSymbolManager();
884 auto Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
885 C.getASTContext().LongTy, C.blockCount());
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000886 State = assumeNoOverflow(State, Sym, 4);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000887 State = setIteratorPosition(State, RetVal,
888 IteratorPosition::getPosition(Cont, Sym));
889 C.addTransition(State);
890}
891
Adam Balogh2cfbe932018-08-28 08:41:15 +0000892void IteratorChecker::handleAssign(CheckerContext &C, const SVal &Cont) const {
893 const auto *ContReg = Cont.getAsRegion();
894 if (!ContReg)
895 return;
896
897 while (const auto *CBOR = ContReg->getAs<CXXBaseObjectRegion>()) {
898 ContReg = CBOR->getSuperRegion();
899 }
900
901 // Assignment of a new value to a container always invalidates all its
902 // iterators
903 auto State = C.getState();
904 const auto CData = getContainerData(State, ContReg);
905 if (CData) {
906 State = invalidateAllIteratorPositions(State, ContReg);
907 }
908
909 C.addTransition(State);
910}
911
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000912void IteratorChecker::reportOutOfRangeBug(const StringRef &Message,
913 const SVal &Val, CheckerContext &C,
914 ExplodedNode *ErrNode) const {
915 auto R = llvm::make_unique<BugReport>(*OutOfRangeBugType, Message, ErrNode);
916 R->markInteresting(Val);
917 C.emitReport(std::move(R));
918}
919
Adam Balogh2cfbe932018-08-28 08:41:15 +0000920void IteratorChecker::reportInvalidatedBug(const StringRef &Message,
921 const SVal &Val, CheckerContext &C,
922 ExplodedNode *ErrNode) const {
923 auto R = llvm::make_unique<BugReport>(*InvalidatedBugType, Message, ErrNode);
924 R->markInteresting(Val);
925 C.emitReport(std::move(R));
926}
927
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000928namespace {
929
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000930bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000931bool isGreaterOrEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
932bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
933 BinaryOperator::Opcode Opc);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000934bool compare(ProgramStateRef State, NonLoc NL1, NonLoc NL2,
935 BinaryOperator::Opcode Opc);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000936
937bool isIteratorType(const QualType &Type) {
938 if (Type->isPointerType())
939 return true;
940
941 const auto *CRD = Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
942 return isIterator(CRD);
943}
944
945bool isIterator(const CXXRecordDecl *CRD) {
946 if (!CRD)
947 return false;
948
949 const auto Name = CRD->getName();
950 if (!(Name.endswith_lower("iterator") || Name.endswith_lower("iter") ||
951 Name.endswith_lower("it")))
952 return false;
953
954 bool HasCopyCtor = false, HasCopyAssign = true, HasDtor = false,
955 HasPreIncrOp = false, HasPostIncrOp = false, HasDerefOp = false;
956 for (const auto *Method : CRD->methods()) {
957 if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(Method)) {
958 if (Ctor->isCopyConstructor()) {
959 HasCopyCtor = !Ctor->isDeleted() && Ctor->getAccess() == AS_public;
960 }
961 continue;
962 }
963 if (const auto *Dtor = dyn_cast<CXXDestructorDecl>(Method)) {
964 HasDtor = !Dtor->isDeleted() && Dtor->getAccess() == AS_public;
965 continue;
966 }
967 if (Method->isCopyAssignmentOperator()) {
968 HasCopyAssign = !Method->isDeleted() && Method->getAccess() == AS_public;
969 continue;
970 }
971 if (!Method->isOverloadedOperator())
972 continue;
973 const auto OPK = Method->getOverloadedOperator();
974 if (OPK == OO_PlusPlus) {
975 HasPreIncrOp = HasPreIncrOp || (Method->getNumParams() == 0);
976 HasPostIncrOp = HasPostIncrOp || (Method->getNumParams() == 1);
977 continue;
978 }
979 if (OPK == OO_Star) {
980 HasDerefOp = (Method->getNumParams() == 0);
981 continue;
982 }
983 }
984
985 return HasCopyCtor && HasCopyAssign && HasDtor && HasPreIncrOp &&
986 HasPostIncrOp && HasDerefOp;
987}
988
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000989bool isBeginCall(const FunctionDecl *Func) {
990 const auto *IdInfo = Func->getIdentifier();
991 if (!IdInfo)
992 return false;
993 return IdInfo->getName().endswith_lower("begin");
994}
995
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000996bool isEndCall(const FunctionDecl *Func) {
997 const auto *IdInfo = Func->getIdentifier();
998 if (!IdInfo)
999 return false;
1000 return IdInfo->getName().endswith_lower("end");
1001}
1002
Adam Balogh2cfbe932018-08-28 08:41:15 +00001003bool isAssignmentOperator(OverloadedOperatorKind OK) { return OK == OO_Equal; }
1004
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001005bool isSimpleComparisonOperator(OverloadedOperatorKind OK) {
1006 return OK == OO_EqualEqual || OK == OO_ExclaimEqual;
1007}
1008
Adam Balogh2cfbe932018-08-28 08:41:15 +00001009bool isAccessOperator(OverloadedOperatorKind OK) {
1010 return isDereferenceOperator(OK) || isIncrementOperator(OK) ||
1011 isDecrementOperator(OK) || isRandomIncrOrDecrOperator(OK);
1012}
1013
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001014bool isDereferenceOperator(OverloadedOperatorKind OK) {
1015 return OK == OO_Star || OK == OO_Arrow || OK == OO_ArrowStar ||
1016 OK == OO_Subscript;
1017}
1018
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001019bool isIncrementOperator(OverloadedOperatorKind OK) {
1020 return OK == OO_PlusPlus;
1021}
1022
1023bool isDecrementOperator(OverloadedOperatorKind OK) {
1024 return OK == OO_MinusMinus;
1025}
1026
1027bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK) {
1028 return OK == OO_Plus || OK == OO_PlusEqual || OK == OO_Minus ||
1029 OK == OO_MinusEqual;
1030}
1031
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001032BinaryOperator::Opcode getOpcode(const SymExpr *SE) {
1033 if (const auto *BSE = dyn_cast<BinarySymExpr>(SE)) {
1034 return BSE->getOpcode();
1035 } else if (const auto *SC = dyn_cast<SymbolConjured>(SE)) {
Henry Wong073d5f02018-03-20 09:27:02 +00001036 const auto *COE = dyn_cast_or_null<CXXOperatorCallExpr>(SC->getStmt());
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001037 if (!COE)
1038 return BO_Comma; // Extremal value, neither EQ nor NE
1039 if (COE->getOperator() == OO_EqualEqual) {
1040 return BO_EQ;
1041 } else if (COE->getOperator() == OO_ExclaimEqual) {
1042 return BO_NE;
1043 }
1044 return BO_Comma; // Extremal value, neither EQ nor NE
1045 }
1046 return BO_Comma; // Extremal value, neither EQ nor NE
1047}
1048
1049const RegionOrSymbol getRegionOrSymbol(const SVal &Val) {
1050 if (const auto Reg = Val.getAsRegion()) {
1051 return Reg;
1052 } else if (const auto Sym = Val.getAsSymbol()) {
1053 return Sym;
1054 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
1055 return LCVal->getRegion();
1056 }
1057 return RegionOrSymbol();
1058}
1059
1060const ProgramStateRef processComparison(ProgramStateRef State,
1061 RegionOrSymbol LVal,
1062 RegionOrSymbol RVal, bool Equal) {
1063 const auto *LPos = getIteratorPosition(State, LVal);
1064 const auto *RPos = getIteratorPosition(State, RVal);
1065 if (LPos && !RPos) {
1066 State = adjustIteratorPosition(State, RVal, *LPos, Equal);
1067 } else if (!LPos && RPos) {
1068 State = adjustIteratorPosition(State, LVal, *RPos, Equal);
1069 } else if (LPos && RPos) {
1070 State = relateIteratorPositions(State, *LPos, *RPos, Equal);
1071 }
1072 return State;
1073}
1074
1075const ProgramStateRef saveComparison(ProgramStateRef State,
1076 const SymExpr *Condition, const SVal &LVal,
1077 const SVal &RVal, bool Eq) {
1078 const auto Left = getRegionOrSymbol(LVal);
1079 const auto Right = getRegionOrSymbol(RVal);
1080 if (!Left || !Right)
1081 return State;
1082 return State->set<IteratorComparisonMap>(Condition,
1083 IteratorComparison(Left, Right, Eq));
1084}
1085
1086const IteratorComparison *loadComparison(ProgramStateRef State,
1087 const SymExpr *Condition) {
1088 return State->get<IteratorComparisonMap>(Condition);
1089}
1090
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001091SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont) {
1092 const auto *CDataPtr = getContainerData(State, Cont);
1093 if (!CDataPtr)
1094 return nullptr;
1095
1096 return CDataPtr->getBegin();
1097}
1098
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001099SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont) {
1100 const auto *CDataPtr = getContainerData(State, Cont);
1101 if (!CDataPtr)
1102 return nullptr;
1103
1104 return CDataPtr->getEnd();
1105}
1106
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001107ProgramStateRef createContainerBegin(ProgramStateRef State,
1108 const MemRegion *Cont,
1109 const SymbolRef Sym) {
1110 // Only create if it does not exist
1111 const auto *CDataPtr = getContainerData(State, Cont);
1112 if (CDataPtr) {
1113 if (CDataPtr->getBegin()) {
1114 return State;
1115 }
1116 const auto CData = CDataPtr->newBegin(Sym);
1117 return setContainerData(State, Cont, CData);
1118 }
1119 const auto CData = ContainerData::fromBegin(Sym);
1120 return setContainerData(State, Cont, CData);
1121}
1122
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001123ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
1124 const SymbolRef Sym) {
1125 // Only create if it does not exist
1126 const auto *CDataPtr = getContainerData(State, Cont);
1127 if (CDataPtr) {
1128 if (CDataPtr->getEnd()) {
1129 return State;
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001130 }
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001131 const auto CData = CDataPtr->newEnd(Sym);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001132 return setContainerData(State, Cont, CData);
1133 }
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001134 const auto CData = ContainerData::fromEnd(Sym);
1135 return setContainerData(State, Cont, CData);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001136}
1137
1138const ContainerData *getContainerData(ProgramStateRef State,
1139 const MemRegion *Cont) {
1140 return State->get<ContainerMap>(Cont);
1141}
1142
1143ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
1144 const ContainerData &CData) {
1145 return State->set<ContainerMap>(Cont, CData);
1146}
1147
1148const IteratorPosition *getIteratorPosition(ProgramStateRef State,
1149 const SVal &Val) {
1150 if (const auto Reg = Val.getAsRegion()) {
1151 return State->get<IteratorRegionMap>(Reg);
1152 } else if (const auto Sym = Val.getAsSymbol()) {
1153 return State->get<IteratorSymbolMap>(Sym);
1154 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
1155 return State->get<IteratorRegionMap>(LCVal->getRegion());
1156 }
1157 return nullptr;
1158}
1159
1160const IteratorPosition *getIteratorPosition(ProgramStateRef State,
1161 RegionOrSymbol RegOrSym) {
1162 if (RegOrSym.is<const MemRegion *>()) {
1163 return State->get<IteratorRegionMap>(RegOrSym.get<const MemRegion *>());
1164 } else if (RegOrSym.is<SymbolRef>()) {
1165 return State->get<IteratorSymbolMap>(RegOrSym.get<SymbolRef>());
1166 }
1167 return nullptr;
1168}
1169
1170ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
1171 const IteratorPosition &Pos) {
1172 if (const auto Reg = Val.getAsRegion()) {
1173 return State->set<IteratorRegionMap>(Reg, Pos);
1174 } else if (const auto Sym = Val.getAsSymbol()) {
1175 return State->set<IteratorSymbolMap>(Sym, Pos);
1176 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
1177 return State->set<IteratorRegionMap>(LCVal->getRegion(), Pos);
1178 }
1179 return nullptr;
1180}
1181
1182ProgramStateRef setIteratorPosition(ProgramStateRef State,
1183 RegionOrSymbol RegOrSym,
1184 const IteratorPosition &Pos) {
1185 if (RegOrSym.is<const MemRegion *>()) {
1186 return State->set<IteratorRegionMap>(RegOrSym.get<const MemRegion *>(),
1187 Pos);
1188 } else if (RegOrSym.is<SymbolRef>()) {
1189 return State->set<IteratorSymbolMap>(RegOrSym.get<SymbolRef>(), Pos);
1190 }
1191 return nullptr;
1192}
1193
1194ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val) {
1195 if (const auto Reg = Val.getAsRegion()) {
1196 return State->remove<IteratorRegionMap>(Reg);
1197 } else if (const auto Sym = Val.getAsSymbol()) {
1198 return State->remove<IteratorSymbolMap>(Sym);
1199 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
1200 return State->remove<IteratorRegionMap>(LCVal->getRegion());
1201 }
1202 return nullptr;
1203}
1204
1205ProgramStateRef adjustIteratorPosition(ProgramStateRef State,
1206 RegionOrSymbol RegOrSym,
1207 const IteratorPosition &Pos,
1208 bool Equal) {
1209 if (Equal) {
1210 return setIteratorPosition(State, RegOrSym, Pos);
1211 } else {
1212 return State;
1213 }
1214}
1215
1216ProgramStateRef relateIteratorPositions(ProgramStateRef State,
1217 const IteratorPosition &Pos1,
1218 const IteratorPosition &Pos2,
1219 bool Equal) {
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001220 auto &SVB = State->getStateManager().getSValBuilder();
Adam Balogh0a7592b2018-07-16 09:27:27 +00001221
1222 // FIXME: This code should be reworked as follows:
1223 // 1. Subtract the operands using evalBinOp().
1224 // 2. Assume that the result doesn't overflow.
1225 // 3. Compare the result to 0.
1226 // 4. Assume the result of the comparison.
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001227 const auto comparison =
1228 SVB.evalBinOp(State, BO_EQ, nonloc::SymbolVal(Pos1.getOffset()),
Adam Balogh0a7592b2018-07-16 09:27:27 +00001229 nonloc::SymbolVal(Pos2.getOffset()),
1230 SVB.getConditionType());
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001231
Adam Balogh0a7592b2018-07-16 09:27:27 +00001232 assert(comparison.getAs<DefinedSVal>() &&
1233 "Symbol comparison must be a `DefinedSVal`");
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001234
Adam Balogh0a7592b2018-07-16 09:27:27 +00001235 auto NewState = State->assume(comparison.castAs<DefinedSVal>(), Equal);
1236 if (const auto CompSym = comparison.getAsSymbol()) {
1237 assert(isa<SymIntExpr>(CompSym) &&
1238 "Symbol comparison must be a `SymIntExpr`");
1239 assert(BinaryOperator::isComparisonOp(
1240 cast<SymIntExpr>(CompSym)->getOpcode()) &&
1241 "Symbol comparison must be a comparison");
1242 return assumeNoOverflow(NewState, cast<SymIntExpr>(CompSym)->getLHS(), 2);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001243 }
1244
Adam Balogh0a7592b2018-07-16 09:27:27 +00001245 return NewState;
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001246}
1247
Adam Balogha6921202018-07-30 08:52:21 +00001248bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont) {
1249 auto RegionMap = State->get<IteratorRegionMap>();
1250 for (const auto Reg : RegionMap) {
1251 if (Reg.second.getContainer() == Cont)
1252 return true;
1253 }
1254
1255 auto SymbolMap = State->get<IteratorSymbolMap>();
1256 for (const auto Sym : SymbolMap) {
1257 if (Sym.second.getContainer() == Cont)
1258 return true;
1259 }
1260
1261 return false;
1262}
1263
Adam Balogh2cfbe932018-08-28 08:41:15 +00001264template <typename Condition, typename Process>
1265ProgramStateRef processIteratorPositions(ProgramStateRef State, Condition Cond,
1266 Process Proc) {
1267 auto &RegionMapFactory = State->get_context<IteratorRegionMap>();
1268 auto RegionMap = State->get<IteratorRegionMap>();
1269 bool Changed = false;
1270 for (const auto Reg : RegionMap) {
1271 if (Cond(Reg.second)) {
1272 RegionMap = RegionMapFactory.add(RegionMap, Reg.first, Proc(Reg.second));
1273 Changed = true;
1274 }
1275 }
1276
1277 if (Changed)
1278 State = State->set<IteratorRegionMap>(RegionMap);
1279
1280 auto &SymbolMapFactory = State->get_context<IteratorSymbolMap>();
1281 auto SymbolMap = State->get<IteratorSymbolMap>();
1282 Changed = false;
1283 for (const auto Sym : SymbolMap) {
1284 if (Cond(Sym.second)) {
1285 SymbolMap = SymbolMapFactory.add(SymbolMap, Sym.first, Proc(Sym.second));
1286 Changed = true;
1287 }
1288 }
1289
1290 if (Changed)
1291 State = State->set<IteratorSymbolMap>(SymbolMap);
1292
1293 return State;
1294}
1295
1296ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State,
1297 const MemRegion *Cont) {
1298 auto MatchCont = [&](const IteratorPosition &Pos) {
1299 return Pos.getContainer() == Cont;
1300 };
1301 auto Invalidate = [&](const IteratorPosition &Pos) {
1302 return Pos.invalidate();
1303 };
1304 return processIteratorPositions(State, MatchCont, Invalidate);
1305}
1306
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001307bool isZero(ProgramStateRef State, const NonLoc &Val) {
1308 auto &BVF = State->getBasicVals();
1309 return compare(State, Val,
1310 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))),
1311 BO_EQ);
1312}
1313
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001314bool isOutOfRange(ProgramStateRef State, const IteratorPosition &Pos) {
1315 const auto *Cont = Pos.getContainer();
1316 const auto *CData = getContainerData(State, Cont);
1317 if (!CData)
1318 return false;
1319
1320 // Out of range means less than the begin symbol or greater or equal to the
1321 // end symbol.
1322
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001323 const auto Beg = CData->getBegin();
1324 if (Beg) {
1325 if (isLess(State, Pos.getOffset(), Beg)) {
1326 return true;
1327 }
1328 }
1329
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001330 const auto End = CData->getEnd();
1331 if (End) {
1332 if (isGreaterOrEqual(State, Pos.getOffset(), End)) {
1333 return true;
1334 }
1335 }
1336
1337 return false;
1338}
1339
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001340bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
1341 return compare(State, Sym1, Sym2, BO_LT);
1342}
1343
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001344bool isGreaterOrEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
1345 return compare(State, Sym1, Sym2, BO_GE);
1346}
1347
1348bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
1349 BinaryOperator::Opcode Opc) {
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001350 return compare(State, nonloc::SymbolVal(Sym1), nonloc::SymbolVal(Sym2), Opc);
1351}
1352
1353bool compare(ProgramStateRef State, NonLoc NL1, NonLoc NL2,
1354 BinaryOperator::Opcode Opc) {
1355 auto &SVB = State->getStateManager().getSValBuilder();
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001356
1357 const auto comparison =
Adam Balogh0a7592b2018-07-16 09:27:27 +00001358 SVB.evalBinOp(State, Opc, NL1, NL2, SVB.getConditionType());
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001359
Adam Balogh0a7592b2018-07-16 09:27:27 +00001360 assert(comparison.getAs<DefinedSVal>() &&
1361 "Symbol comparison must be a `DefinedSVal`");
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001362
Adam Balogh0a7592b2018-07-16 09:27:27 +00001363 return !State->assume(comparison.castAs<DefinedSVal>(), false);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001364}
1365
1366} // namespace
1367
1368#define REGISTER_CHECKER(name) \
1369 void ento::register##name(CheckerManager &Mgr) { \
1370 auto *checker = Mgr.registerChecker<IteratorChecker>(); \
1371 checker->ChecksEnabled[IteratorChecker::CK_##name] = true; \
1372 checker->CheckNames[IteratorChecker::CK_##name] = \
1373 Mgr.getCurrentCheckName(); \
1374 }
1375
1376REGISTER_CHECKER(IteratorRangeChecker)
Adam Balogh2cfbe932018-08-28 08:41:15 +00001377REGISTER_CHECKER(InvalidatedIteratorChecker)