blob: fc713bc4999f27fffd6348dff0256c4bff140bff [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"
70#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
71#include "clang/StaticAnalyzer/Core/Checker.h"
72#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
73#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
74
75using namespace clang;
76using namespace ento;
77
78namespace {
79
80// Abstract position of an iterator. This helps to handle all three kinds
81// of operators in a common way by using a symbolic position.
82struct IteratorPosition {
83private:
84
85 // Container the iterator belongs to
86 const MemRegion *Cont;
87
88 // Abstract offset
Adam Baloghb03ed5e2018-06-28 10:58:53 +000089 const SymbolRef Offset;
Artem Dergachev8fa639e2017-05-29 15:03:20 +000090
91 IteratorPosition(const MemRegion *C, SymbolRef Of)
92 : Cont(C), Offset(Of) {}
93
94public:
95 const MemRegion *getContainer() const { return Cont; }
96 SymbolRef getOffset() const { return Offset; }
97
98 static IteratorPosition getPosition(const MemRegion *C, SymbolRef Of) {
99 return IteratorPosition(C, Of);
100 }
101
102 IteratorPosition setTo(SymbolRef NewOf) const {
103 return IteratorPosition(Cont, NewOf);
104 }
105
106 bool operator==(const IteratorPosition &X) const {
107 return Cont == X.Cont && Offset == X.Offset;
108 }
109
110 bool operator!=(const IteratorPosition &X) const {
111 return Cont != X.Cont || Offset != X.Offset;
112 }
113
114 void Profile(llvm::FoldingSetNodeID &ID) const {
115 ID.AddPointer(Cont);
116 ID.Add(Offset);
117 }
118};
119
120typedef llvm::PointerUnion<const MemRegion *, SymbolRef> RegionOrSymbol;
121
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000122// Structure to record the symbolic begin and end position of a container
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000123struct ContainerData {
124private:
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000125 const SymbolRef Begin, End;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000126
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000127 ContainerData(SymbolRef B, SymbolRef E) : Begin(B), End(E) {}
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000128
129public:
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000130 static ContainerData fromBegin(SymbolRef B) {
131 return ContainerData(B, nullptr);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000132 }
133
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000134 static ContainerData fromEnd(SymbolRef E) {
135 return ContainerData(nullptr, E);
136 }
137
138 SymbolRef getBegin() const { return Begin; }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000139 SymbolRef getEnd() const { return End; }
140
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000141 ContainerData newBegin(SymbolRef B) const { return ContainerData(B, End); }
142
143 ContainerData newEnd(SymbolRef E) const { return ContainerData(Begin, E); }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000144
145 bool operator==(const ContainerData &X) const {
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000146 return Begin == X.Begin && End == X.End;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000147 }
148
149 bool operator!=(const ContainerData &X) const {
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000150 return Begin != X.Begin || End != X.End;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000151 }
152
153 void Profile(llvm::FoldingSetNodeID &ID) const {
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000154 ID.Add(Begin);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000155 ID.Add(End);
156 }
157};
158
159// Structure fo recording iterator comparisons. We needed to retrieve the
160// original comparison expression in assumptions.
161struct IteratorComparison {
162private:
163 RegionOrSymbol Left, Right;
164 bool Equality;
165
166public:
167 IteratorComparison(RegionOrSymbol L, RegionOrSymbol R, bool Eq)
168 : Left(L), Right(R), Equality(Eq) {}
169
170 RegionOrSymbol getLeft() const { return Left; }
171 RegionOrSymbol getRight() const { return Right; }
172 bool isEquality() const { return Equality; }
173 bool operator==(const IteratorComparison &X) const {
174 return Left == X.Left && Right == X.Right && Equality == X.Equality;
175 }
176 bool operator!=(const IteratorComparison &X) const {
177 return Left != X.Left || Right != X.Right || Equality != X.Equality;
178 }
179 void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(Equality); }
180};
181
182class IteratorChecker
183 : public Checker<check::PreCall, check::PostCall,
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000184 check::PreStmt<CXXOperatorCallExpr>,
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000185 check::PostStmt<MaterializeTemporaryExpr>,
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000186 check::LiveSymbols, check::DeadSymbols,
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000187 eval::Assume> {
188
189 std::unique_ptr<BugType> OutOfRangeBugType;
190
191 void handleComparison(CheckerContext &C, const SVal &RetVal, const SVal &LVal,
192 const SVal &RVal, OverloadedOperatorKind Op) const;
193 void verifyDereference(CheckerContext &C, const SVal &Val) const;
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000194 void handleIncrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
195 bool Postfix) const;
196 void handleDecrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
197 bool Postfix) const;
198 void handleRandomIncrOrDecr(CheckerContext &C, OverloadedOperatorKind Op,
199 const SVal &RetVal, const SVal &LHS,
200 const SVal &RHS) const;
201 void handleBegin(CheckerContext &C, const Expr *CE, const SVal &RetVal,
202 const SVal &Cont) const;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000203 void handleEnd(CheckerContext &C, const Expr *CE, const SVal &RetVal,
204 const SVal &Cont) const;
205 void assignToContainer(CheckerContext &C, const Expr *CE, const SVal &RetVal,
206 const MemRegion *Cont) const;
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000207 void verifyRandomIncrOrDecr(CheckerContext &C, OverloadedOperatorKind Op,
208 const SVal &RetVal, const SVal &LHS,
209 const SVal &RHS) const;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000210 void reportOutOfRangeBug(const StringRef &Message, const SVal &Val,
211 CheckerContext &C, ExplodedNode *ErrNode) const;
212
213public:
214 IteratorChecker();
215
216 enum CheckKind {
217 CK_IteratorRangeChecker,
218 CK_NumCheckKinds
219 };
220
221 DefaultBool ChecksEnabled[CK_NumCheckKinds];
222 CheckName CheckNames[CK_NumCheckKinds];
223
224 void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
225 void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000226 void checkPreStmt(const CXXOperatorCallExpr *COCE, CheckerContext &C) const;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000227 void checkPostStmt(const MaterializeTemporaryExpr *MTE,
228 CheckerContext &C) const;
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000229 void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000230 void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
231 ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond,
232 bool Assumption) const;
233};
234} // namespace
235
236REGISTER_MAP_WITH_PROGRAMSTATE(IteratorSymbolMap, SymbolRef, IteratorPosition)
237REGISTER_MAP_WITH_PROGRAMSTATE(IteratorRegionMap, const MemRegion *,
238 IteratorPosition)
239
240REGISTER_MAP_WITH_PROGRAMSTATE(ContainerMap, const MemRegion *, ContainerData)
241
242REGISTER_MAP_WITH_PROGRAMSTATE(IteratorComparisonMap, const SymExpr *,
243 IteratorComparison)
244
245namespace {
246
247bool isIteratorType(const QualType &Type);
248bool isIterator(const CXXRecordDecl *CRD);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000249bool isBeginCall(const FunctionDecl *Func);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000250bool isEndCall(const FunctionDecl *Func);
251bool isSimpleComparisonOperator(OverloadedOperatorKind OK);
252bool isDereferenceOperator(OverloadedOperatorKind OK);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000253bool isIncrementOperator(OverloadedOperatorKind OK);
254bool isDecrementOperator(OverloadedOperatorKind OK);
255bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000256BinaryOperator::Opcode getOpcode(const SymExpr *SE);
257const RegionOrSymbol getRegionOrSymbol(const SVal &Val);
258const ProgramStateRef processComparison(ProgramStateRef State,
259 RegionOrSymbol LVal,
260 RegionOrSymbol RVal, bool Equal);
261const ProgramStateRef saveComparison(ProgramStateRef State,
262 const SymExpr *Condition, const SVal &LVal,
263 const SVal &RVal, bool Eq);
264const IteratorComparison *loadComparison(ProgramStateRef State,
265 const SymExpr *Condition);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000266SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000267SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000268ProgramStateRef createContainerBegin(ProgramStateRef State,
269 const MemRegion *Cont,
270 const SymbolRef Sym);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000271ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
272 const SymbolRef Sym);
273const IteratorPosition *getIteratorPosition(ProgramStateRef State,
274 const SVal &Val);
275const IteratorPosition *getIteratorPosition(ProgramStateRef State,
276 RegionOrSymbol RegOrSym);
277ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
278 const IteratorPosition &Pos);
279ProgramStateRef setIteratorPosition(ProgramStateRef State,
280 RegionOrSymbol RegOrSym,
281 const IteratorPosition &Pos);
282ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val);
283ProgramStateRef adjustIteratorPosition(ProgramStateRef State,
284 RegionOrSymbol RegOrSym,
285 const IteratorPosition &Pos, bool Equal);
286ProgramStateRef relateIteratorPositions(ProgramStateRef State,
287 const IteratorPosition &Pos1,
288 const IteratorPosition &Pos2,
289 bool Equal);
290const ContainerData *getContainerData(ProgramStateRef State,
291 const MemRegion *Cont);
292ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
293 const ContainerData &CData);
Adam Balogha6921202018-07-30 08:52:21 +0000294bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000295bool isOutOfRange(ProgramStateRef State, const IteratorPosition &Pos);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000296bool isZero(ProgramStateRef State, const NonLoc &Val);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000297} // namespace
298
299IteratorChecker::IteratorChecker() {
300 OutOfRangeBugType.reset(
301 new BugType(this, "Iterator out of range", "Misuse of STL APIs"));
302 OutOfRangeBugType->setSuppressOnSink(true);
303}
304
305void IteratorChecker::checkPreCall(const CallEvent &Call,
306 CheckerContext &C) const {
307 // Check for out of range access
308 const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
309 if (!Func)
310 return;
311
312 if (Func->isOverloadedOperator()) {
313 if (ChecksEnabled[CK_IteratorRangeChecker] &&
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000314 isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) {
315 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
316 // Check for out-of-range incrementions and decrementions
317 if (Call.getNumArgs() >= 1) {
318 verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(),
319 Call.getReturnValue(),
320 InstCall->getCXXThisVal(), Call.getArgSVal(0));
321 }
322 } else {
323 if (Call.getNumArgs() >= 2) {
324 verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(),
325 Call.getReturnValue(), Call.getArgSVal(0),
326 Call.getArgSVal(1));
327 }
328 }
329 } else if (ChecksEnabled[CK_IteratorRangeChecker] &&
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000330 isDereferenceOperator(Func->getOverloadedOperator())) {
331 // Check for dereference of out-of-range iterators
332 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
333 verifyDereference(C, InstCall->getCXXThisVal());
334 } else {
335 verifyDereference(C, Call.getArgSVal(0));
336 }
337 }
338 }
339}
340
341void IteratorChecker::checkPostCall(const CallEvent &Call,
342 CheckerContext &C) const {
343 // Record new iterator positions and iterator position changes
344 const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
345 if (!Func)
346 return;
347
348 if (Func->isOverloadedOperator()) {
349 const auto Op = Func->getOverloadedOperator();
350 if (isSimpleComparisonOperator(Op)) {
351 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
352 handleComparison(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
353 Call.getArgSVal(0), Op);
354 } else {
355 handleComparison(C, Call.getReturnValue(), Call.getArgSVal(0),
356 Call.getArgSVal(1), Op);
357 }
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000358 } else if (isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) {
359 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
360 if (Call.getNumArgs() >= 1) {
361 handleRandomIncrOrDecr(C, Func->getOverloadedOperator(),
362 Call.getReturnValue(),
363 InstCall->getCXXThisVal(), Call.getArgSVal(0));
364 }
365 } else {
366 if (Call.getNumArgs() >= 2) {
367 handleRandomIncrOrDecr(C, Func->getOverloadedOperator(),
368 Call.getReturnValue(), Call.getArgSVal(0),
369 Call.getArgSVal(1));
370 }
371 }
372 } else if (isIncrementOperator(Func->getOverloadedOperator())) {
373 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
374 handleIncrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
375 Call.getNumArgs());
376 } else {
377 handleIncrement(C, Call.getReturnValue(), Call.getArgSVal(0),
378 Call.getNumArgs());
379 }
380 } else if (isDecrementOperator(Func->getOverloadedOperator())) {
381 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
382 handleDecrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
383 Call.getNumArgs());
384 } else {
385 handleDecrement(C, Call.getReturnValue(), Call.getArgSVal(0),
386 Call.getNumArgs());
387 }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000388 }
389 } else {
390 const auto *OrigExpr = Call.getOriginExpr();
391 if (!OrigExpr)
392 return;
393
394 if (!isIteratorType(Call.getResultType()))
395 return;
396
397 auto State = C.getState();
398 // Already bound to container?
399 if (getIteratorPosition(State, Call.getReturnValue()))
400 return;
401
402 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000403 if (isBeginCall(Func)) {
404 handleBegin(C, OrigExpr, Call.getReturnValue(),
405 InstCall->getCXXThisVal());
406 return;
407 }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000408 if (isEndCall(Func)) {
409 handleEnd(C, OrigExpr, Call.getReturnValue(),
410 InstCall->getCXXThisVal());
411 return;
412 }
413 }
414
415 // Copy-like and move constructors
416 if (isa<CXXConstructorCall>(&Call) && Call.getNumArgs() == 1) {
417 if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(0))) {
418 State = setIteratorPosition(State, Call.getReturnValue(), *Pos);
419 if (cast<CXXConstructorDecl>(Func)->isMoveConstructor()) {
420 State = removeIteratorPosition(State, Call.getArgSVal(0));
421 }
422 C.addTransition(State);
423 return;
424 }
425 }
426
427 // Assumption: if return value is an iterator which is not yet bound to a
428 // container, then look for the first iterator argument, and
429 // bind the return value to the same container. This approach
430 // works for STL algorithms.
431 // FIXME: Add a more conservative mode
432 for (unsigned i = 0; i < Call.getNumArgs(); ++i) {
433 if (isIteratorType(Call.getArgExpr(i)->getType())) {
434 if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(i))) {
435 assignToContainer(C, OrigExpr, Call.getReturnValue(),
436 Pos->getContainer());
437 return;
438 }
439 }
440 }
441 }
442}
443
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000444void IteratorChecker::checkPreStmt(const CXXOperatorCallExpr *COCE,
445 CheckerContext &C) const {
446 const auto *ThisExpr = COCE->getArg(0);
447
448 auto State = C.getState();
449 const auto *LCtx = C.getLocationContext();
450
451 const auto CurrentThis = State->getSVal(ThisExpr, LCtx);
452 if (const auto *Reg = CurrentThis.getAsRegion()) {
453 if (!Reg->getAs<CXXTempObjectRegion>())
454 return;
455 const auto OldState = C.getPredecessor()->getFirstPred()->getState();
456 const auto OldThis = OldState->getSVal(ThisExpr, LCtx);
457 // FIXME: This solution is unreliable. It may happen that another checker
458 // subscribes to the pre-statement check of `CXXOperatorCallExpr`
459 // and adds a transition before us. The proper fix is to make the
460 // CFG provide a `ConstructionContext` for the `CXXOperatorCallExpr`,
461 // which would turn the corresponding `CFGStmt` element into a
462 // `CFGCXXRecordTypedCall` element, which will allow `ExprEngine` to
463 // foresee that the `begin()`/`end()` call constructs the object
464 // directly in the temporary region that `CXXOperatorCallExpr` takes
465 // as its implicit object argument.
466 const auto *Pos = getIteratorPosition(OldState, OldThis);
467 if (!Pos)
468 return;
469 State = setIteratorPosition(State, CurrentThis, *Pos);
470 C.addTransition(State);
471 }
472}
473
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000474void IteratorChecker::checkPostStmt(const MaterializeTemporaryExpr *MTE,
475 CheckerContext &C) const {
476 /* Transfer iterator state to temporary objects */
477 auto State = C.getState();
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000478 const auto *Pos =
George Karpenkovd703ec92018-01-17 20:27:29 +0000479 getIteratorPosition(State, C.getSVal(MTE->GetTemporaryExpr()));
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000480 if (!Pos)
481 return;
George Karpenkovd703ec92018-01-17 20:27:29 +0000482 State = setIteratorPosition(State, C.getSVal(MTE), *Pos);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000483 C.addTransition(State);
484}
485
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000486void IteratorChecker::checkLiveSymbols(ProgramStateRef State,
487 SymbolReaper &SR) const {
488 // Keep symbolic expressions of iterator positions, container begins and ends
489 // alive
490 auto RegionMap = State->get<IteratorRegionMap>();
491 for (const auto Reg : RegionMap) {
Adam Balogh0a7592b2018-07-16 09:27:27 +0000492 const auto Offset = Reg.second.getOffset();
493 for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
494 if (isa<SymbolData>(*i))
495 SR.markLive(*i);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000496 }
497
498 auto SymbolMap = State->get<IteratorSymbolMap>();
499 for (const auto Sym : SymbolMap) {
Adam Balogh0a7592b2018-07-16 09:27:27 +0000500 const auto Offset = Sym.second.getOffset();
501 for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
502 if (isa<SymbolData>(*i))
503 SR.markLive(*i);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000504 }
505
506 auto ContMap = State->get<ContainerMap>();
507 for (const auto Cont : ContMap) {
508 const auto CData = Cont.second;
509 if (CData.getBegin()) {
510 SR.markLive(CData.getBegin());
511 }
512 if (CData.getEnd()) {
513 SR.markLive(CData.getEnd());
514 }
515 }
516}
517
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000518void IteratorChecker::checkDeadSymbols(SymbolReaper &SR,
519 CheckerContext &C) const {
520 // Cleanup
521 auto State = C.getState();
522
523 auto RegionMap = State->get<IteratorRegionMap>();
524 for (const auto Reg : RegionMap) {
525 if (!SR.isLiveRegion(Reg.first)) {
526 State = State->remove<IteratorRegionMap>(Reg.first);
527 }
528 }
529
530 auto SymbolMap = State->get<IteratorSymbolMap>();
531 for (const auto Sym : SymbolMap) {
532 if (!SR.isLive(Sym.first)) {
533 State = State->remove<IteratorSymbolMap>(Sym.first);
534 }
535 }
536
537 auto ContMap = State->get<ContainerMap>();
538 for (const auto Cont : ContMap) {
539 if (!SR.isLiveRegion(Cont.first)) {
Adam Balogha6921202018-07-30 08:52:21 +0000540 // We must keep the container data while it has live iterators to be able
541 // to compare them to the begin and the end of the container.
542 if (!hasLiveIterators(State, Cont.first)) {
543 State = State->remove<ContainerMap>(Cont.first);
544 }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000545 }
546 }
547
548 auto ComparisonMap = State->get<IteratorComparisonMap>();
549 for (const auto Comp : ComparisonMap) {
550 if (!SR.isLive(Comp.first)) {
551 State = State->remove<IteratorComparisonMap>(Comp.first);
552 }
553 }
554}
555
556ProgramStateRef IteratorChecker::evalAssume(ProgramStateRef State, SVal Cond,
557 bool Assumption) const {
558 // Load recorded comparison and transfer iterator state between sides
559 // according to comparison operator and assumption
560 const auto *SE = Cond.getAsSymExpr();
561 if (!SE)
562 return State;
563
564 auto Opc = getOpcode(SE);
565 if (Opc != BO_EQ && Opc != BO_NE)
566 return State;
567
568 bool Negated = false;
569 const auto *Comp = loadComparison(State, SE);
570 if (!Comp) {
571 // Try negated comparison, which is a SymExpr to 0 integer comparison
572 const auto *SIE = dyn_cast<SymIntExpr>(SE);
573 if (!SIE)
574 return State;
575
576 if (SIE->getRHS() != 0)
577 return State;
578
579 SE = SIE->getLHS();
580 Negated = SIE->getOpcode() == BO_EQ; // Equal to zero means negation
581 Opc = getOpcode(SE);
582 if (Opc != BO_EQ && Opc != BO_NE)
583 return State;
584
585 Comp = loadComparison(State, SE);
586 if (!Comp)
587 return State;
588 }
589
590 return processComparison(State, Comp->getLeft(), Comp->getRight(),
591 (Comp->isEquality() == Assumption) != Negated);
592}
593
594void IteratorChecker::handleComparison(CheckerContext &C, const SVal &RetVal,
595 const SVal &LVal, const SVal &RVal,
596 OverloadedOperatorKind Op) const {
597 // Record the operands and the operator of the comparison for the next
598 // evalAssume, if the result is a symbolic expression. If it is a concrete
599 // value (only one branch is possible), then transfer the state between
600 // the operands according to the operator and the result
601 auto State = C.getState();
602 if (const auto *Condition = RetVal.getAsSymbolicExpression()) {
603 const auto *LPos = getIteratorPosition(State, LVal);
604 const auto *RPos = getIteratorPosition(State, RVal);
605 if (!LPos && !RPos)
606 return;
607 State = saveComparison(State, Condition, LVal, RVal, Op == OO_EqualEqual);
608 C.addTransition(State);
609 } else if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) {
610 if ((State = processComparison(
611 State, getRegionOrSymbol(LVal), getRegionOrSymbol(RVal),
612 (Op == OO_EqualEqual) == (TruthVal->getValue() != 0)))) {
613 C.addTransition(State);
614 } else {
615 C.generateSink(State, C.getPredecessor());
616 }
617 }
618}
619
620void IteratorChecker::verifyDereference(CheckerContext &C,
621 const SVal &Val) const {
622 auto State = C.getState();
623 const auto *Pos = getIteratorPosition(State, Val);
624 if (Pos && isOutOfRange(State, *Pos)) {
625 // If I do not put a tag here, some range tests will fail
626 static CheckerProgramPointTag Tag("IteratorRangeChecker",
627 "IteratorOutOfRange");
628 auto *N = C.generateNonFatalErrorNode(State, &Tag);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000629 if (!N)
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000630 return;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000631 reportOutOfRangeBug("Iterator accessed outside of its range.", Val, C, N);
632 }
633}
634
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000635void IteratorChecker::handleIncrement(CheckerContext &C, const SVal &RetVal,
636 const SVal &Iter, bool Postfix) const {
637 // Increment the symbolic expressions which represents the position of the
638 // iterator
639 auto State = C.getState();
640 const auto *Pos = getIteratorPosition(State, Iter);
641 if (Pos) {
642 auto &SymMgr = C.getSymbolManager();
643 auto &BVF = SymMgr.getBasicVals();
644 auto &SVB = C.getSValBuilder();
645 const auto OldOffset = Pos->getOffset();
646 auto NewOffset =
647 SVB.evalBinOp(State, BO_Add,
648 nonloc::SymbolVal(OldOffset),
649 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
650 SymMgr.getType(OldOffset)).getAsSymbol();
651 auto NewPos = Pos->setTo(NewOffset);
652 State = setIteratorPosition(State, Iter, NewPos);
653 State = setIteratorPosition(State, RetVal, Postfix ? *Pos : NewPos);
654 C.addTransition(State);
655 }
656}
657
658void IteratorChecker::handleDecrement(CheckerContext &C, const SVal &RetVal,
659 const SVal &Iter, bool Postfix) const {
660 // Decrement the symbolic expressions which represents the position of the
661 // iterator
662 auto State = C.getState();
663 const auto *Pos = getIteratorPosition(State, Iter);
664 if (Pos) {
665 auto &SymMgr = C.getSymbolManager();
666 auto &BVF = SymMgr.getBasicVals();
667 auto &SVB = C.getSValBuilder();
668 const auto OldOffset = Pos->getOffset();
669 auto NewOffset =
670 SVB.evalBinOp(State, BO_Sub,
671 nonloc::SymbolVal(OldOffset),
672 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
673 SymMgr.getType(OldOffset)).getAsSymbol();
674 auto NewPos = Pos->setTo(NewOffset);
675 State = setIteratorPosition(State, Iter, NewPos);
676 State = setIteratorPosition(State, RetVal, Postfix ? *Pos : NewPos);
677 C.addTransition(State);
678 }
679}
680
681// This function tells the analyzer's engine that symbols produced by our
682// checker, most notably iterator positions, are relatively small.
683// A distance between items in the container should not be very large.
684// By assuming that it is within around 1/8 of the address space,
685// we can help the analyzer perform operations on these symbols
686// without being afraid of integer overflows.
687// FIXME: Should we provide it as an API, so that all checkers could use it?
688static ProgramStateRef assumeNoOverflow(ProgramStateRef State, SymbolRef Sym,
689 long Scale) {
690 SValBuilder &SVB = State->getStateManager().getSValBuilder();
691 BasicValueFactory &BV = SVB.getBasicValueFactory();
692
693 QualType T = Sym->getType();
694 assert(T->isSignedIntegerOrEnumerationType());
695 APSIntType AT = BV.getAPSIntType(T);
696
697 ProgramStateRef NewState = State;
698
699 llvm::APSInt Max = AT.getMaxValue() / AT.getValue(Scale);
700 SVal IsCappedFromAbove =
701 SVB.evalBinOpNN(State, BO_LE, nonloc::SymbolVal(Sym),
702 nonloc::ConcreteInt(Max), SVB.getConditionType());
703 if (auto DV = IsCappedFromAbove.getAs<DefinedSVal>()) {
704 NewState = NewState->assume(*DV, true);
705 if (!NewState)
706 return State;
707 }
708
709 llvm::APSInt Min = -Max;
710 SVal IsCappedFromBelow =
711 SVB.evalBinOpNN(State, BO_GE, nonloc::SymbolVal(Sym),
712 nonloc::ConcreteInt(Min), SVB.getConditionType());
713 if (auto DV = IsCappedFromBelow.getAs<DefinedSVal>()) {
714 NewState = NewState->assume(*DV, true);
715 if (!NewState)
716 return State;
717 }
718
719 return NewState;
720}
721
722void IteratorChecker::handleRandomIncrOrDecr(CheckerContext &C,
723 OverloadedOperatorKind Op,
724 const SVal &RetVal,
725 const SVal &LHS,
726 const SVal &RHS) const {
727 // Increment or decrement the symbolic expressions which represents the
728 // position of the iterator
729 auto State = C.getState();
730 const auto *Pos = getIteratorPosition(State, LHS);
731 if (!Pos)
732 return;
733
734 const auto *value = &RHS;
735 if (auto loc = RHS.getAs<Loc>()) {
736 const auto val = State->getRawSVal(*loc);
737 value = &val;
738 }
739
740 auto &SymMgr = C.getSymbolManager();
741 auto &SVB = C.getSValBuilder();
742 auto BinOp = (Op == OO_Plus || Op == OO_PlusEqual) ? BO_Add : BO_Sub;
743 const auto OldOffset = Pos->getOffset();
744 SymbolRef NewOffset;
745 if (const auto intValue = value->getAs<nonloc::ConcreteInt>()) {
746 // For concrete integers we can calculate the new position
747 NewOffset = SVB.evalBinOp(State, BinOp, nonloc::SymbolVal(OldOffset),
748 *intValue,
749 SymMgr.getType(OldOffset)).getAsSymbol();
750 } else {
751 // For other symbols create a new symbol to keep expressions simple
752 const auto &LCtx = C.getLocationContext();
753 NewOffset = SymMgr.conjureSymbol(nullptr, LCtx, SymMgr.getType(OldOffset),
754 C.blockCount());
755 State = assumeNoOverflow(State, NewOffset, 4);
756 }
757 auto NewPos = Pos->setTo(NewOffset);
758 auto &TgtVal = (Op == OO_PlusEqual || Op == OO_MinusEqual) ? LHS : RetVal;
759 State = setIteratorPosition(State, TgtVal, NewPos);
760 C.addTransition(State);
761}
762
763void IteratorChecker::verifyRandomIncrOrDecr(CheckerContext &C,
764 OverloadedOperatorKind Op,
765 const SVal &RetVal,
766 const SVal &LHS,
767 const SVal &RHS) const {
768 auto State = C.getState();
769
770 // If the iterator is initially inside its range, then the operation is valid
771 const auto *Pos = getIteratorPosition(State, LHS);
772 if (!Pos || !isOutOfRange(State, *Pos))
773 return;
774
775 auto value = RHS;
776 if (auto loc = RHS.getAs<Loc>()) {
777 value = State->getRawSVal(*loc);
778 }
779
780 // Incremention or decremention by 0 is never bug
781 if (isZero(State, value.castAs<NonLoc>()))
782 return;
783
784 auto &SymMgr = C.getSymbolManager();
785 auto &SVB = C.getSValBuilder();
786 auto BinOp = (Op == OO_Plus || Op == OO_PlusEqual) ? BO_Add : BO_Sub;
787 const auto OldOffset = Pos->getOffset();
788 const auto intValue = value.getAs<nonloc::ConcreteInt>();
789 if (!intValue)
790 return;
791
792 auto NewOffset = SVB.evalBinOp(State, BinOp, nonloc::SymbolVal(OldOffset),
793 *intValue,
794 SymMgr.getType(OldOffset)).getAsSymbol();
795 auto NewPos = Pos->setTo(NewOffset);
796
797 // If out of range, the only valid operation is to step into the range
798 if (isOutOfRange(State, NewPos)) {
799 auto *N = C.generateNonFatalErrorNode(State);
800 if (!N)
801 return;
802 reportOutOfRangeBug("Iterator accessed past its end.", LHS, C, N);
803 }
804}
805
806void IteratorChecker::handleBegin(CheckerContext &C, const Expr *CE,
807 const SVal &RetVal, const SVal &Cont) const {
808 const auto *ContReg = Cont.getAsRegion();
809 if (!ContReg)
810 return;
811
812 while (const auto *CBOR = ContReg->getAs<CXXBaseObjectRegion>()) {
813 ContReg = CBOR->getSuperRegion();
814 }
815
816 // If the container already has a begin symbol then use it. Otherwise first
817 // create a new one.
818 auto State = C.getState();
819 auto BeginSym = getContainerBegin(State, ContReg);
820 if (!BeginSym) {
821 auto &SymMgr = C.getSymbolManager();
822 BeginSym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
823 C.getASTContext().LongTy, C.blockCount());
824 State = assumeNoOverflow(State, BeginSym, 4);
825 State = createContainerBegin(State, ContReg, BeginSym);
826 }
827 State = setIteratorPosition(State, RetVal,
828 IteratorPosition::getPosition(ContReg, BeginSym));
829 C.addTransition(State);
830}
831
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000832void IteratorChecker::handleEnd(CheckerContext &C, const Expr *CE,
833 const SVal &RetVal, const SVal &Cont) const {
834 const auto *ContReg = Cont.getAsRegion();
835 if (!ContReg)
836 return;
837
838 while (const auto *CBOR = ContReg->getAs<CXXBaseObjectRegion>()) {
839 ContReg = CBOR->getSuperRegion();
840 }
841
842 // If the container already has an end symbol then use it. Otherwise first
843 // create a new one.
844 auto State = C.getState();
845 auto EndSym = getContainerEnd(State, ContReg);
846 if (!EndSym) {
847 auto &SymMgr = C.getSymbolManager();
848 EndSym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
849 C.getASTContext().LongTy, C.blockCount());
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000850 State = assumeNoOverflow(State, EndSym, 4);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000851 State = createContainerEnd(State, ContReg, EndSym);
852 }
853 State = setIteratorPosition(State, RetVal,
854 IteratorPosition::getPosition(ContReg, EndSym));
855 C.addTransition(State);
856}
857
858void IteratorChecker::assignToContainer(CheckerContext &C, const Expr *CE,
859 const SVal &RetVal,
860 const MemRegion *Cont) const {
861 while (const auto *CBOR = Cont->getAs<CXXBaseObjectRegion>()) {
862 Cont = CBOR->getSuperRegion();
863 }
864
865 auto State = C.getState();
866 auto &SymMgr = C.getSymbolManager();
867 auto Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
868 C.getASTContext().LongTy, C.blockCount());
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000869 State = assumeNoOverflow(State, Sym, 4);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000870 State = setIteratorPosition(State, RetVal,
871 IteratorPosition::getPosition(Cont, Sym));
872 C.addTransition(State);
873}
874
875void IteratorChecker::reportOutOfRangeBug(const StringRef &Message,
876 const SVal &Val, CheckerContext &C,
877 ExplodedNode *ErrNode) const {
878 auto R = llvm::make_unique<BugReport>(*OutOfRangeBugType, Message, ErrNode);
879 R->markInteresting(Val);
880 C.emitReport(std::move(R));
881}
882
883namespace {
884
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000885bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000886bool isGreaterOrEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
887bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
888 BinaryOperator::Opcode Opc);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000889bool compare(ProgramStateRef State, NonLoc NL1, NonLoc NL2,
890 BinaryOperator::Opcode Opc);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000891
892bool isIteratorType(const QualType &Type) {
893 if (Type->isPointerType())
894 return true;
895
896 const auto *CRD = Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
897 return isIterator(CRD);
898}
899
900bool isIterator(const CXXRecordDecl *CRD) {
901 if (!CRD)
902 return false;
903
904 const auto Name = CRD->getName();
905 if (!(Name.endswith_lower("iterator") || Name.endswith_lower("iter") ||
906 Name.endswith_lower("it")))
907 return false;
908
909 bool HasCopyCtor = false, HasCopyAssign = true, HasDtor = false,
910 HasPreIncrOp = false, HasPostIncrOp = false, HasDerefOp = false;
911 for (const auto *Method : CRD->methods()) {
912 if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(Method)) {
913 if (Ctor->isCopyConstructor()) {
914 HasCopyCtor = !Ctor->isDeleted() && Ctor->getAccess() == AS_public;
915 }
916 continue;
917 }
918 if (const auto *Dtor = dyn_cast<CXXDestructorDecl>(Method)) {
919 HasDtor = !Dtor->isDeleted() && Dtor->getAccess() == AS_public;
920 continue;
921 }
922 if (Method->isCopyAssignmentOperator()) {
923 HasCopyAssign = !Method->isDeleted() && Method->getAccess() == AS_public;
924 continue;
925 }
926 if (!Method->isOverloadedOperator())
927 continue;
928 const auto OPK = Method->getOverloadedOperator();
929 if (OPK == OO_PlusPlus) {
930 HasPreIncrOp = HasPreIncrOp || (Method->getNumParams() == 0);
931 HasPostIncrOp = HasPostIncrOp || (Method->getNumParams() == 1);
932 continue;
933 }
934 if (OPK == OO_Star) {
935 HasDerefOp = (Method->getNumParams() == 0);
936 continue;
937 }
938 }
939
940 return HasCopyCtor && HasCopyAssign && HasDtor && HasPreIncrOp &&
941 HasPostIncrOp && HasDerefOp;
942}
943
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000944bool isBeginCall(const FunctionDecl *Func) {
945 const auto *IdInfo = Func->getIdentifier();
946 if (!IdInfo)
947 return false;
948 return IdInfo->getName().endswith_lower("begin");
949}
950
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000951bool isEndCall(const FunctionDecl *Func) {
952 const auto *IdInfo = Func->getIdentifier();
953 if (!IdInfo)
954 return false;
955 return IdInfo->getName().endswith_lower("end");
956}
957
958bool isSimpleComparisonOperator(OverloadedOperatorKind OK) {
959 return OK == OO_EqualEqual || OK == OO_ExclaimEqual;
960}
961
962bool isDereferenceOperator(OverloadedOperatorKind OK) {
963 return OK == OO_Star || OK == OO_Arrow || OK == OO_ArrowStar ||
964 OK == OO_Subscript;
965}
966
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000967bool isIncrementOperator(OverloadedOperatorKind OK) {
968 return OK == OO_PlusPlus;
969}
970
971bool isDecrementOperator(OverloadedOperatorKind OK) {
972 return OK == OO_MinusMinus;
973}
974
975bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK) {
976 return OK == OO_Plus || OK == OO_PlusEqual || OK == OO_Minus ||
977 OK == OO_MinusEqual;
978}
979
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000980BinaryOperator::Opcode getOpcode(const SymExpr *SE) {
981 if (const auto *BSE = dyn_cast<BinarySymExpr>(SE)) {
982 return BSE->getOpcode();
983 } else if (const auto *SC = dyn_cast<SymbolConjured>(SE)) {
Henry Wong073d5f02018-03-20 09:27:02 +0000984 const auto *COE = dyn_cast_or_null<CXXOperatorCallExpr>(SC->getStmt());
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000985 if (!COE)
986 return BO_Comma; // Extremal value, neither EQ nor NE
987 if (COE->getOperator() == OO_EqualEqual) {
988 return BO_EQ;
989 } else if (COE->getOperator() == OO_ExclaimEqual) {
990 return BO_NE;
991 }
992 return BO_Comma; // Extremal value, neither EQ nor NE
993 }
994 return BO_Comma; // Extremal value, neither EQ nor NE
995}
996
997const RegionOrSymbol getRegionOrSymbol(const SVal &Val) {
998 if (const auto Reg = Val.getAsRegion()) {
999 return Reg;
1000 } else if (const auto Sym = Val.getAsSymbol()) {
1001 return Sym;
1002 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
1003 return LCVal->getRegion();
1004 }
1005 return RegionOrSymbol();
1006}
1007
1008const ProgramStateRef processComparison(ProgramStateRef State,
1009 RegionOrSymbol LVal,
1010 RegionOrSymbol RVal, bool Equal) {
1011 const auto *LPos = getIteratorPosition(State, LVal);
1012 const auto *RPos = getIteratorPosition(State, RVal);
1013 if (LPos && !RPos) {
1014 State = adjustIteratorPosition(State, RVal, *LPos, Equal);
1015 } else if (!LPos && RPos) {
1016 State = adjustIteratorPosition(State, LVal, *RPos, Equal);
1017 } else if (LPos && RPos) {
1018 State = relateIteratorPositions(State, *LPos, *RPos, Equal);
1019 }
1020 return State;
1021}
1022
1023const ProgramStateRef saveComparison(ProgramStateRef State,
1024 const SymExpr *Condition, const SVal &LVal,
1025 const SVal &RVal, bool Eq) {
1026 const auto Left = getRegionOrSymbol(LVal);
1027 const auto Right = getRegionOrSymbol(RVal);
1028 if (!Left || !Right)
1029 return State;
1030 return State->set<IteratorComparisonMap>(Condition,
1031 IteratorComparison(Left, Right, Eq));
1032}
1033
1034const IteratorComparison *loadComparison(ProgramStateRef State,
1035 const SymExpr *Condition) {
1036 return State->get<IteratorComparisonMap>(Condition);
1037}
1038
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001039SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont) {
1040 const auto *CDataPtr = getContainerData(State, Cont);
1041 if (!CDataPtr)
1042 return nullptr;
1043
1044 return CDataPtr->getBegin();
1045}
1046
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001047SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont) {
1048 const auto *CDataPtr = getContainerData(State, Cont);
1049 if (!CDataPtr)
1050 return nullptr;
1051
1052 return CDataPtr->getEnd();
1053}
1054
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001055ProgramStateRef createContainerBegin(ProgramStateRef State,
1056 const MemRegion *Cont,
1057 const SymbolRef Sym) {
1058 // Only create if it does not exist
1059 const auto *CDataPtr = getContainerData(State, Cont);
1060 if (CDataPtr) {
1061 if (CDataPtr->getBegin()) {
1062 return State;
1063 }
1064 const auto CData = CDataPtr->newBegin(Sym);
1065 return setContainerData(State, Cont, CData);
1066 }
1067 const auto CData = ContainerData::fromBegin(Sym);
1068 return setContainerData(State, Cont, CData);
1069}
1070
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001071ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
1072 const SymbolRef Sym) {
1073 // Only create if it does not exist
1074 const auto *CDataPtr = getContainerData(State, Cont);
1075 if (CDataPtr) {
1076 if (CDataPtr->getEnd()) {
1077 return State;
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001078 }
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001079 const auto CData = CDataPtr->newEnd(Sym);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001080 return setContainerData(State, Cont, CData);
1081 }
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001082 const auto CData = ContainerData::fromEnd(Sym);
1083 return setContainerData(State, Cont, CData);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001084}
1085
1086const ContainerData *getContainerData(ProgramStateRef State,
1087 const MemRegion *Cont) {
1088 return State->get<ContainerMap>(Cont);
1089}
1090
1091ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
1092 const ContainerData &CData) {
1093 return State->set<ContainerMap>(Cont, CData);
1094}
1095
1096const IteratorPosition *getIteratorPosition(ProgramStateRef State,
1097 const SVal &Val) {
1098 if (const auto Reg = Val.getAsRegion()) {
1099 return State->get<IteratorRegionMap>(Reg);
1100 } else if (const auto Sym = Val.getAsSymbol()) {
1101 return State->get<IteratorSymbolMap>(Sym);
1102 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
1103 return State->get<IteratorRegionMap>(LCVal->getRegion());
1104 }
1105 return nullptr;
1106}
1107
1108const IteratorPosition *getIteratorPosition(ProgramStateRef State,
1109 RegionOrSymbol RegOrSym) {
1110 if (RegOrSym.is<const MemRegion *>()) {
1111 return State->get<IteratorRegionMap>(RegOrSym.get<const MemRegion *>());
1112 } else if (RegOrSym.is<SymbolRef>()) {
1113 return State->get<IteratorSymbolMap>(RegOrSym.get<SymbolRef>());
1114 }
1115 return nullptr;
1116}
1117
1118ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
1119 const IteratorPosition &Pos) {
1120 if (const auto Reg = Val.getAsRegion()) {
1121 return State->set<IteratorRegionMap>(Reg, Pos);
1122 } else if (const auto Sym = Val.getAsSymbol()) {
1123 return State->set<IteratorSymbolMap>(Sym, Pos);
1124 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
1125 return State->set<IteratorRegionMap>(LCVal->getRegion(), Pos);
1126 }
1127 return nullptr;
1128}
1129
1130ProgramStateRef setIteratorPosition(ProgramStateRef State,
1131 RegionOrSymbol RegOrSym,
1132 const IteratorPosition &Pos) {
1133 if (RegOrSym.is<const MemRegion *>()) {
1134 return State->set<IteratorRegionMap>(RegOrSym.get<const MemRegion *>(),
1135 Pos);
1136 } else if (RegOrSym.is<SymbolRef>()) {
1137 return State->set<IteratorSymbolMap>(RegOrSym.get<SymbolRef>(), Pos);
1138 }
1139 return nullptr;
1140}
1141
1142ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val) {
1143 if (const auto Reg = Val.getAsRegion()) {
1144 return State->remove<IteratorRegionMap>(Reg);
1145 } else if (const auto Sym = Val.getAsSymbol()) {
1146 return State->remove<IteratorSymbolMap>(Sym);
1147 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
1148 return State->remove<IteratorRegionMap>(LCVal->getRegion());
1149 }
1150 return nullptr;
1151}
1152
1153ProgramStateRef adjustIteratorPosition(ProgramStateRef State,
1154 RegionOrSymbol RegOrSym,
1155 const IteratorPosition &Pos,
1156 bool Equal) {
1157 if (Equal) {
1158 return setIteratorPosition(State, RegOrSym, Pos);
1159 } else {
1160 return State;
1161 }
1162}
1163
1164ProgramStateRef relateIteratorPositions(ProgramStateRef State,
1165 const IteratorPosition &Pos1,
1166 const IteratorPosition &Pos2,
1167 bool Equal) {
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001168 auto &SVB = State->getStateManager().getSValBuilder();
Adam Balogh0a7592b2018-07-16 09:27:27 +00001169
1170 // FIXME: This code should be reworked as follows:
1171 // 1. Subtract the operands using evalBinOp().
1172 // 2. Assume that the result doesn't overflow.
1173 // 3. Compare the result to 0.
1174 // 4. Assume the result of the comparison.
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001175 const auto comparison =
1176 SVB.evalBinOp(State, BO_EQ, nonloc::SymbolVal(Pos1.getOffset()),
Adam Balogh0a7592b2018-07-16 09:27:27 +00001177 nonloc::SymbolVal(Pos2.getOffset()),
1178 SVB.getConditionType());
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001179
Adam Balogh0a7592b2018-07-16 09:27:27 +00001180 assert(comparison.getAs<DefinedSVal>() &&
1181 "Symbol comparison must be a `DefinedSVal`");
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001182
Adam Balogh0a7592b2018-07-16 09:27:27 +00001183 auto NewState = State->assume(comparison.castAs<DefinedSVal>(), Equal);
1184 if (const auto CompSym = comparison.getAsSymbol()) {
1185 assert(isa<SymIntExpr>(CompSym) &&
1186 "Symbol comparison must be a `SymIntExpr`");
1187 assert(BinaryOperator::isComparisonOp(
1188 cast<SymIntExpr>(CompSym)->getOpcode()) &&
1189 "Symbol comparison must be a comparison");
1190 return assumeNoOverflow(NewState, cast<SymIntExpr>(CompSym)->getLHS(), 2);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001191 }
1192
Adam Balogh0a7592b2018-07-16 09:27:27 +00001193 return NewState;
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001194}
1195
Adam Balogha6921202018-07-30 08:52:21 +00001196bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont) {
1197 auto RegionMap = State->get<IteratorRegionMap>();
1198 for (const auto Reg : RegionMap) {
1199 if (Reg.second.getContainer() == Cont)
1200 return true;
1201 }
1202
1203 auto SymbolMap = State->get<IteratorSymbolMap>();
1204 for (const auto Sym : SymbolMap) {
1205 if (Sym.second.getContainer() == Cont)
1206 return true;
1207 }
1208
1209 return false;
1210}
1211
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001212bool isZero(ProgramStateRef State, const NonLoc &Val) {
1213 auto &BVF = State->getBasicVals();
1214 return compare(State, Val,
1215 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))),
1216 BO_EQ);
1217}
1218
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001219bool isOutOfRange(ProgramStateRef State, const IteratorPosition &Pos) {
1220 const auto *Cont = Pos.getContainer();
1221 const auto *CData = getContainerData(State, Cont);
1222 if (!CData)
1223 return false;
1224
1225 // Out of range means less than the begin symbol or greater or equal to the
1226 // end symbol.
1227
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001228 const auto Beg = CData->getBegin();
1229 if (Beg) {
1230 if (isLess(State, Pos.getOffset(), Beg)) {
1231 return true;
1232 }
1233 }
1234
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001235 const auto End = CData->getEnd();
1236 if (End) {
1237 if (isGreaterOrEqual(State, Pos.getOffset(), End)) {
1238 return true;
1239 }
1240 }
1241
1242 return false;
1243}
1244
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001245bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
1246 return compare(State, Sym1, Sym2, BO_LT);
1247}
1248
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001249bool isGreaterOrEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
1250 return compare(State, Sym1, Sym2, BO_GE);
1251}
1252
1253bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
1254 BinaryOperator::Opcode Opc) {
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001255 return compare(State, nonloc::SymbolVal(Sym1), nonloc::SymbolVal(Sym2), Opc);
1256}
1257
1258bool compare(ProgramStateRef State, NonLoc NL1, NonLoc NL2,
1259 BinaryOperator::Opcode Opc) {
1260 auto &SVB = State->getStateManager().getSValBuilder();
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001261
1262 const auto comparison =
Adam Balogh0a7592b2018-07-16 09:27:27 +00001263 SVB.evalBinOp(State, Opc, NL1, NL2, SVB.getConditionType());
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001264
Adam Balogh0a7592b2018-07-16 09:27:27 +00001265 assert(comparison.getAs<DefinedSVal>() &&
1266 "Symbol comparison must be a `DefinedSVal`");
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001267
Adam Balogh0a7592b2018-07-16 09:27:27 +00001268 return !State->assume(comparison.castAs<DefinedSVal>(), false);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001269}
1270
1271} // namespace
1272
1273#define REGISTER_CHECKER(name) \
1274 void ento::register##name(CheckerManager &Mgr) { \
1275 auto *checker = Mgr.registerChecker<IteratorChecker>(); \
1276 checker->ChecksEnabled[IteratorChecker::CK_##name] = true; \
1277 checker->CheckNames[IteratorChecker::CK_##name] = \
1278 Mgr.getCurrentCheckName(); \
1279 }
1280
1281REGISTER_CHECKER(IteratorRangeChecker)
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001282