1 //===-- IteratorChecker.cpp ---------------------------------------*- C++ -*--//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // Defines a checker for using iterators outside their range (past end). Usage
10 // means here dereferencing, incrementing etc.
12 //===----------------------------------------------------------------------===//
14 // In the code, iterator can be represented as a:
15 // * type-I: typedef-ed pointer. Operations over such iterator, such as
16 // comparisons or increments, are modeled straightforwardly by the
18 // * type-II: structure with its method bodies available. Operations over such
19 // iterator are inlined by the analyzer, and results of modeling
20 // these operations are exposing implementation details of the
21 // iterators, which is not necessarily helping.
22 // * type-III: completely opaque structure. Operations over such iterator are
23 // modeled conservatively, producing conjured symbols everywhere.
25 // To handle all these types in a common way we introduce a structure called
26 // IteratorPosition which is an abstraction of the position the iterator
27 // represents using symbolic expressions. The checker handles all the
28 // operations on this structure.
30 // Additionally, depending on the circumstances, operators of types II and III
31 // can be represented as:
32 // * type-IIa, type-IIIa: conjured structure symbols - when returned by value
33 // from conservatively evaluated methods such as
35 // * type-IIb, type-IIIb: memory regions of iterator-typed objects, such as
36 // variables or temporaries, when the iterator object is
37 // currently treated as an lvalue.
38 // * type-IIc, type-IIIc: compound values of iterator-typed objects, when the
39 // iterator object is treated as an rvalue taken of a
40 // particular lvalue, eg. a copy of "type-a" iterator
41 // object, or an iterator that existed before the
42 // analysis has started.
44 // To handle any of these three different representations stored in an SVal we
45 // use setter and getters functions which separate the three cases. To store
46 // them we use a pointer union of symbol and memory region.
48 // The checker works the following way: We record the begin and the
49 // past-end iterator for all containers whenever their `.begin()` and `.end()`
50 // are called. Since the Constraint Manager cannot handle such SVals we need
51 // to take over its role. We post-check equality and non-equality comparisons
52 // and record that the two sides are equal if we are in the 'equal' branch
53 // (true-branch for `==` and false-branch for `!=`).
55 // In case of type-I or type-II iterators we get a concrete integer as a result
56 // of the comparison (1 or 0) but in case of type-III we only get a Symbol. In
57 // this latter case we record the symbol and reload it in evalAssume() and do
58 // the propagation there. We also handle (maybe double) negated comparisons
59 // which are represented in the form of (x == 0 or x != 0) where x is the
62 // Since `SimpleConstraintManager` cannot handle complex symbolic expressions
63 // we only use expressions of the format S, S+n or S-n for iterator positions
64 // where S is a conjured symbol and n is an unsigned concrete integer. When
65 // making an assumption e.g. `S1 + n == S2 + m` we store `S1 - S2 == m - n` as
66 // a constraint which we later retrieve when doing an actual comparison.
68 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
69 #include "clang/AST/DeclTemplate.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 #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
78 using namespace clang;
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.
85 struct IteratorPosition {
88 // Container the iterator belongs to
89 const MemRegion *Cont;
91 // Whether iterator is valid
95 const SymbolRef Offset;
97 IteratorPosition(const MemRegion *C, bool V, SymbolRef Of)
98 : Cont(C), Valid(V), Offset(Of) {}
101 const MemRegion *getContainer() const { return Cont; }
102 bool isValid() const { return Valid; }
103 SymbolRef getOffset() const { return Offset; }
105 IteratorPosition invalidate() const {
106 return IteratorPosition(Cont, false, Offset);
109 static IteratorPosition getPosition(const MemRegion *C, SymbolRef Of) {
110 return IteratorPosition(C, true, Of);
113 IteratorPosition setTo(SymbolRef NewOf) const {
114 return IteratorPosition(Cont, Valid, NewOf);
117 IteratorPosition reAssign(const MemRegion *NewCont) const {
118 return IteratorPosition(NewCont, Valid, Offset);
121 bool operator==(const IteratorPosition &X) const {
122 return Cont == X.Cont && Valid == X.Valid && Offset == X.Offset;
125 bool operator!=(const IteratorPosition &X) const {
126 return Cont != X.Cont || Valid != X.Valid || Offset != X.Offset;
129 void Profile(llvm::FoldingSetNodeID &ID) const {
131 ID.AddInteger(Valid);
136 // Structure to record the symbolic begin and end position of a container
137 struct ContainerData {
139 const SymbolRef Begin, End;
141 ContainerData(SymbolRef B, SymbolRef E) : Begin(B), End(E) {}
144 static ContainerData fromBegin(SymbolRef B) {
145 return ContainerData(B, nullptr);
148 static ContainerData fromEnd(SymbolRef E) {
149 return ContainerData(nullptr, E);
152 SymbolRef getBegin() const { return Begin; }
153 SymbolRef getEnd() const { return End; }
155 ContainerData newBegin(SymbolRef B) const { return ContainerData(B, End); }
157 ContainerData newEnd(SymbolRef E) const { return ContainerData(Begin, E); }
159 bool operator==(const ContainerData &X) const {
160 return Begin == X.Begin && End == X.End;
163 bool operator!=(const ContainerData &X) const {
164 return Begin != X.Begin || End != X.End;
167 void Profile(llvm::FoldingSetNodeID &ID) const {
173 class IteratorChecker
174 : public Checker<check::PreCall, check::PostCall,
175 check::PostStmt<MaterializeTemporaryExpr>, check::Bind,
176 check::LiveSymbols, check::DeadSymbols> {
178 std::unique_ptr<BugType> OutOfRangeBugType;
179 std::unique_ptr<BugType> MismatchedBugType;
180 std::unique_ptr<BugType> InvalidatedBugType;
182 void handleComparison(CheckerContext &C, const Expr *CE, const SVal &RetVal,
183 const SVal &LVal, const SVal &RVal,
184 OverloadedOperatorKind Op) const;
185 void processComparison(CheckerContext &C, ProgramStateRef State,
186 SymbolRef Sym1, SymbolRef Sym2, const SVal &RetVal,
187 OverloadedOperatorKind Op) const;
188 void verifyAccess(CheckerContext &C, const SVal &Val) const;
189 void verifyDereference(CheckerContext &C, const SVal &Val) const;
190 void handleIncrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
192 void handleDecrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
194 void handleRandomIncrOrDecr(CheckerContext &C, OverloadedOperatorKind Op,
195 const SVal &RetVal, const SVal &LHS,
196 const SVal &RHS) const;
197 void handleBegin(CheckerContext &C, const Expr *CE, const SVal &RetVal,
198 const SVal &Cont) const;
199 void handleEnd(CheckerContext &C, const Expr *CE, const SVal &RetVal,
200 const SVal &Cont) const;
201 void assignToContainer(CheckerContext &C, const Expr *CE, const SVal &RetVal,
202 const MemRegion *Cont) const;
203 void handleAssign(CheckerContext &C, const SVal &Cont,
204 const Expr *CE = nullptr,
205 const SVal &OldCont = UndefinedVal()) const;
206 void handleClear(CheckerContext &C, const SVal &Cont) const;
207 void handlePushBack(CheckerContext &C, const SVal &Cont) const;
208 void handlePopBack(CheckerContext &C, const SVal &Cont) const;
209 void handlePushFront(CheckerContext &C, const SVal &Cont) const;
210 void handlePopFront(CheckerContext &C, const SVal &Cont) const;
211 void handleInsert(CheckerContext &C, const SVal &Iter) const;
212 void handleErase(CheckerContext &C, const SVal &Iter) const;
213 void handleErase(CheckerContext &C, const SVal &Iter1,
214 const SVal &Iter2) const;
215 void handleEraseAfter(CheckerContext &C, const SVal &Iter) const;
216 void handleEraseAfter(CheckerContext &C, const SVal &Iter1,
217 const SVal &Iter2) const;
218 void verifyIncrement(CheckerContext &C, const SVal &Iter) const;
219 void verifyDecrement(CheckerContext &C, const SVal &Iter) const;
220 void verifyRandomIncrOrDecr(CheckerContext &C, OverloadedOperatorKind Op,
221 const SVal &LHS, const SVal &RHS) const;
222 void verifyMatch(CheckerContext &C, const SVal &Iter,
223 const MemRegion *Cont) const;
224 void verifyMatch(CheckerContext &C, const SVal &Iter1,
225 const SVal &Iter2) const;
226 IteratorPosition advancePosition(CheckerContext &C, OverloadedOperatorKind Op,
227 const IteratorPosition &Pos,
228 const SVal &Distance) const;
229 void reportOutOfRangeBug(const StringRef &Message, const SVal &Val,
230 CheckerContext &C, ExplodedNode *ErrNode) const;
231 void reportMismatchedBug(const StringRef &Message, const SVal &Val1,
232 const SVal &Val2, CheckerContext &C,
233 ExplodedNode *ErrNode) const;
234 void reportMismatchedBug(const StringRef &Message, const SVal &Val,
235 const MemRegion *Reg, CheckerContext &C,
236 ExplodedNode *ErrNode) const;
237 void reportInvalidatedBug(const StringRef &Message, const SVal &Val,
238 CheckerContext &C, ExplodedNode *ErrNode) const;
244 CK_IteratorRangeChecker,
245 CK_MismatchedIteratorChecker,
246 CK_InvalidatedIteratorChecker,
250 DefaultBool ChecksEnabled[CK_NumCheckKinds];
251 CheckerNameRef CheckNames[CK_NumCheckKinds];
253 void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
254 void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
255 void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const;
256 void checkPostStmt(const CXXConstructExpr *CCE, CheckerContext &C) const;
257 void checkPostStmt(const DeclStmt *DS, CheckerContext &C) const;
258 void checkPostStmt(const MaterializeTemporaryExpr *MTE,
259 CheckerContext &C) const;
260 void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
261 void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
265 REGISTER_MAP_WITH_PROGRAMSTATE(IteratorSymbolMap, SymbolRef, IteratorPosition)
266 REGISTER_MAP_WITH_PROGRAMSTATE(IteratorRegionMap, const MemRegion *,
269 REGISTER_MAP_WITH_PROGRAMSTATE(ContainerMap, const MemRegion *, ContainerData)
273 bool isIteratorType(const QualType &Type);
274 bool isIterator(const CXXRecordDecl *CRD);
275 bool isComparisonOperator(OverloadedOperatorKind OK);
276 bool isBeginCall(const FunctionDecl *Func);
277 bool isEndCall(const FunctionDecl *Func);
278 bool isAssignCall(const FunctionDecl *Func);
279 bool isClearCall(const FunctionDecl *Func);
280 bool isPushBackCall(const FunctionDecl *Func);
281 bool isEmplaceBackCall(const FunctionDecl *Func);
282 bool isPopBackCall(const FunctionDecl *Func);
283 bool isPushFrontCall(const FunctionDecl *Func);
284 bool isEmplaceFrontCall(const FunctionDecl *Func);
285 bool isPopFrontCall(const FunctionDecl *Func);
286 bool isInsertCall(const FunctionDecl *Func);
287 bool isEraseCall(const FunctionDecl *Func);
288 bool isEraseAfterCall(const FunctionDecl *Func);
289 bool isEmplaceCall(const FunctionDecl *Func);
290 bool isAssignmentOperator(OverloadedOperatorKind OK);
291 bool isSimpleComparisonOperator(OverloadedOperatorKind OK);
292 bool isAccessOperator(OverloadedOperatorKind OK);
293 bool isDereferenceOperator(OverloadedOperatorKind OK);
294 bool isIncrementOperator(OverloadedOperatorKind OK);
295 bool isDecrementOperator(OverloadedOperatorKind OK);
296 bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK);
297 bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg);
298 bool frontModifiable(ProgramStateRef State, const MemRegion *Reg);
299 bool backModifiable(ProgramStateRef State, const MemRegion *Reg);
300 SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont);
301 SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont);
302 ProgramStateRef createContainerBegin(ProgramStateRef State,
303 const MemRegion *Cont, const Expr *E,
304 QualType T, const LocationContext *LCtx,
305 unsigned BlockCount);
306 ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
307 const Expr *E, QualType T,
308 const LocationContext *LCtx,
309 unsigned BlockCount);
310 const IteratorPosition *getIteratorPosition(ProgramStateRef State,
312 ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
313 const IteratorPosition &Pos);
314 ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val);
315 ProgramStateRef assumeNoOverflow(ProgramStateRef State, SymbolRef Sym,
317 ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State,
318 const MemRegion *Cont);
320 invalidateAllIteratorPositionsExcept(ProgramStateRef State,
321 const MemRegion *Cont, SymbolRef Offset,
322 BinaryOperator::Opcode Opc);
323 ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
325 BinaryOperator::Opcode Opc);
326 ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
328 BinaryOperator::Opcode Opc1,
330 BinaryOperator::Opcode Opc2);
331 ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State,
332 const MemRegion *Cont,
333 const MemRegion *NewCont);
334 ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State,
335 const MemRegion *Cont,
336 const MemRegion *NewCont,
338 BinaryOperator::Opcode Opc);
339 ProgramStateRef rebaseSymbolInIteratorPositionsIf(
340 ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym,
341 SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc);
342 ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1,
343 SymbolRef Sym2, bool Equal);
344 const ContainerData *getContainerData(ProgramStateRef State,
345 const MemRegion *Cont);
346 ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
347 const ContainerData &CData);
348 bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont);
349 bool isBoundThroughLazyCompoundVal(const Environment &Env,
350 const MemRegion *Reg);
351 bool isPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos);
352 bool isAheadOfRange(ProgramStateRef State, const IteratorPosition &Pos);
353 bool isBehindPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos);
354 bool isZero(ProgramStateRef State, const NonLoc &Val);
357 IteratorChecker::IteratorChecker() {
358 OutOfRangeBugType.reset(
359 new BugType(this, "Iterator out of range", "Misuse of STL APIs"));
360 MismatchedBugType.reset(
361 new BugType(this, "Iterator(s) mismatched", "Misuse of STL APIs",
362 /*SuppressOnSink=*/true));
363 InvalidatedBugType.reset(
364 new BugType(this, "Iterator invalidated", "Misuse of STL APIs"));
367 void IteratorChecker::checkPreCall(const CallEvent &Call,
368 CheckerContext &C) const {
369 // Check for out of range access or access of invalidated position and
370 // iterator mismatches
371 const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
375 if (Func->isOverloadedOperator()) {
376 if (ChecksEnabled[CK_InvalidatedIteratorChecker] &&
377 isAccessOperator(Func->getOverloadedOperator())) {
378 // Check for any kind of access of invalidated iterator positions
379 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
380 verifyAccess(C, InstCall->getCXXThisVal());
382 verifyAccess(C, Call.getArgSVal(0));
385 if (ChecksEnabled[CK_IteratorRangeChecker]) {
386 if (isIncrementOperator(Func->getOverloadedOperator())) {
387 // Check for out-of-range incrementions
388 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
389 verifyIncrement(C, InstCall->getCXXThisVal());
391 if (Call.getNumArgs() >= 1) {
392 verifyIncrement(C, Call.getArgSVal(0));
395 } else if (isDecrementOperator(Func->getOverloadedOperator())) {
396 // Check for out-of-range decrementions
397 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
398 verifyDecrement(C, InstCall->getCXXThisVal());
400 if (Call.getNumArgs() >= 1) {
401 verifyDecrement(C, Call.getArgSVal(0));
404 } else if (isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) {
405 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
406 // Check for out-of-range incrementions and decrementions
407 if (Call.getNumArgs() >= 1 &&
408 Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) {
409 verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(),
410 InstCall->getCXXThisVal(),
414 if (Call.getNumArgs() >= 2 &&
415 Call.getArgExpr(1)->getType()->isIntegralOrEnumerationType()) {
416 verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(),
417 Call.getArgSVal(0), Call.getArgSVal(1));
420 } else if (isDereferenceOperator(Func->getOverloadedOperator())) {
421 // Check for dereference of out-of-range iterators
422 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
423 verifyDereference(C, InstCall->getCXXThisVal());
425 verifyDereference(C, Call.getArgSVal(0));
428 } else if (ChecksEnabled[CK_MismatchedIteratorChecker] &&
429 isComparisonOperator(Func->getOverloadedOperator())) {
430 // Check for comparisons of iterators of different containers
431 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
432 if (Call.getNumArgs() < 1)
435 if (!isIteratorType(InstCall->getCXXThisExpr()->getType()) ||
436 !isIteratorType(Call.getArgExpr(0)->getType()))
439 verifyMatch(C, InstCall->getCXXThisVal(), Call.getArgSVal(0));
441 if (Call.getNumArgs() < 2)
444 if (!isIteratorType(Call.getArgExpr(0)->getType()) ||
445 !isIteratorType(Call.getArgExpr(1)->getType()))
448 verifyMatch(C, Call.getArgSVal(0), Call.getArgSVal(1));
451 } else if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
452 if (!ChecksEnabled[CK_MismatchedIteratorChecker])
455 const auto *ContReg = InstCall->getCXXThisVal().getAsRegion();
458 // Check for erase, insert and emplace using iterator of another container
459 if (isEraseCall(Func) || isEraseAfterCall(Func)) {
460 verifyMatch(C, Call.getArgSVal(0),
461 InstCall->getCXXThisVal().getAsRegion());
462 if (Call.getNumArgs() == 2) {
463 verifyMatch(C, Call.getArgSVal(1),
464 InstCall->getCXXThisVal().getAsRegion());
466 } else if (isInsertCall(Func)) {
467 verifyMatch(C, Call.getArgSVal(0),
468 InstCall->getCXXThisVal().getAsRegion());
469 if (Call.getNumArgs() == 3 &&
470 isIteratorType(Call.getArgExpr(1)->getType()) &&
471 isIteratorType(Call.getArgExpr(2)->getType())) {
472 verifyMatch(C, Call.getArgSVal(1), Call.getArgSVal(2));
474 } else if (isEmplaceCall(Func)) {
475 verifyMatch(C, Call.getArgSVal(0),
476 InstCall->getCXXThisVal().getAsRegion());
478 } else if (isa<CXXConstructorCall>(&Call)) {
479 // Check match of first-last iterator pair in a constructor of a container
480 if (Call.getNumArgs() < 2)
483 const auto *Ctr = cast<CXXConstructorDecl>(Call.getDecl());
484 if (Ctr->getNumParams() < 2)
487 if (Ctr->getParamDecl(0)->getName() != "first" ||
488 Ctr->getParamDecl(1)->getName() != "last")
491 if (!isIteratorType(Call.getArgExpr(0)->getType()) ||
492 !isIteratorType(Call.getArgExpr(1)->getType()))
495 verifyMatch(C, Call.getArgSVal(0), Call.getArgSVal(1));
497 // The main purpose of iterators is to abstract away from different
498 // containers and provide a (maybe limited) uniform access to them.
499 // This implies that any correctly written template function that
500 // works on multiple containers using iterators takes different
501 // template parameters for different containers. So we can safely
502 // assume that passing iterators of different containers as arguments
503 // whose type replaces the same template parameter is a bug.
506 // template<typename I1, typename I2>
507 // void f(I1 first1, I1 last1, I2 first2, I2 last2);
509 // In this case the first two arguments to f() must be iterators must belong
510 // to the same container and the last to also to the same container but
511 // not necessarily to the same as the first two.
513 if (!ChecksEnabled[CK_MismatchedIteratorChecker])
516 const auto *Templ = Func->getPrimaryTemplate();
520 const auto *TParams = Templ->getTemplateParameters();
521 const auto *TArgs = Func->getTemplateSpecializationArgs();
523 // Iterate over all the template parameters
524 for (size_t I = 0; I < TParams->size(); ++I) {
525 const auto *TPDecl = dyn_cast<TemplateTypeParmDecl>(TParams->getParam(I));
529 if (TPDecl->isParameterPack())
532 const auto TAType = TArgs->get(I).getAsType();
533 if (!isIteratorType(TAType))
536 SVal LHS = UndefinedVal();
538 // For every template parameter which is an iterator type in the
539 // instantiation look for all functions' parameters' type by it and
540 // check whether they belong to the same container
541 for (auto J = 0U; J < Func->getNumParams(); ++J) {
542 const auto *Param = Func->getParamDecl(J);
543 const auto *ParamType =
544 Param->getType()->getAs<SubstTemplateTypeParmType>();
546 ParamType->getReplacedParameter()->getDecl() != TPDecl)
549 LHS = Call.getArgSVal(J);
551 verifyMatch(C, LHS, Call.getArgSVal(J));
558 void IteratorChecker::checkPostCall(const CallEvent &Call,
559 CheckerContext &C) const {
560 // Record new iterator positions and iterator position changes
561 const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
565 if (Func->isOverloadedOperator()) {
566 const auto Op = Func->getOverloadedOperator();
567 if (isAssignmentOperator(Op)) {
568 // Overloaded 'operator=' must be a non-static member function.
569 const auto *InstCall = cast<CXXInstanceCall>(&Call);
570 if (cast<CXXMethodDecl>(Func)->isMoveAssignmentOperator()) {
571 handleAssign(C, InstCall->getCXXThisVal(), Call.getOriginExpr(),
576 handleAssign(C, InstCall->getCXXThisVal());
578 } else if (isSimpleComparisonOperator(Op)) {
579 const auto *OrigExpr = Call.getOriginExpr();
583 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
584 handleComparison(C, OrigExpr, Call.getReturnValue(),
585 InstCall->getCXXThisVal(), Call.getArgSVal(0), Op);
589 handleComparison(C, OrigExpr, Call.getReturnValue(), Call.getArgSVal(0),
590 Call.getArgSVal(1), Op);
592 } else if (isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) {
593 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
594 if (Call.getNumArgs() >= 1 &&
595 Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) {
596 handleRandomIncrOrDecr(C, Func->getOverloadedOperator(),
597 Call.getReturnValue(),
598 InstCall->getCXXThisVal(), Call.getArgSVal(0));
602 if (Call.getNumArgs() >= 2 &&
603 Call.getArgExpr(1)->getType()->isIntegralOrEnumerationType()) {
604 handleRandomIncrOrDecr(C, Func->getOverloadedOperator(),
605 Call.getReturnValue(), Call.getArgSVal(0),
610 } else if (isIncrementOperator(Func->getOverloadedOperator())) {
611 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
612 handleIncrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
617 handleIncrement(C, Call.getReturnValue(), Call.getArgSVal(0),
620 } else if (isDecrementOperator(Func->getOverloadedOperator())) {
621 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
622 handleDecrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
627 handleDecrement(C, Call.getReturnValue(), Call.getArgSVal(0),
632 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
633 if (isAssignCall(Func)) {
634 handleAssign(C, InstCall->getCXXThisVal());
638 if (isClearCall(Func)) {
639 handleClear(C, InstCall->getCXXThisVal());
643 if (isPushBackCall(Func) || isEmplaceBackCall(Func)) {
644 handlePushBack(C, InstCall->getCXXThisVal());
648 if (isPopBackCall(Func)) {
649 handlePopBack(C, InstCall->getCXXThisVal());
653 if (isPushFrontCall(Func) || isEmplaceFrontCall(Func)) {
654 handlePushFront(C, InstCall->getCXXThisVal());
658 if (isPopFrontCall(Func)) {
659 handlePopFront(C, InstCall->getCXXThisVal());
663 if (isInsertCall(Func) || isEmplaceCall(Func)) {
664 handleInsert(C, Call.getArgSVal(0));
668 if (isEraseCall(Func)) {
669 if (Call.getNumArgs() == 1) {
670 handleErase(C, Call.getArgSVal(0));
674 if (Call.getNumArgs() == 2) {
675 handleErase(C, Call.getArgSVal(0), Call.getArgSVal(1));
680 if (isEraseAfterCall(Func)) {
681 if (Call.getNumArgs() == 1) {
682 handleEraseAfter(C, Call.getArgSVal(0));
686 if (Call.getNumArgs() == 2) {
687 handleEraseAfter(C, Call.getArgSVal(0), Call.getArgSVal(1));
693 const auto *OrigExpr = Call.getOriginExpr();
697 if (!isIteratorType(Call.getResultType()))
700 auto State = C.getState();
702 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
703 if (isBeginCall(Func)) {
704 handleBegin(C, OrigExpr, Call.getReturnValue(),
705 InstCall->getCXXThisVal());
709 if (isEndCall(Func)) {
710 handleEnd(C, OrigExpr, Call.getReturnValue(),
711 InstCall->getCXXThisVal());
716 // Already bound to container?
717 if (getIteratorPosition(State, Call.getReturnValue()))
720 // Copy-like and move constructors
721 if (isa<CXXConstructorCall>(&Call) && Call.getNumArgs() == 1) {
722 if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(0))) {
723 State = setIteratorPosition(State, Call.getReturnValue(), *Pos);
724 if (cast<CXXConstructorDecl>(Func)->isMoveConstructor()) {
725 State = removeIteratorPosition(State, Call.getArgSVal(0));
727 C.addTransition(State);
732 // Assumption: if return value is an iterator which is not yet bound to a
733 // container, then look for the first iterator argument, and
734 // bind the return value to the same container. This approach
735 // works for STL algorithms.
736 // FIXME: Add a more conservative mode
737 for (unsigned i = 0; i < Call.getNumArgs(); ++i) {
738 if (isIteratorType(Call.getArgExpr(i)->getType())) {
739 if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(i))) {
740 assignToContainer(C, OrigExpr, Call.getReturnValue(),
741 Pos->getContainer());
749 void IteratorChecker::checkBind(SVal Loc, SVal Val, const Stmt *S,
750 CheckerContext &C) const {
751 auto State = C.getState();
752 const auto *Pos = getIteratorPosition(State, Val);
754 State = setIteratorPosition(State, Loc, *Pos);
755 C.addTransition(State);
757 const auto *OldPos = getIteratorPosition(State, Loc);
759 State = removeIteratorPosition(State, Loc);
760 C.addTransition(State);
765 void IteratorChecker::checkPostStmt(const MaterializeTemporaryExpr *MTE,
766 CheckerContext &C) const {
767 /* Transfer iterator state to temporary objects */
768 auto State = C.getState();
770 getIteratorPosition(State, C.getSVal(MTE->GetTemporaryExpr()));
773 State = setIteratorPosition(State, C.getSVal(MTE), *Pos);
774 C.addTransition(State);
777 void IteratorChecker::checkLiveSymbols(ProgramStateRef State,
778 SymbolReaper &SR) const {
779 // Keep symbolic expressions of iterator positions, container begins and ends
781 auto RegionMap = State->get<IteratorRegionMap>();
782 for (const auto Reg : RegionMap) {
783 const auto Offset = Reg.second.getOffset();
784 for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
785 if (isa<SymbolData>(*i))
789 auto SymbolMap = State->get<IteratorSymbolMap>();
790 for (const auto Sym : SymbolMap) {
791 const auto Offset = Sym.second.getOffset();
792 for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
793 if (isa<SymbolData>(*i))
797 auto ContMap = State->get<ContainerMap>();
798 for (const auto Cont : ContMap) {
799 const auto CData = Cont.second;
800 if (CData.getBegin()) {
801 SR.markLive(CData.getBegin());
802 if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getBegin()))
803 SR.markLive(SIE->getLHS());
805 if (CData.getEnd()) {
806 SR.markLive(CData.getEnd());
807 if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getEnd()))
808 SR.markLive(SIE->getLHS());
813 void IteratorChecker::checkDeadSymbols(SymbolReaper &SR,
814 CheckerContext &C) const {
816 auto State = C.getState();
818 auto RegionMap = State->get<IteratorRegionMap>();
819 for (const auto Reg : RegionMap) {
820 if (!SR.isLiveRegion(Reg.first)) {
821 // The region behind the `LazyCompoundVal` is often cleaned up before
822 // the `LazyCompoundVal` itself. If there are iterator positions keyed
823 // by these regions their cleanup must be deferred.
824 if (!isBoundThroughLazyCompoundVal(State->getEnvironment(), Reg.first)) {
825 State = State->remove<IteratorRegionMap>(Reg.first);
830 auto SymbolMap = State->get<IteratorSymbolMap>();
831 for (const auto Sym : SymbolMap) {
832 if (!SR.isLive(Sym.first)) {
833 State = State->remove<IteratorSymbolMap>(Sym.first);
837 auto ContMap = State->get<ContainerMap>();
838 for (const auto Cont : ContMap) {
839 if (!SR.isLiveRegion(Cont.first)) {
840 // We must keep the container data while it has live iterators to be able
841 // to compare them to the begin and the end of the container.
842 if (!hasLiveIterators(State, Cont.first)) {
843 State = State->remove<ContainerMap>(Cont.first);
848 C.addTransition(State);
851 void IteratorChecker::handleComparison(CheckerContext &C, const Expr *CE,
852 const SVal &RetVal, const SVal &LVal,
854 OverloadedOperatorKind Op) const {
855 // Record the operands and the operator of the comparison for the next
856 // evalAssume, if the result is a symbolic expression. If it is a concrete
857 // value (only one branch is possible), then transfer the state between
858 // the operands according to the operator and the result
859 auto State = C.getState();
860 const auto *LPos = getIteratorPosition(State, LVal);
861 const auto *RPos = getIteratorPosition(State, RVal);
862 const MemRegion *Cont = nullptr;
864 Cont = LPos->getContainer();
866 Cont = RPos->getContainer();
871 // At least one of the iterators have recorded positions. If one of them has
872 // not then create a new symbol for the offset.
874 if (!LPos || !RPos) {
875 auto &SymMgr = C.getSymbolManager();
876 Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
877 C.getASTContext().LongTy, C.blockCount());
878 State = assumeNoOverflow(State, Sym, 4);
882 State = setIteratorPosition(State, LVal,
883 IteratorPosition::getPosition(Cont, Sym));
884 LPos = getIteratorPosition(State, LVal);
886 State = setIteratorPosition(State, RVal,
887 IteratorPosition::getPosition(Cont, Sym));
888 RPos = getIteratorPosition(State, RVal);
891 processComparison(C, State, LPos->getOffset(), RPos->getOffset(), RetVal, Op);
894 void IteratorChecker::processComparison(CheckerContext &C,
895 ProgramStateRef State, SymbolRef Sym1,
896 SymbolRef Sym2, const SVal &RetVal,
897 OverloadedOperatorKind Op) const {
898 if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) {
899 if ((State = relateSymbols(State, Sym1, Sym2,
900 (Op == OO_EqualEqual) ==
901 (TruthVal->getValue() != 0)))) {
902 C.addTransition(State);
904 C.generateSink(State, C.getPredecessor());
909 const auto ConditionVal = RetVal.getAs<DefinedSVal>();
913 if (auto StateTrue = relateSymbols(State, Sym1, Sym2, Op == OO_EqualEqual)) {
914 StateTrue = StateTrue->assume(*ConditionVal, true);
915 C.addTransition(StateTrue);
918 if (auto StateFalse = relateSymbols(State, Sym1, Sym2, Op != OO_EqualEqual)) {
919 StateFalse = StateFalse->assume(*ConditionVal, false);
920 C.addTransition(StateFalse);
924 void IteratorChecker::verifyDereference(CheckerContext &C,
925 const SVal &Val) const {
926 auto State = C.getState();
927 const auto *Pos = getIteratorPosition(State, Val);
928 if (Pos && isPastTheEnd(State, *Pos)) {
929 auto *N = C.generateErrorNode(State);
932 reportOutOfRangeBug("Past-the-end iterator dereferenced.", Val, C, N);
937 void IteratorChecker::verifyAccess(CheckerContext &C, const SVal &Val) const {
938 auto State = C.getState();
939 const auto *Pos = getIteratorPosition(State, Val);
940 if (Pos && !Pos->isValid()) {
941 auto *N = C.generateErrorNode(State);
945 reportInvalidatedBug("Invalidated iterator accessed.", Val, C, N);
949 void IteratorChecker::handleIncrement(CheckerContext &C, const SVal &RetVal,
950 const SVal &Iter, bool Postfix) const {
951 // Increment the symbolic expressions which represents the position of the
953 auto State = C.getState();
954 const auto *Pos = getIteratorPosition(State, Iter);
956 auto &SymMgr = C.getSymbolManager();
957 auto &BVF = SymMgr.getBasicVals();
959 advancePosition(C, OO_Plus, *Pos,
960 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
961 State = setIteratorPosition(State, Iter, NewPos);
962 State = setIteratorPosition(State, RetVal, Postfix ? *Pos : NewPos);
963 C.addTransition(State);
967 void IteratorChecker::handleDecrement(CheckerContext &C, const SVal &RetVal,
968 const SVal &Iter, bool Postfix) const {
969 // Decrement the symbolic expressions which represents the position of the
971 auto State = C.getState();
972 const auto *Pos = getIteratorPosition(State, Iter);
974 auto &SymMgr = C.getSymbolManager();
975 auto &BVF = SymMgr.getBasicVals();
977 advancePosition(C, OO_Minus, *Pos,
978 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
979 State = setIteratorPosition(State, Iter, NewPos);
980 State = setIteratorPosition(State, RetVal, Postfix ? *Pos : NewPos);
981 C.addTransition(State);
985 void IteratorChecker::handleRandomIncrOrDecr(CheckerContext &C,
986 OverloadedOperatorKind Op,
989 const SVal &RHS) const {
990 // Increment or decrement the symbolic expressions which represents the
991 // position of the iterator
992 auto State = C.getState();
993 const auto *Pos = getIteratorPosition(State, LHS);
997 const auto *value = &RHS;
998 if (auto loc = RHS.getAs<Loc>()) {
999 const auto val = State->getRawSVal(*loc);
1003 auto &TgtVal = (Op == OO_PlusEqual || Op == OO_MinusEqual) ? LHS : RetVal;
1005 setIteratorPosition(State, TgtVal, advancePosition(C, Op, *Pos, *value));
1006 C.addTransition(State);
1009 void IteratorChecker::verifyIncrement(CheckerContext &C,
1010 const SVal &Iter) const {
1011 auto &BVF = C.getSValBuilder().getBasicValueFactory();
1012 verifyRandomIncrOrDecr(C, OO_Plus, Iter,
1013 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
1016 void IteratorChecker::verifyDecrement(CheckerContext &C,
1017 const SVal &Iter) const {
1018 auto &BVF = C.getSValBuilder().getBasicValueFactory();
1019 verifyRandomIncrOrDecr(C, OO_Minus, Iter,
1020 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
1023 void IteratorChecker::verifyRandomIncrOrDecr(CheckerContext &C,
1024 OverloadedOperatorKind Op,
1026 const SVal &RHS) const {
1027 auto State = C.getState();
1029 // If the iterator is initially inside its range, then the operation is valid
1030 const auto *Pos = getIteratorPosition(State, LHS);
1035 if (auto ValAsLoc = RHS.getAs<Loc>()) {
1036 Value = State->getRawSVal(*ValAsLoc);
1039 if (Value.isUnknown())
1042 // Incremention or decremention by 0 is never a bug.
1043 if (isZero(State, Value.castAs<NonLoc>()))
1046 // The result may be the past-end iterator of the container, but any other
1047 // out of range position is undefined behaviour
1048 if (isAheadOfRange(State, advancePosition(C, Op, *Pos, Value))) {
1049 auto *N = C.generateErrorNode(State);
1052 reportOutOfRangeBug("Iterator decremented ahead of its valid range.", LHS,
1055 if (isBehindPastTheEnd(State, advancePosition(C, Op, *Pos, Value))) {
1056 auto *N = C.generateErrorNode(State);
1059 reportOutOfRangeBug("Iterator incremented behind the past-the-end "
1060 "iterator.", LHS, C, N);
1064 void IteratorChecker::verifyMatch(CheckerContext &C, const SVal &Iter,
1065 const MemRegion *Cont) const {
1066 // Verify match between a container and the container of an iterator
1067 Cont = Cont->getMostDerivedObjectRegion();
1069 if (const auto *ContSym = Cont->getSymbolicBase()) {
1070 if (isa<SymbolConjured>(ContSym->getSymbol()))
1074 auto State = C.getState();
1075 const auto *Pos = getIteratorPosition(State, Iter);
1079 const auto *IterCont = Pos->getContainer();
1081 // Skip symbolic regions based on conjured symbols. Two conjured symbols
1082 // may or may not be the same. For example, the same function can return
1083 // the same or a different container but we get different conjured symbols
1084 // for each call. This may cause false positives so omit them from the check.
1085 if (const auto *ContSym = IterCont->getSymbolicBase()) {
1086 if (isa<SymbolConjured>(ContSym->getSymbol()))
1090 if (IterCont != Cont) {
1091 auto *N = C.generateNonFatalErrorNode(State);
1095 reportMismatchedBug("Container accessed using foreign iterator argument.",
1100 void IteratorChecker::verifyMatch(CheckerContext &C, const SVal &Iter1,
1101 const SVal &Iter2) const {
1102 // Verify match between the containers of two iterators
1103 auto State = C.getState();
1104 const auto *Pos1 = getIteratorPosition(State, Iter1);
1108 const auto *IterCont1 = Pos1->getContainer();
1110 // Skip symbolic regions based on conjured symbols. Two conjured symbols
1111 // may or may not be the same. For example, the same function can return
1112 // the same or a different container but we get different conjured symbols
1113 // for each call. This may cause false positives so omit them from the check.
1114 if (const auto *ContSym = IterCont1->getSymbolicBase()) {
1115 if (isa<SymbolConjured>(ContSym->getSymbol()))
1119 const auto *Pos2 = getIteratorPosition(State, Iter2);
1123 const auto *IterCont2 = Pos2->getContainer();
1124 if (const auto *ContSym = IterCont2->getSymbolicBase()) {
1125 if (isa<SymbolConjured>(ContSym->getSymbol()))
1129 if (IterCont1 != IterCont2) {
1130 auto *N = C.generateNonFatalErrorNode(State);
1133 reportMismatchedBug("Iterators of different containers used where the "
1134 "same container is expected.", Iter1, Iter2, C, N);
1138 void IteratorChecker::handleBegin(CheckerContext &C, const Expr *CE,
1139 const SVal &RetVal, const SVal &Cont) const {
1140 const auto *ContReg = Cont.getAsRegion();
1144 ContReg = ContReg->getMostDerivedObjectRegion();
1146 // If the container already has a begin symbol then use it. Otherwise first
1147 // create a new one.
1148 auto State = C.getState();
1149 auto BeginSym = getContainerBegin(State, ContReg);
1151 State = createContainerBegin(State, ContReg, CE, C.getASTContext().LongTy,
1152 C.getLocationContext(), C.blockCount());
1153 BeginSym = getContainerBegin(State, ContReg);
1155 State = setIteratorPosition(State, RetVal,
1156 IteratorPosition::getPosition(ContReg, BeginSym));
1157 C.addTransition(State);
1160 void IteratorChecker::handleEnd(CheckerContext &C, const Expr *CE,
1161 const SVal &RetVal, const SVal &Cont) const {
1162 const auto *ContReg = Cont.getAsRegion();
1166 ContReg = ContReg->getMostDerivedObjectRegion();
1168 // If the container already has an end symbol then use it. Otherwise first
1169 // create a new one.
1170 auto State = C.getState();
1171 auto EndSym = getContainerEnd(State, ContReg);
1173 State = createContainerEnd(State, ContReg, CE, C.getASTContext().LongTy,
1174 C.getLocationContext(), C.blockCount());
1175 EndSym = getContainerEnd(State, ContReg);
1177 State = setIteratorPosition(State, RetVal,
1178 IteratorPosition::getPosition(ContReg, EndSym));
1179 C.addTransition(State);
1182 void IteratorChecker::assignToContainer(CheckerContext &C, const Expr *CE,
1184 const MemRegion *Cont) const {
1185 Cont = Cont->getMostDerivedObjectRegion();
1187 auto State = C.getState();
1188 auto &SymMgr = C.getSymbolManager();
1189 auto Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
1190 C.getASTContext().LongTy, C.blockCount());
1191 State = assumeNoOverflow(State, Sym, 4);
1192 State = setIteratorPosition(State, RetVal,
1193 IteratorPosition::getPosition(Cont, Sym));
1194 C.addTransition(State);
1197 void IteratorChecker::handleAssign(CheckerContext &C, const SVal &Cont,
1198 const Expr *CE, const SVal &OldCont) const {
1199 const auto *ContReg = Cont.getAsRegion();
1203 ContReg = ContReg->getMostDerivedObjectRegion();
1205 // Assignment of a new value to a container always invalidates all its
1207 auto State = C.getState();
1208 const auto CData = getContainerData(State, ContReg);
1210 State = invalidateAllIteratorPositions(State, ContReg);
1213 // In case of move, iterators of the old container (except the past-end
1214 // iterators) remain valid but refer to the new container
1215 if (!OldCont.isUndef()) {
1216 const auto *OldContReg = OldCont.getAsRegion();
1218 OldContReg = OldContReg->getMostDerivedObjectRegion();
1219 const auto OldCData = getContainerData(State, OldContReg);
1221 if (const auto OldEndSym = OldCData->getEnd()) {
1222 // If we already assigned an "end" symbol to the old container, then
1223 // first reassign all iterator positions to the new container which
1224 // are not past the container (thus not greater or equal to the
1225 // current "end" symbol).
1226 State = reassignAllIteratorPositionsUnless(State, OldContReg, ContReg,
1228 auto &SymMgr = C.getSymbolManager();
1229 auto &SVB = C.getSValBuilder();
1230 // Then generate and assign a new "end" symbol for the new container.
1232 SymMgr.conjureSymbol(CE, C.getLocationContext(),
1233 C.getASTContext().LongTy, C.blockCount());
1234 State = assumeNoOverflow(State, NewEndSym, 4);
1236 State = setContainerData(State, ContReg, CData->newEnd(NewEndSym));
1238 State = setContainerData(State, ContReg,
1239 ContainerData::fromEnd(NewEndSym));
1241 // Finally, replace the old "end" symbol in the already reassigned
1242 // iterator positions with the new "end" symbol.
1243 State = rebaseSymbolInIteratorPositionsIf(
1244 State, SVB, OldEndSym, NewEndSym, OldEndSym, BO_LT);
1246 // There was no "end" symbol assigned yet to the old container,
1247 // so reassign all iterator positions to the new container.
1248 State = reassignAllIteratorPositions(State, OldContReg, ContReg);
1250 if (const auto OldBeginSym = OldCData->getBegin()) {
1251 // If we already assigned a "begin" symbol to the old container, then
1252 // assign it to the new container and remove it from the old one.
1255 setContainerData(State, ContReg, CData->newBegin(OldBeginSym));
1257 State = setContainerData(State, ContReg,
1258 ContainerData::fromBegin(OldBeginSym));
1261 setContainerData(State, OldContReg, OldCData->newEnd(nullptr));
1264 // There was neither "begin" nor "end" symbol assigned yet to the old
1265 // container, so reassign all iterator positions to the new container.
1266 State = reassignAllIteratorPositions(State, OldContReg, ContReg);
1270 C.addTransition(State);
1273 void IteratorChecker::handleClear(CheckerContext &C, const SVal &Cont) const {
1274 const auto *ContReg = Cont.getAsRegion();
1278 ContReg = ContReg->getMostDerivedObjectRegion();
1280 // The clear() operation invalidates all the iterators, except the past-end
1281 // iterators of list-like containers
1282 auto State = C.getState();
1283 if (!hasSubscriptOperator(State, ContReg) ||
1284 !backModifiable(State, ContReg)) {
1285 const auto CData = getContainerData(State, ContReg);
1287 if (const auto EndSym = CData->getEnd()) {
1289 invalidateAllIteratorPositionsExcept(State, ContReg, EndSym, BO_GE);
1290 C.addTransition(State);
1295 State = invalidateAllIteratorPositions(State, ContReg);
1296 C.addTransition(State);
1299 void IteratorChecker::handlePushBack(CheckerContext &C,
1300 const SVal &Cont) const {
1301 const auto *ContReg = Cont.getAsRegion();
1305 ContReg = ContReg->getMostDerivedObjectRegion();
1307 // For deque-like containers invalidate all iterator positions
1308 auto State = C.getState();
1309 if (hasSubscriptOperator(State, ContReg) && frontModifiable(State, ContReg)) {
1310 State = invalidateAllIteratorPositions(State, ContReg);
1311 C.addTransition(State);
1315 const auto CData = getContainerData(State, ContReg);
1319 // For vector-like containers invalidate the past-end iterator positions
1320 if (const auto EndSym = CData->getEnd()) {
1321 if (hasSubscriptOperator(State, ContReg)) {
1322 State = invalidateIteratorPositions(State, EndSym, BO_GE);
1324 auto &SymMgr = C.getSymbolManager();
1325 auto &BVF = SymMgr.getBasicVals();
1326 auto &SVB = C.getSValBuilder();
1327 const auto newEndSym =
1328 SVB.evalBinOp(State, BO_Add,
1329 nonloc::SymbolVal(EndSym),
1330 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
1331 SymMgr.getType(EndSym)).getAsSymbol();
1332 State = setContainerData(State, ContReg, CData->newEnd(newEndSym));
1334 C.addTransition(State);
1337 void IteratorChecker::handlePopBack(CheckerContext &C, const SVal &Cont) const {
1338 const auto *ContReg = Cont.getAsRegion();
1342 ContReg = ContReg->getMostDerivedObjectRegion();
1344 auto State = C.getState();
1345 const auto CData = getContainerData(State, ContReg);
1349 if (const auto EndSym = CData->getEnd()) {
1350 auto &SymMgr = C.getSymbolManager();
1351 auto &BVF = SymMgr.getBasicVals();
1352 auto &SVB = C.getSValBuilder();
1353 const auto BackSym =
1354 SVB.evalBinOp(State, BO_Sub,
1355 nonloc::SymbolVal(EndSym),
1356 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
1357 SymMgr.getType(EndSym)).getAsSymbol();
1358 // For vector-like and deque-like containers invalidate the last and the
1359 // past-end iterator positions. For list-like containers only invalidate
1360 // the last position
1361 if (hasSubscriptOperator(State, ContReg) &&
1362 backModifiable(State, ContReg)) {
1363 State = invalidateIteratorPositions(State, BackSym, BO_GE);
1364 State = setContainerData(State, ContReg, CData->newEnd(nullptr));
1366 State = invalidateIteratorPositions(State, BackSym, BO_EQ);
1368 auto newEndSym = BackSym;
1369 State = setContainerData(State, ContReg, CData->newEnd(newEndSym));
1370 C.addTransition(State);
1374 void IteratorChecker::handlePushFront(CheckerContext &C,
1375 const SVal &Cont) const {
1376 const auto *ContReg = Cont.getAsRegion();
1380 ContReg = ContReg->getMostDerivedObjectRegion();
1382 // For deque-like containers invalidate all iterator positions
1383 auto State = C.getState();
1384 if (hasSubscriptOperator(State, ContReg)) {
1385 State = invalidateAllIteratorPositions(State, ContReg);
1386 C.addTransition(State);
1388 const auto CData = getContainerData(State, ContReg);
1392 if (const auto BeginSym = CData->getBegin()) {
1393 auto &SymMgr = C.getSymbolManager();
1394 auto &BVF = SymMgr.getBasicVals();
1395 auto &SVB = C.getSValBuilder();
1396 const auto newBeginSym =
1397 SVB.evalBinOp(State, BO_Sub,
1398 nonloc::SymbolVal(BeginSym),
1399 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
1400 SymMgr.getType(BeginSym)).getAsSymbol();
1401 State = setContainerData(State, ContReg, CData->newBegin(newBeginSym));
1402 C.addTransition(State);
1407 void IteratorChecker::handlePopFront(CheckerContext &C,
1408 const SVal &Cont) const {
1409 const auto *ContReg = Cont.getAsRegion();
1413 ContReg = ContReg->getMostDerivedObjectRegion();
1415 auto State = C.getState();
1416 const auto CData = getContainerData(State, ContReg);
1420 // For deque-like containers invalidate all iterator positions. For list-like
1421 // iterators only invalidate the first position
1422 if (const auto BeginSym = CData->getBegin()) {
1423 if (hasSubscriptOperator(State, ContReg)) {
1424 State = invalidateIteratorPositions(State, BeginSym, BO_LE);
1426 State = invalidateIteratorPositions(State, BeginSym, BO_EQ);
1428 auto &SymMgr = C.getSymbolManager();
1429 auto &BVF = SymMgr.getBasicVals();
1430 auto &SVB = C.getSValBuilder();
1431 const auto newBeginSym =
1432 SVB.evalBinOp(State, BO_Add,
1433 nonloc::SymbolVal(BeginSym),
1434 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
1435 SymMgr.getType(BeginSym)).getAsSymbol();
1436 State = setContainerData(State, ContReg, CData->newBegin(newBeginSym));
1437 C.addTransition(State);
1441 void IteratorChecker::handleInsert(CheckerContext &C, const SVal &Iter) const {
1442 auto State = C.getState();
1443 const auto *Pos = getIteratorPosition(State, Iter);
1447 // For deque-like containers invalidate all iterator positions. For
1448 // vector-like containers invalidate iterator positions after the insertion.
1449 const auto *Cont = Pos->getContainer();
1450 if (hasSubscriptOperator(State, Cont) && backModifiable(State, Cont)) {
1451 if (frontModifiable(State, Cont)) {
1452 State = invalidateAllIteratorPositions(State, Cont);
1454 State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE);
1456 if (const auto *CData = getContainerData(State, Cont)) {
1457 if (const auto EndSym = CData->getEnd()) {
1458 State = invalidateIteratorPositions(State, EndSym, BO_GE);
1459 State = setContainerData(State, Cont, CData->newEnd(nullptr));
1462 C.addTransition(State);
1466 void IteratorChecker::handleErase(CheckerContext &C, const SVal &Iter) const {
1467 auto State = C.getState();
1468 const auto *Pos = getIteratorPosition(State, Iter);
1472 // For deque-like containers invalidate all iterator positions. For
1473 // vector-like containers invalidate iterator positions at and after the
1474 // deletion. For list-like containers only invalidate the deleted position.
1475 const auto *Cont = Pos->getContainer();
1476 if (hasSubscriptOperator(State, Cont) && backModifiable(State, Cont)) {
1477 if (frontModifiable(State, Cont)) {
1478 State = invalidateAllIteratorPositions(State, Cont);
1480 State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE);
1482 if (const auto *CData = getContainerData(State, Cont)) {
1483 if (const auto EndSym = CData->getEnd()) {
1484 State = invalidateIteratorPositions(State, EndSym, BO_GE);
1485 State = setContainerData(State, Cont, CData->newEnd(nullptr));
1489 State = invalidateIteratorPositions(State, Pos->getOffset(), BO_EQ);
1491 C.addTransition(State);
1494 void IteratorChecker::handleErase(CheckerContext &C, const SVal &Iter1,
1495 const SVal &Iter2) const {
1496 auto State = C.getState();
1497 const auto *Pos1 = getIteratorPosition(State, Iter1);
1498 const auto *Pos2 = getIteratorPosition(State, Iter2);
1502 // For deque-like containers invalidate all iterator positions. For
1503 // vector-like containers invalidate iterator positions at and after the
1504 // deletion range. For list-like containers only invalidate the deleted
1505 // position range [first..last].
1506 const auto *Cont = Pos1->getContainer();
1507 if (hasSubscriptOperator(State, Cont) && backModifiable(State, Cont)) {
1508 if (frontModifiable(State, Cont)) {
1509 State = invalidateAllIteratorPositions(State, Cont);
1511 State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE);
1513 if (const auto *CData = getContainerData(State, Cont)) {
1514 if (const auto EndSym = CData->getEnd()) {
1515 State = invalidateIteratorPositions(State, EndSym, BO_GE);
1516 State = setContainerData(State, Cont, CData->newEnd(nullptr));
1520 State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE,
1521 Pos2->getOffset(), BO_LT);
1523 C.addTransition(State);
1526 void IteratorChecker::handleEraseAfter(CheckerContext &C,
1527 const SVal &Iter) const {
1528 auto State = C.getState();
1529 const auto *Pos = getIteratorPosition(State, Iter);
1533 // Invalidate the deleted iterator position, which is the position of the
1534 // parameter plus one.
1535 auto &SymMgr = C.getSymbolManager();
1536 auto &BVF = SymMgr.getBasicVals();
1537 auto &SVB = C.getSValBuilder();
1538 const auto NextSym =
1539 SVB.evalBinOp(State, BO_Add,
1540 nonloc::SymbolVal(Pos->getOffset()),
1541 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
1542 SymMgr.getType(Pos->getOffset())).getAsSymbol();
1543 State = invalidateIteratorPositions(State, NextSym, BO_EQ);
1544 C.addTransition(State);
1547 void IteratorChecker::handleEraseAfter(CheckerContext &C, const SVal &Iter1,
1548 const SVal &Iter2) const {
1549 auto State = C.getState();
1550 const auto *Pos1 = getIteratorPosition(State, Iter1);
1551 const auto *Pos2 = getIteratorPosition(State, Iter2);
1555 // Invalidate the deleted iterator position range (first..last)
1556 State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GT,
1557 Pos2->getOffset(), BO_LT);
1558 C.addTransition(State);
1561 IteratorPosition IteratorChecker::advancePosition(CheckerContext &C,
1562 OverloadedOperatorKind Op,
1563 const IteratorPosition &Pos,
1564 const SVal &Distance) const {
1565 auto State = C.getState();
1566 auto &SymMgr = C.getSymbolManager();
1567 auto &SVB = C.getSValBuilder();
1569 assert ((Op == OO_Plus || Op == OO_PlusEqual ||
1570 Op == OO_Minus || Op == OO_MinusEqual) &&
1571 "Advance operator must be one of +, -, += and -=.");
1572 auto BinOp = (Op == OO_Plus || Op == OO_PlusEqual) ? BO_Add : BO_Sub;
1573 if (const auto IntDist = Distance.getAs<nonloc::ConcreteInt>()) {
1574 // For concrete integers we can calculate the new position
1575 return Pos.setTo(SVB.evalBinOp(State, BinOp,
1576 nonloc::SymbolVal(Pos.getOffset()), *IntDist,
1577 SymMgr.getType(Pos.getOffset()))
1580 // For other symbols create a new symbol to keep expressions simple
1581 const auto &LCtx = C.getLocationContext();
1582 const auto NewPosSym = SymMgr.conjureSymbol(nullptr, LCtx,
1583 SymMgr.getType(Pos.getOffset()),
1585 State = assumeNoOverflow(State, NewPosSym, 4);
1586 return Pos.setTo(NewPosSym);
1590 void IteratorChecker::reportOutOfRangeBug(const StringRef &Message,
1591 const SVal &Val, CheckerContext &C,
1592 ExplodedNode *ErrNode) const {
1593 auto R = std::make_unique<PathSensitiveBugReport>(*OutOfRangeBugType, Message,
1595 R->markInteresting(Val);
1596 C.emitReport(std::move(R));
1599 void IteratorChecker::reportMismatchedBug(const StringRef &Message,
1600 const SVal &Val1, const SVal &Val2,
1602 ExplodedNode *ErrNode) const {
1603 auto R = std::make_unique<PathSensitiveBugReport>(*MismatchedBugType, Message,
1605 R->markInteresting(Val1);
1606 R->markInteresting(Val2);
1607 C.emitReport(std::move(R));
1610 void IteratorChecker::reportMismatchedBug(const StringRef &Message,
1611 const SVal &Val, const MemRegion *Reg,
1613 ExplodedNode *ErrNode) const {
1614 auto R = std::make_unique<PathSensitiveBugReport>(*MismatchedBugType, Message,
1616 R->markInteresting(Val);
1617 R->markInteresting(Reg);
1618 C.emitReport(std::move(R));
1621 void IteratorChecker::reportInvalidatedBug(const StringRef &Message,
1622 const SVal &Val, CheckerContext &C,
1623 ExplodedNode *ErrNode) const {
1624 auto R = std::make_unique<PathSensitiveBugReport>(*InvalidatedBugType,
1626 R->markInteresting(Val);
1627 C.emitReport(std::move(R));
1632 bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
1633 bool isGreater(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
1634 bool isEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
1635 bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
1636 BinaryOperator::Opcode Opc);
1637 bool compare(ProgramStateRef State, NonLoc NL1, NonLoc NL2,
1638 BinaryOperator::Opcode Opc);
1639 const CXXRecordDecl *getCXXRecordDecl(ProgramStateRef State,
1640 const MemRegion *Reg);
1641 SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB, SymbolRef Expr,
1642 SymbolRef OldSym, SymbolRef NewSym);
1644 bool isIteratorType(const QualType &Type) {
1645 if (Type->isPointerType())
1648 const auto *CRD = Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
1649 return isIterator(CRD);
1652 bool isIterator(const CXXRecordDecl *CRD) {
1656 const auto Name = CRD->getName();
1657 if (!(Name.endswith_lower("iterator") || Name.endswith_lower("iter") ||
1658 Name.endswith_lower("it")))
1661 bool HasCopyCtor = false, HasCopyAssign = true, HasDtor = false,
1662 HasPreIncrOp = false, HasPostIncrOp = false, HasDerefOp = false;
1663 for (const auto *Method : CRD->methods()) {
1664 if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(Method)) {
1665 if (Ctor->isCopyConstructor()) {
1666 HasCopyCtor = !Ctor->isDeleted() && Ctor->getAccess() == AS_public;
1670 if (const auto *Dtor = dyn_cast<CXXDestructorDecl>(Method)) {
1671 HasDtor = !Dtor->isDeleted() && Dtor->getAccess() == AS_public;
1674 if (Method->isCopyAssignmentOperator()) {
1675 HasCopyAssign = !Method->isDeleted() && Method->getAccess() == AS_public;
1678 if (!Method->isOverloadedOperator())
1680 const auto OPK = Method->getOverloadedOperator();
1681 if (OPK == OO_PlusPlus) {
1682 HasPreIncrOp = HasPreIncrOp || (Method->getNumParams() == 0);
1683 HasPostIncrOp = HasPostIncrOp || (Method->getNumParams() == 1);
1686 if (OPK == OO_Star) {
1687 HasDerefOp = (Method->getNumParams() == 0);
1692 return HasCopyCtor && HasCopyAssign && HasDtor && HasPreIncrOp &&
1693 HasPostIncrOp && HasDerefOp;
1696 bool isComparisonOperator(OverloadedOperatorKind OK) {
1697 return OK == OO_EqualEqual || OK == OO_ExclaimEqual || OK == OO_Less ||
1698 OK == OO_LessEqual || OK == OO_Greater || OK == OO_GreaterEqual;
1701 bool isBeginCall(const FunctionDecl *Func) {
1702 const auto *IdInfo = Func->getIdentifier();
1705 return IdInfo->getName().endswith_lower("begin");
1708 bool isEndCall(const FunctionDecl *Func) {
1709 const auto *IdInfo = Func->getIdentifier();
1712 return IdInfo->getName().endswith_lower("end");
1715 bool isAssignCall(const FunctionDecl *Func) {
1716 const auto *IdInfo = Func->getIdentifier();
1719 if (Func->getNumParams() > 2)
1721 return IdInfo->getName() == "assign";
1724 bool isClearCall(const FunctionDecl *Func) {
1725 const auto *IdInfo = Func->getIdentifier();
1728 if (Func->getNumParams() > 0)
1730 return IdInfo->getName() == "clear";
1733 bool isPushBackCall(const FunctionDecl *Func) {
1734 const auto *IdInfo = Func->getIdentifier();
1737 if (Func->getNumParams() != 1)
1739 return IdInfo->getName() == "push_back";
1742 bool isEmplaceBackCall(const FunctionDecl *Func) {
1743 const auto *IdInfo = Func->getIdentifier();
1746 if (Func->getNumParams() < 1)
1748 return IdInfo->getName() == "emplace_back";
1751 bool isPopBackCall(const FunctionDecl *Func) {
1752 const auto *IdInfo = Func->getIdentifier();
1755 if (Func->getNumParams() > 0)
1757 return IdInfo->getName() == "pop_back";
1760 bool isPushFrontCall(const FunctionDecl *Func) {
1761 const auto *IdInfo = Func->getIdentifier();
1764 if (Func->getNumParams() != 1)
1766 return IdInfo->getName() == "push_front";
1769 bool isEmplaceFrontCall(const FunctionDecl *Func) {
1770 const auto *IdInfo = Func->getIdentifier();
1773 if (Func->getNumParams() < 1)
1775 return IdInfo->getName() == "emplace_front";
1778 bool isPopFrontCall(const FunctionDecl *Func) {
1779 const auto *IdInfo = Func->getIdentifier();
1782 if (Func->getNumParams() > 0)
1784 return IdInfo->getName() == "pop_front";
1787 bool isInsertCall(const FunctionDecl *Func) {
1788 const auto *IdInfo = Func->getIdentifier();
1791 if (Func->getNumParams() < 2 || Func->getNumParams() > 3)
1793 if (!isIteratorType(Func->getParamDecl(0)->getType()))
1795 return IdInfo->getName() == "insert";
1798 bool isEmplaceCall(const FunctionDecl *Func) {
1799 const auto *IdInfo = Func->getIdentifier();
1802 if (Func->getNumParams() < 2)
1804 if (!isIteratorType(Func->getParamDecl(0)->getType()))
1806 return IdInfo->getName() == "emplace";
1809 bool isEraseCall(const FunctionDecl *Func) {
1810 const auto *IdInfo = Func->getIdentifier();
1813 if (Func->getNumParams() < 1 || Func->getNumParams() > 2)
1815 if (!isIteratorType(Func->getParamDecl(0)->getType()))
1817 if (Func->getNumParams() == 2 &&
1818 !isIteratorType(Func->getParamDecl(1)->getType()))
1820 return IdInfo->getName() == "erase";
1823 bool isEraseAfterCall(const FunctionDecl *Func) {
1824 const auto *IdInfo = Func->getIdentifier();
1827 if (Func->getNumParams() < 1 || Func->getNumParams() > 2)
1829 if (!isIteratorType(Func->getParamDecl(0)->getType()))
1831 if (Func->getNumParams() == 2 &&
1832 !isIteratorType(Func->getParamDecl(1)->getType()))
1834 return IdInfo->getName() == "erase_after";
1837 bool isAssignmentOperator(OverloadedOperatorKind OK) { return OK == OO_Equal; }
1839 bool isSimpleComparisonOperator(OverloadedOperatorKind OK) {
1840 return OK == OO_EqualEqual || OK == OO_ExclaimEqual;
1843 bool isAccessOperator(OverloadedOperatorKind OK) {
1844 return isDereferenceOperator(OK) || isIncrementOperator(OK) ||
1845 isDecrementOperator(OK) || isRandomIncrOrDecrOperator(OK);
1848 bool isDereferenceOperator(OverloadedOperatorKind OK) {
1849 return OK == OO_Star || OK == OO_Arrow || OK == OO_ArrowStar ||
1853 bool isIncrementOperator(OverloadedOperatorKind OK) {
1854 return OK == OO_PlusPlus;
1857 bool isDecrementOperator(OverloadedOperatorKind OK) {
1858 return OK == OO_MinusMinus;
1861 bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK) {
1862 return OK == OO_Plus || OK == OO_PlusEqual || OK == OO_Minus ||
1863 OK == OO_MinusEqual;
1866 bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg) {
1867 const auto *CRD = getCXXRecordDecl(State, Reg);
1871 for (const auto *Method : CRD->methods()) {
1872 if (!Method->isOverloadedOperator())
1874 const auto OPK = Method->getOverloadedOperator();
1875 if (OPK == OO_Subscript) {
1882 bool frontModifiable(ProgramStateRef State, const MemRegion *Reg) {
1883 const auto *CRD = getCXXRecordDecl(State, Reg);
1887 for (const auto *Method : CRD->methods()) {
1888 if (!Method->getDeclName().isIdentifier())
1890 if (Method->getName() == "push_front" || Method->getName() == "pop_front") {
1897 bool backModifiable(ProgramStateRef State, const MemRegion *Reg) {
1898 const auto *CRD = getCXXRecordDecl(State, Reg);
1902 for (const auto *Method : CRD->methods()) {
1903 if (!Method->getDeclName().isIdentifier())
1905 if (Method->getName() == "push_back" || Method->getName() == "pop_back") {
1912 const CXXRecordDecl *getCXXRecordDecl(ProgramStateRef State,
1913 const MemRegion *Reg) {
1914 auto TI = getDynamicTypeInfo(State, Reg);
1918 auto Type = TI.getType();
1919 if (const auto *RefT = Type->getAs<ReferenceType>()) {
1920 Type = RefT->getPointeeType();
1923 return Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
1926 SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont) {
1927 const auto *CDataPtr = getContainerData(State, Cont);
1931 return CDataPtr->getBegin();
1934 SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont) {
1935 const auto *CDataPtr = getContainerData(State, Cont);
1939 return CDataPtr->getEnd();
1942 ProgramStateRef createContainerBegin(ProgramStateRef State,
1943 const MemRegion *Cont, const Expr *E,
1944 QualType T, const LocationContext *LCtx,
1945 unsigned BlockCount) {
1946 // Only create if it does not exist
1947 const auto *CDataPtr = getContainerData(State, Cont);
1948 if (CDataPtr && CDataPtr->getBegin())
1951 auto &SymMgr = State->getSymbolManager();
1952 const SymbolConjured *Sym = SymMgr.conjureSymbol(E, LCtx, T, BlockCount,
1954 State = assumeNoOverflow(State, Sym, 4);
1957 const auto CData = CDataPtr->newBegin(Sym);
1958 return setContainerData(State, Cont, CData);
1961 const auto CData = ContainerData::fromBegin(Sym);
1962 return setContainerData(State, Cont, CData);
1965 ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
1966 const Expr *E, QualType T,
1967 const LocationContext *LCtx,
1968 unsigned BlockCount) {
1969 // Only create if it does not exist
1970 const auto *CDataPtr = getContainerData(State, Cont);
1971 if (CDataPtr && CDataPtr->getEnd())
1974 auto &SymMgr = State->getSymbolManager();
1975 const SymbolConjured *Sym = SymMgr.conjureSymbol(E, LCtx, T, BlockCount,
1977 State = assumeNoOverflow(State, Sym, 4);
1980 const auto CData = CDataPtr->newEnd(Sym);
1981 return setContainerData(State, Cont, CData);
1984 const auto CData = ContainerData::fromEnd(Sym);
1985 return setContainerData(State, Cont, CData);
1988 const ContainerData *getContainerData(ProgramStateRef State,
1989 const MemRegion *Cont) {
1990 return State->get<ContainerMap>(Cont);
1993 ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
1994 const ContainerData &CData) {
1995 return State->set<ContainerMap>(Cont, CData);
1998 const IteratorPosition *getIteratorPosition(ProgramStateRef State,
2000 if (auto Reg = Val.getAsRegion()) {
2001 Reg = Reg->getMostDerivedObjectRegion();
2002 return State->get<IteratorRegionMap>(Reg);
2003 } else if (const auto Sym = Val.getAsSymbol()) {
2004 return State->get<IteratorSymbolMap>(Sym);
2005 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
2006 return State->get<IteratorRegionMap>(LCVal->getRegion());
2011 ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
2012 const IteratorPosition &Pos) {
2013 if (auto Reg = Val.getAsRegion()) {
2014 Reg = Reg->getMostDerivedObjectRegion();
2015 return State->set<IteratorRegionMap>(Reg, Pos);
2016 } else if (const auto Sym = Val.getAsSymbol()) {
2017 return State->set<IteratorSymbolMap>(Sym, Pos);
2018 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
2019 return State->set<IteratorRegionMap>(LCVal->getRegion(), Pos);
2024 ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val) {
2025 if (auto Reg = Val.getAsRegion()) {
2026 Reg = Reg->getMostDerivedObjectRegion();
2027 return State->remove<IteratorRegionMap>(Reg);
2028 } else if (const auto Sym = Val.getAsSymbol()) {
2029 return State->remove<IteratorSymbolMap>(Sym);
2030 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
2031 return State->remove<IteratorRegionMap>(LCVal->getRegion());
2036 ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1,
2037 SymbolRef Sym2, bool Equal) {
2038 auto &SVB = State->getStateManager().getSValBuilder();
2040 // FIXME: This code should be reworked as follows:
2041 // 1. Subtract the operands using evalBinOp().
2042 // 2. Assume that the result doesn't overflow.
2043 // 3. Compare the result to 0.
2044 // 4. Assume the result of the comparison.
2045 const auto comparison =
2046 SVB.evalBinOp(State, BO_EQ, nonloc::SymbolVal(Sym1),
2047 nonloc::SymbolVal(Sym2), SVB.getConditionType());
2049 assert(comparison.getAs<DefinedSVal>() &&
2050 "Symbol comparison must be a `DefinedSVal`");
2052 auto NewState = State->assume(comparison.castAs<DefinedSVal>(), Equal);
2056 if (const auto CompSym = comparison.getAsSymbol()) {
2057 assert(isa<SymIntExpr>(CompSym) &&
2058 "Symbol comparison must be a `SymIntExpr`");
2059 assert(BinaryOperator::isComparisonOp(
2060 cast<SymIntExpr>(CompSym)->getOpcode()) &&
2061 "Symbol comparison must be a comparison");
2062 return assumeNoOverflow(NewState, cast<SymIntExpr>(CompSym)->getLHS(), 2);
2068 bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont) {
2069 auto RegionMap = State->get<IteratorRegionMap>();
2070 for (const auto Reg : RegionMap) {
2071 if (Reg.second.getContainer() == Cont)
2075 auto SymbolMap = State->get<IteratorSymbolMap>();
2076 for (const auto Sym : SymbolMap) {
2077 if (Sym.second.getContainer() == Cont)
2084 bool isBoundThroughLazyCompoundVal(const Environment &Env,
2085 const MemRegion *Reg) {
2086 for (const auto Binding: Env) {
2087 if (const auto LCVal = Binding.second.getAs<nonloc::LazyCompoundVal>()) {
2088 if (LCVal->getRegion() == Reg)
2096 // This function tells the analyzer's engine that symbols produced by our
2097 // checker, most notably iterator positions, are relatively small.
2098 // A distance between items in the container should not be very large.
2099 // By assuming that it is within around 1/8 of the address space,
2100 // we can help the analyzer perform operations on these symbols
2101 // without being afraid of integer overflows.
2102 // FIXME: Should we provide it as an API, so that all checkers could use it?
2103 ProgramStateRef assumeNoOverflow(ProgramStateRef State, SymbolRef Sym,
2105 SValBuilder &SVB = State->getStateManager().getSValBuilder();
2106 BasicValueFactory &BV = SVB.getBasicValueFactory();
2108 QualType T = Sym->getType();
2109 assert(T->isSignedIntegerOrEnumerationType());
2110 APSIntType AT = BV.getAPSIntType(T);
2112 ProgramStateRef NewState = State;
2114 llvm::APSInt Max = AT.getMaxValue() / AT.getValue(Scale);
2115 SVal IsCappedFromAbove =
2116 SVB.evalBinOpNN(State, BO_LE, nonloc::SymbolVal(Sym),
2117 nonloc::ConcreteInt(Max), SVB.getConditionType());
2118 if (auto DV = IsCappedFromAbove.getAs<DefinedSVal>()) {
2119 NewState = NewState->assume(*DV, true);
2124 llvm::APSInt Min = -Max;
2125 SVal IsCappedFromBelow =
2126 SVB.evalBinOpNN(State, BO_GE, nonloc::SymbolVal(Sym),
2127 nonloc::ConcreteInt(Min), SVB.getConditionType());
2128 if (auto DV = IsCappedFromBelow.getAs<DefinedSVal>()) {
2129 NewState = NewState->assume(*DV, true);
2137 template <typename Condition, typename Process>
2138 ProgramStateRef processIteratorPositions(ProgramStateRef State, Condition Cond,
2140 auto &RegionMapFactory = State->get_context<IteratorRegionMap>();
2141 auto RegionMap = State->get<IteratorRegionMap>();
2142 bool Changed = false;
2143 for (const auto Reg : RegionMap) {
2144 if (Cond(Reg.second)) {
2145 RegionMap = RegionMapFactory.add(RegionMap, Reg.first, Proc(Reg.second));
2151 State = State->set<IteratorRegionMap>(RegionMap);
2153 auto &SymbolMapFactory = State->get_context<IteratorSymbolMap>();
2154 auto SymbolMap = State->get<IteratorSymbolMap>();
2156 for (const auto Sym : SymbolMap) {
2157 if (Cond(Sym.second)) {
2158 SymbolMap = SymbolMapFactory.add(SymbolMap, Sym.first, Proc(Sym.second));
2164 State = State->set<IteratorSymbolMap>(SymbolMap);
2169 ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State,
2170 const MemRegion *Cont) {
2171 auto MatchCont = [&](const IteratorPosition &Pos) {
2172 return Pos.getContainer() == Cont;
2174 auto Invalidate = [&](const IteratorPosition &Pos) {
2175 return Pos.invalidate();
2177 return processIteratorPositions(State, MatchCont, Invalidate);
2181 invalidateAllIteratorPositionsExcept(ProgramStateRef State,
2182 const MemRegion *Cont, SymbolRef Offset,
2183 BinaryOperator::Opcode Opc) {
2184 auto MatchContAndCompare = [&](const IteratorPosition &Pos) {
2185 return Pos.getContainer() == Cont &&
2186 !compare(State, Pos.getOffset(), Offset, Opc);
2188 auto Invalidate = [&](const IteratorPosition &Pos) {
2189 return Pos.invalidate();
2191 return processIteratorPositions(State, MatchContAndCompare, Invalidate);
2194 ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
2196 BinaryOperator::Opcode Opc) {
2197 auto Compare = [&](const IteratorPosition &Pos) {
2198 return compare(State, Pos.getOffset(), Offset, Opc);
2200 auto Invalidate = [&](const IteratorPosition &Pos) {
2201 return Pos.invalidate();
2203 return processIteratorPositions(State, Compare, Invalidate);
2206 ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
2208 BinaryOperator::Opcode Opc1,
2210 BinaryOperator::Opcode Opc2) {
2211 auto Compare = [&](const IteratorPosition &Pos) {
2212 return compare(State, Pos.getOffset(), Offset1, Opc1) &&
2213 compare(State, Pos.getOffset(), Offset2, Opc2);
2215 auto Invalidate = [&](const IteratorPosition &Pos) {
2216 return Pos.invalidate();
2218 return processIteratorPositions(State, Compare, Invalidate);
2221 ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State,
2222 const MemRegion *Cont,
2223 const MemRegion *NewCont) {
2224 auto MatchCont = [&](const IteratorPosition &Pos) {
2225 return Pos.getContainer() == Cont;
2227 auto ReAssign = [&](const IteratorPosition &Pos) {
2228 return Pos.reAssign(NewCont);
2230 return processIteratorPositions(State, MatchCont, ReAssign);
2233 ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State,
2234 const MemRegion *Cont,
2235 const MemRegion *NewCont,
2237 BinaryOperator::Opcode Opc) {
2238 auto MatchContAndCompare = [&](const IteratorPosition &Pos) {
2239 return Pos.getContainer() == Cont &&
2240 !compare(State, Pos.getOffset(), Offset, Opc);
2242 auto ReAssign = [&](const IteratorPosition &Pos) {
2243 return Pos.reAssign(NewCont);
2245 return processIteratorPositions(State, MatchContAndCompare, ReAssign);
2248 // This function rebases symbolic expression `OldSym + Int` to `NewSym + Int`,
2249 // `OldSym - Int` to `NewSym - Int` and `OldSym` to `NewSym` in any iterator
2250 // position offsets where `CondSym` is true.
2251 ProgramStateRef rebaseSymbolInIteratorPositionsIf(
2252 ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym,
2253 SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc) {
2254 auto LessThanEnd = [&](const IteratorPosition &Pos) {
2255 return compare(State, Pos.getOffset(), CondSym, Opc);
2257 auto RebaseSymbol = [&](const IteratorPosition &Pos) {
2258 return Pos.setTo(rebaseSymbol(State, SVB, Pos.getOffset(), OldSym,
2261 return processIteratorPositions(State, LessThanEnd, RebaseSymbol);
2264 // This function rebases symbolic expression `OldExpr + Int` to `NewExpr + Int`,
2265 // `OldExpr - Int` to `NewExpr - Int` and `OldExpr` to `NewExpr` in expression
2267 SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB,
2268 SymbolRef OrigExpr, SymbolRef OldExpr,
2270 auto &SymMgr = SVB.getSymbolManager();
2271 auto Diff = SVB.evalBinOpNN(State, BO_Sub, nonloc::SymbolVal(OrigExpr),
2272 nonloc::SymbolVal(OldExpr),
2273 SymMgr.getType(OrigExpr));
2275 const auto DiffInt = Diff.getAs<nonloc::ConcreteInt>();
2279 return SVB.evalBinOpNN(State, BO_Add, *DiffInt, nonloc::SymbolVal(NewSym),
2280 SymMgr.getType(OrigExpr)).getAsSymbol();
2283 bool isZero(ProgramStateRef State, const NonLoc &Val) {
2284 auto &BVF = State->getBasicVals();
2285 return compare(State, Val,
2286 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))),
2290 bool isPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos) {
2291 const auto *Cont = Pos.getContainer();
2292 const auto *CData = getContainerData(State, Cont);
2296 const auto End = CData->getEnd();
2298 if (isEqual(State, Pos.getOffset(), End)) {
2306 bool isAheadOfRange(ProgramStateRef State, const IteratorPosition &Pos) {
2307 const auto *Cont = Pos.getContainer();
2308 const auto *CData = getContainerData(State, Cont);
2312 const auto Beg = CData->getBegin();
2314 if (isLess(State, Pos.getOffset(), Beg)) {
2322 bool isBehindPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos) {
2323 const auto *Cont = Pos.getContainer();
2324 const auto *CData = getContainerData(State, Cont);
2328 const auto End = CData->getEnd();
2330 if (isGreater(State, Pos.getOffset(), End)) {
2338 bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
2339 return compare(State, Sym1, Sym2, BO_LT);
2342 bool isGreater(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
2343 return compare(State, Sym1, Sym2, BO_GT);
2346 bool isEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
2347 return compare(State, Sym1, Sym2, BO_EQ);
2350 bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
2351 BinaryOperator::Opcode Opc) {
2352 return compare(State, nonloc::SymbolVal(Sym1), nonloc::SymbolVal(Sym2), Opc);
2355 bool compare(ProgramStateRef State, NonLoc NL1, NonLoc NL2,
2356 BinaryOperator::Opcode Opc) {
2357 auto &SVB = State->getStateManager().getSValBuilder();
2359 const auto comparison =
2360 SVB.evalBinOp(State, Opc, NL1, NL2, SVB.getConditionType());
2362 assert(comparison.getAs<DefinedSVal>() &&
2363 "Symbol comparison must be a `DefinedSVal`");
2365 return !State->assume(comparison.castAs<DefinedSVal>(), false);
2370 void ento::registerIteratorModeling(CheckerManager &mgr) {
2371 mgr.registerChecker<IteratorChecker>();
2374 bool ento::shouldRegisterIteratorModeling(const LangOptions &LO) {
2378 #define REGISTER_CHECKER(name) \
2379 void ento::register##name(CheckerManager &Mgr) { \
2380 auto *checker = Mgr.getChecker<IteratorChecker>(); \
2381 checker->ChecksEnabled[IteratorChecker::CK_##name] = true; \
2382 checker->CheckNames[IteratorChecker::CK_##name] = \
2383 Mgr.getCurrentCheckerName(); \
2386 bool ento::shouldRegister##name(const LangOptions &LO) { return true; }
2388 REGISTER_CHECKER(IteratorRangeChecker)
2389 REGISTER_CHECKER(MismatchedIteratorChecker)
2390 REGISTER_CHECKER(InvalidatedIteratorChecker)