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/DynamicTypeMap.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 CheckName 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 /*SuppressOnSink=*/true));
361 MismatchedBugType.reset(
362 new BugType(this, "Iterator(s) mismatched", "Misuse of STL APIs",
363 /*SuppressOnSink=*/true));
364 InvalidatedBugType.reset(
365 new BugType(this, "Iterator invalidated", "Misuse of STL APIs",
366 /*SuppressOnSink=*/true));
369 void IteratorChecker::checkPreCall(const CallEvent &Call,
370 CheckerContext &C) const {
371 // Check for out of range access or access of invalidated position and
372 // iterator mismatches
373 const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
377 if (Func->isOverloadedOperator()) {
378 if (ChecksEnabled[CK_InvalidatedIteratorChecker] &&
379 isAccessOperator(Func->getOverloadedOperator())) {
380 // Check for any kind of access of invalidated iterator positions
381 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
382 verifyAccess(C, InstCall->getCXXThisVal());
384 verifyAccess(C, Call.getArgSVal(0));
387 if (ChecksEnabled[CK_IteratorRangeChecker]) {
388 if (isIncrementOperator(Func->getOverloadedOperator())) {
389 // Check for out-of-range incrementions
390 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
391 verifyIncrement(C, InstCall->getCXXThisVal());
393 if (Call.getNumArgs() >= 1) {
394 verifyIncrement(C, Call.getArgSVal(0));
397 } else if (isDecrementOperator(Func->getOverloadedOperator())) {
398 // Check for out-of-range decrementions
399 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
400 verifyDecrement(C, InstCall->getCXXThisVal());
402 if (Call.getNumArgs() >= 1) {
403 verifyDecrement(C, Call.getArgSVal(0));
406 } else if (isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) {
407 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
408 // Check for out-of-range incrementions and decrementions
409 if (Call.getNumArgs() >= 1 &&
410 Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) {
411 verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(),
412 InstCall->getCXXThisVal(),
416 if (Call.getNumArgs() >= 2 &&
417 Call.getArgExpr(1)->getType()->isIntegralOrEnumerationType()) {
418 verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(),
419 Call.getArgSVal(0), Call.getArgSVal(1));
422 } else if (isDereferenceOperator(Func->getOverloadedOperator())) {
423 // Check for dereference of out-of-range iterators
424 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
425 verifyDereference(C, InstCall->getCXXThisVal());
427 verifyDereference(C, Call.getArgSVal(0));
430 } else if (ChecksEnabled[CK_MismatchedIteratorChecker] &&
431 isComparisonOperator(Func->getOverloadedOperator())) {
432 // Check for comparisons of iterators of different containers
433 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
434 if (Call.getNumArgs() < 1)
437 if (!isIteratorType(InstCall->getCXXThisExpr()->getType()) ||
438 !isIteratorType(Call.getArgExpr(0)->getType()))
441 verifyMatch(C, InstCall->getCXXThisVal(), Call.getArgSVal(0));
443 if (Call.getNumArgs() < 2)
446 if (!isIteratorType(Call.getArgExpr(0)->getType()) ||
447 !isIteratorType(Call.getArgExpr(1)->getType()))
450 verifyMatch(C, Call.getArgSVal(0), Call.getArgSVal(1));
453 } else if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
454 if (!ChecksEnabled[CK_MismatchedIteratorChecker])
457 const auto *ContReg = InstCall->getCXXThisVal().getAsRegion();
460 // Check for erase, insert and emplace using iterator of another container
461 if (isEraseCall(Func) || isEraseAfterCall(Func)) {
462 verifyMatch(C, Call.getArgSVal(0),
463 InstCall->getCXXThisVal().getAsRegion());
464 if (Call.getNumArgs() == 2) {
465 verifyMatch(C, Call.getArgSVal(1),
466 InstCall->getCXXThisVal().getAsRegion());
468 } else if (isInsertCall(Func)) {
469 verifyMatch(C, Call.getArgSVal(0),
470 InstCall->getCXXThisVal().getAsRegion());
471 if (Call.getNumArgs() == 3 &&
472 isIteratorType(Call.getArgExpr(1)->getType()) &&
473 isIteratorType(Call.getArgExpr(2)->getType())) {
474 verifyMatch(C, Call.getArgSVal(1), Call.getArgSVal(2));
476 } else if (isEmplaceCall(Func)) {
477 verifyMatch(C, Call.getArgSVal(0),
478 InstCall->getCXXThisVal().getAsRegion());
480 } else if (isa<CXXConstructorCall>(&Call)) {
481 // Check match of first-last iterator pair in a constructor of a container
482 if (Call.getNumArgs() < 2)
485 const auto *Ctr = cast<CXXConstructorDecl>(Call.getDecl());
486 if (Ctr->getNumParams() < 2)
489 if (Ctr->getParamDecl(0)->getName() != "first" ||
490 Ctr->getParamDecl(1)->getName() != "last")
493 if (!isIteratorType(Call.getArgExpr(0)->getType()) ||
494 !isIteratorType(Call.getArgExpr(1)->getType()))
497 verifyMatch(C, Call.getArgSVal(0), Call.getArgSVal(1));
499 // The main purpose of iterators is to abstract away from different
500 // containers and provide a (maybe limited) uniform access to them.
501 // This implies that any correctly written template function that
502 // works on multiple containers using iterators takes different
503 // template parameters for different containers. So we can safely
504 // assume that passing iterators of different containers as arguments
505 // whose type replaces the same template parameter is a bug.
508 // template<typename I1, typename I2>
509 // void f(I1 first1, I1 last1, I2 first2, I2 last2);
511 // In this case the first two arguments to f() must be iterators must belong
512 // to the same container and the last to also to the same container but
513 // not necessarily to the same as the first two.
515 if (!ChecksEnabled[CK_MismatchedIteratorChecker])
518 const auto *Templ = Func->getPrimaryTemplate();
522 const auto *TParams = Templ->getTemplateParameters();
523 const auto *TArgs = Func->getTemplateSpecializationArgs();
525 // Iterate over all the template parameters
526 for (size_t I = 0; I < TParams->size(); ++I) {
527 const auto *TPDecl = dyn_cast<TemplateTypeParmDecl>(TParams->getParam(I));
531 if (TPDecl->isParameterPack())
534 const auto TAType = TArgs->get(I).getAsType();
535 if (!isIteratorType(TAType))
538 SVal LHS = UndefinedVal();
540 // For every template parameter which is an iterator type in the
541 // instantiation look for all functions' parameters' type by it and
542 // check whether they belong to the same container
543 for (auto J = 0U; J < Func->getNumParams(); ++J) {
544 const auto *Param = Func->getParamDecl(J);
545 const auto *ParamType =
546 Param->getType()->getAs<SubstTemplateTypeParmType>();
548 ParamType->getReplacedParameter()->getDecl() != TPDecl)
551 LHS = Call.getArgSVal(J);
553 verifyMatch(C, LHS, Call.getArgSVal(J));
560 void IteratorChecker::checkPostCall(const CallEvent &Call,
561 CheckerContext &C) const {
562 // Record new iterator positions and iterator position changes
563 const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
567 if (Func->isOverloadedOperator()) {
568 const auto Op = Func->getOverloadedOperator();
569 if (isAssignmentOperator(Op)) {
570 const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call);
571 if (cast<CXXMethodDecl>(Func)->isMoveAssignmentOperator()) {
572 handleAssign(C, InstCall->getCXXThisVal(), Call.getOriginExpr(),
577 handleAssign(C, InstCall->getCXXThisVal());
579 } else if (isSimpleComparisonOperator(Op)) {
580 const auto *OrigExpr = Call.getOriginExpr();
584 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
585 handleComparison(C, OrigExpr, Call.getReturnValue(),
586 InstCall->getCXXThisVal(), Call.getArgSVal(0), Op);
590 handleComparison(C, OrigExpr, Call.getReturnValue(), Call.getArgSVal(0),
591 Call.getArgSVal(1), Op);
593 } else if (isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) {
594 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
595 if (Call.getNumArgs() >= 1 &&
596 Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) {
597 handleRandomIncrOrDecr(C, Func->getOverloadedOperator(),
598 Call.getReturnValue(),
599 InstCall->getCXXThisVal(), Call.getArgSVal(0));
603 if (Call.getNumArgs() >= 2 &&
604 Call.getArgExpr(1)->getType()->isIntegralOrEnumerationType()) {
605 handleRandomIncrOrDecr(C, Func->getOverloadedOperator(),
606 Call.getReturnValue(), Call.getArgSVal(0),
611 } else if (isIncrementOperator(Func->getOverloadedOperator())) {
612 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
613 handleIncrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
618 handleIncrement(C, Call.getReturnValue(), Call.getArgSVal(0),
621 } else if (isDecrementOperator(Func->getOverloadedOperator())) {
622 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
623 handleDecrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
628 handleDecrement(C, Call.getReturnValue(), Call.getArgSVal(0),
633 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
634 if (isAssignCall(Func)) {
635 handleAssign(C, InstCall->getCXXThisVal());
639 if (isClearCall(Func)) {
640 handleClear(C, InstCall->getCXXThisVal());
644 if (isPushBackCall(Func) || isEmplaceBackCall(Func)) {
645 handlePushBack(C, InstCall->getCXXThisVal());
649 if (isPopBackCall(Func)) {
650 handlePopBack(C, InstCall->getCXXThisVal());
654 if (isPushFrontCall(Func) || isEmplaceFrontCall(Func)) {
655 handlePushFront(C, InstCall->getCXXThisVal());
659 if (isPopFrontCall(Func)) {
660 handlePopFront(C, InstCall->getCXXThisVal());
664 if (isInsertCall(Func) || isEmplaceCall(Func)) {
665 handleInsert(C, Call.getArgSVal(0));
669 if (isEraseCall(Func)) {
670 if (Call.getNumArgs() == 1) {
671 handleErase(C, Call.getArgSVal(0));
675 if (Call.getNumArgs() == 2) {
676 handleErase(C, Call.getArgSVal(0), Call.getArgSVal(1));
681 if (isEraseAfterCall(Func)) {
682 if (Call.getNumArgs() == 1) {
683 handleEraseAfter(C, Call.getArgSVal(0));
687 if (Call.getNumArgs() == 2) {
688 handleEraseAfter(C, Call.getArgSVal(0), Call.getArgSVal(1));
694 const auto *OrigExpr = Call.getOriginExpr();
698 if (!isIteratorType(Call.getResultType()))
701 auto State = C.getState();
703 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
704 if (isBeginCall(Func)) {
705 handleBegin(C, OrigExpr, Call.getReturnValue(),
706 InstCall->getCXXThisVal());
710 if (isEndCall(Func)) {
711 handleEnd(C, OrigExpr, Call.getReturnValue(),
712 InstCall->getCXXThisVal());
717 // Already bound to container?
718 if (getIteratorPosition(State, Call.getReturnValue()))
721 // Copy-like and move constructors
722 if (isa<CXXConstructorCall>(&Call) && Call.getNumArgs() == 1) {
723 if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(0))) {
724 State = setIteratorPosition(State, Call.getReturnValue(), *Pos);
725 if (cast<CXXConstructorDecl>(Func)->isMoveConstructor()) {
726 State = removeIteratorPosition(State, Call.getArgSVal(0));
728 C.addTransition(State);
733 // Assumption: if return value is an iterator which is not yet bound to a
734 // container, then look for the first iterator argument, and
735 // bind the return value to the same container. This approach
736 // works for STL algorithms.
737 // FIXME: Add a more conservative mode
738 for (unsigned i = 0; i < Call.getNumArgs(); ++i) {
739 if (isIteratorType(Call.getArgExpr(i)->getType())) {
740 if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(i))) {
741 assignToContainer(C, OrigExpr, Call.getReturnValue(),
742 Pos->getContainer());
750 void IteratorChecker::checkBind(SVal Loc, SVal Val, const Stmt *S,
751 CheckerContext &C) const {
752 auto State = C.getState();
753 const auto *Pos = getIteratorPosition(State, Val);
755 State = setIteratorPosition(State, Loc, *Pos);
756 C.addTransition(State);
758 const auto *OldPos = getIteratorPosition(State, Loc);
760 State = removeIteratorPosition(State, Loc);
761 C.addTransition(State);
766 void IteratorChecker::checkPostStmt(const MaterializeTemporaryExpr *MTE,
767 CheckerContext &C) const {
768 /* Transfer iterator state to temporary objects */
769 auto State = C.getState();
771 getIteratorPosition(State, C.getSVal(MTE->GetTemporaryExpr()));
774 State = setIteratorPosition(State, C.getSVal(MTE), *Pos);
775 C.addTransition(State);
778 void IteratorChecker::checkLiveSymbols(ProgramStateRef State,
779 SymbolReaper &SR) const {
780 // Keep symbolic expressions of iterator positions, container begins and ends
782 auto RegionMap = State->get<IteratorRegionMap>();
783 for (const auto Reg : RegionMap) {
784 const auto Offset = Reg.second.getOffset();
785 for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
786 if (isa<SymbolData>(*i))
790 auto SymbolMap = State->get<IteratorSymbolMap>();
791 for (const auto Sym : SymbolMap) {
792 const auto Offset = Sym.second.getOffset();
793 for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
794 if (isa<SymbolData>(*i))
798 auto ContMap = State->get<ContainerMap>();
799 for (const auto Cont : ContMap) {
800 const auto CData = Cont.second;
801 if (CData.getBegin()) {
802 SR.markLive(CData.getBegin());
803 if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getBegin()))
804 SR.markLive(SIE->getLHS());
806 if (CData.getEnd()) {
807 SR.markLive(CData.getEnd());
808 if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getEnd()))
809 SR.markLive(SIE->getLHS());
814 void IteratorChecker::checkDeadSymbols(SymbolReaper &SR,
815 CheckerContext &C) const {
817 auto State = C.getState();
819 auto RegionMap = State->get<IteratorRegionMap>();
820 for (const auto Reg : RegionMap) {
821 if (!SR.isLiveRegion(Reg.first)) {
822 // The region behind the `LazyCompoundVal` is often cleaned up before
823 // the `LazyCompoundVal` itself. If there are iterator positions keyed
824 // by these regions their cleanup must be deferred.
825 if (!isBoundThroughLazyCompoundVal(State->getEnvironment(), Reg.first)) {
826 State = State->remove<IteratorRegionMap>(Reg.first);
831 auto SymbolMap = State->get<IteratorSymbolMap>();
832 for (const auto Sym : SymbolMap) {
833 if (!SR.isLive(Sym.first)) {
834 State = State->remove<IteratorSymbolMap>(Sym.first);
838 auto ContMap = State->get<ContainerMap>();
839 for (const auto Cont : ContMap) {
840 if (!SR.isLiveRegion(Cont.first)) {
841 // We must keep the container data while it has live iterators to be able
842 // to compare them to the begin and the end of the container.
843 if (!hasLiveIterators(State, Cont.first)) {
844 State = State->remove<ContainerMap>(Cont.first);
849 C.addTransition(State);
852 void IteratorChecker::handleComparison(CheckerContext &C, const Expr *CE,
853 const SVal &RetVal, const SVal &LVal,
855 OverloadedOperatorKind Op) const {
856 // Record the operands and the operator of the comparison for the next
857 // evalAssume, if the result is a symbolic expression. If it is a concrete
858 // value (only one branch is possible), then transfer the state between
859 // the operands according to the operator and the result
860 auto State = C.getState();
861 const auto *LPos = getIteratorPosition(State, LVal);
862 const auto *RPos = getIteratorPosition(State, RVal);
863 const MemRegion *Cont = nullptr;
865 Cont = LPos->getContainer();
867 Cont = RPos->getContainer();
872 // At least one of the iterators have recorded positions. If one of them has
873 // not then create a new symbol for the offset.
875 if (!LPos || !RPos) {
876 auto &SymMgr = C.getSymbolManager();
877 Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
878 C.getASTContext().LongTy, C.blockCount());
879 State = assumeNoOverflow(State, Sym, 4);
883 State = setIteratorPosition(State, LVal,
884 IteratorPosition::getPosition(Cont, Sym));
885 LPos = getIteratorPosition(State, LVal);
887 State = setIteratorPosition(State, RVal,
888 IteratorPosition::getPosition(Cont, Sym));
889 RPos = getIteratorPosition(State, RVal);
892 processComparison(C, State, LPos->getOffset(), RPos->getOffset(), RetVal, Op);
895 void IteratorChecker::processComparison(CheckerContext &C,
896 ProgramStateRef State, SymbolRef Sym1,
897 SymbolRef Sym2, const SVal &RetVal,
898 OverloadedOperatorKind Op) const {
899 if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) {
900 if ((State = relateSymbols(State, Sym1, Sym2,
901 (Op == OO_EqualEqual) ==
902 (TruthVal->getValue() != 0)))) {
903 C.addTransition(State);
905 C.generateSink(State, C.getPredecessor());
910 const auto ConditionVal = RetVal.getAs<DefinedSVal>();
914 if (auto StateTrue = relateSymbols(State, Sym1, Sym2, Op == OO_EqualEqual)) {
915 StateTrue = StateTrue->assume(*ConditionVal, true);
916 C.addTransition(StateTrue);
919 if (auto StateFalse = relateSymbols(State, Sym1, Sym2, Op != OO_EqualEqual)) {
920 StateFalse = StateFalse->assume(*ConditionVal, false);
921 C.addTransition(StateFalse);
925 void IteratorChecker::verifyDereference(CheckerContext &C,
926 const SVal &Val) const {
927 auto State = C.getState();
928 const auto *Pos = getIteratorPosition(State, Val);
929 if (Pos && isPastTheEnd(State, *Pos)) {
930 auto *N = C.generateNonFatalErrorNode(State);
933 reportOutOfRangeBug("Past-the-end iterator dereferenced.", Val, C, N);
938 void IteratorChecker::verifyAccess(CheckerContext &C, const SVal &Val) const {
939 auto State = C.getState();
940 const auto *Pos = getIteratorPosition(State, Val);
941 if (Pos && !Pos->isValid()) {
942 auto *N = C.generateNonFatalErrorNode(State);
946 reportInvalidatedBug("Invalidated iterator accessed.", Val, C, N);
950 void IteratorChecker::handleIncrement(CheckerContext &C, const SVal &RetVal,
951 const SVal &Iter, bool Postfix) const {
952 // Increment the symbolic expressions which represents the position of the
954 auto State = C.getState();
955 const auto *Pos = getIteratorPosition(State, Iter);
957 auto &SymMgr = C.getSymbolManager();
958 auto &BVF = SymMgr.getBasicVals();
960 advancePosition(C, OO_Plus, *Pos,
961 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
962 State = setIteratorPosition(State, Iter, NewPos);
963 State = setIteratorPosition(State, RetVal, Postfix ? *Pos : NewPos);
964 C.addTransition(State);
968 void IteratorChecker::handleDecrement(CheckerContext &C, const SVal &RetVal,
969 const SVal &Iter, bool Postfix) const {
970 // Decrement the symbolic expressions which represents the position of the
972 auto State = C.getState();
973 const auto *Pos = getIteratorPosition(State, Iter);
975 auto &SymMgr = C.getSymbolManager();
976 auto &BVF = SymMgr.getBasicVals();
978 advancePosition(C, OO_Minus, *Pos,
979 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
980 State = setIteratorPosition(State, Iter, NewPos);
981 State = setIteratorPosition(State, RetVal, Postfix ? *Pos : NewPos);
982 C.addTransition(State);
986 void IteratorChecker::handleRandomIncrOrDecr(CheckerContext &C,
987 OverloadedOperatorKind Op,
990 const SVal &RHS) const {
991 // Increment or decrement the symbolic expressions which represents the
992 // position of the iterator
993 auto State = C.getState();
994 const auto *Pos = getIteratorPosition(State, LHS);
998 const auto *value = &RHS;
999 if (auto loc = RHS.getAs<Loc>()) {
1000 const auto val = State->getRawSVal(*loc);
1004 auto &TgtVal = (Op == OO_PlusEqual || Op == OO_MinusEqual) ? LHS : RetVal;
1006 setIteratorPosition(State, TgtVal, advancePosition(C, Op, *Pos, *value));
1007 C.addTransition(State);
1010 void IteratorChecker::verifyIncrement(CheckerContext &C,
1011 const SVal &Iter) const {
1012 auto &BVF = C.getSValBuilder().getBasicValueFactory();
1013 verifyRandomIncrOrDecr(C, OO_Plus, Iter,
1014 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
1017 void IteratorChecker::verifyDecrement(CheckerContext &C,
1018 const SVal &Iter) const {
1019 auto &BVF = C.getSValBuilder().getBasicValueFactory();
1020 verifyRandomIncrOrDecr(C, OO_Minus, Iter,
1021 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
1024 void IteratorChecker::verifyRandomIncrOrDecr(CheckerContext &C,
1025 OverloadedOperatorKind Op,
1027 const SVal &RHS) const {
1028 auto State = C.getState();
1030 // If the iterator is initially inside its range, then the operation is valid
1031 const auto *Pos = getIteratorPosition(State, LHS);
1036 if (auto ValAsLoc = RHS.getAs<Loc>()) {
1037 Value = State->getRawSVal(*ValAsLoc);
1040 if (Value.isUnknown())
1043 // Incremention or decremention by 0 is never a bug.
1044 if (isZero(State, Value.castAs<NonLoc>()))
1047 // The result may be the past-end iterator of the container, but any other
1048 // out of range position is undefined behaviour
1049 if (isAheadOfRange(State, advancePosition(C, Op, *Pos, Value))) {
1050 auto *N = C.generateNonFatalErrorNode(State);
1053 reportOutOfRangeBug("Iterator decremented ahead of its valid range.", LHS,
1056 if (isBehindPastTheEnd(State, advancePosition(C, Op, *Pos, Value))) {
1057 auto *N = C.generateNonFatalErrorNode(State);
1060 reportOutOfRangeBug("Iterator incremented behind the past-the-end "
1061 "iterator.", LHS, C, N);
1065 void IteratorChecker::verifyMatch(CheckerContext &C, const SVal &Iter,
1066 const MemRegion *Cont) const {
1067 // Verify match between a container and the container of an iterator
1068 Cont = Cont->getMostDerivedObjectRegion();
1070 if (const auto *ContSym = Cont->getSymbolicBase()) {
1071 if (isa<SymbolConjured>(ContSym->getSymbol()))
1075 auto State = C.getState();
1076 const auto *Pos = getIteratorPosition(State, Iter);
1080 const auto *IterCont = Pos->getContainer();
1082 // Skip symbolic regions based on conjured symbols. Two conjured symbols
1083 // may or may not be the same. For example, the same function can return
1084 // the same or a different container but we get different conjured symbols
1085 // for each call. This may cause false positives so omit them from the check.
1086 if (const auto *ContSym = IterCont->getSymbolicBase()) {
1087 if (isa<SymbolConjured>(ContSym->getSymbol()))
1091 if (IterCont != Cont) {
1092 auto *N = C.generateNonFatalErrorNode(State);
1096 reportMismatchedBug("Container accessed using foreign iterator argument.",
1101 void IteratorChecker::verifyMatch(CheckerContext &C, const SVal &Iter1,
1102 const SVal &Iter2) const {
1103 // Verify match between the containers of two iterators
1104 auto State = C.getState();
1105 const auto *Pos1 = getIteratorPosition(State, Iter1);
1109 const auto *IterCont1 = Pos1->getContainer();
1111 // Skip symbolic regions based on conjured symbols. Two conjured symbols
1112 // may or may not be the same. For example, the same function can return
1113 // the same or a different container but we get different conjured symbols
1114 // for each call. This may cause false positives so omit them from the check.
1115 if (const auto *ContSym = IterCont1->getSymbolicBase()) {
1116 if (isa<SymbolConjured>(ContSym->getSymbol()))
1120 const auto *Pos2 = getIteratorPosition(State, Iter2);
1124 const auto *IterCont2 = Pos2->getContainer();
1125 if (const auto *ContSym = IterCont2->getSymbolicBase()) {
1126 if (isa<SymbolConjured>(ContSym->getSymbol()))
1130 if (IterCont1 != IterCont2) {
1131 auto *N = C.generateNonFatalErrorNode(State);
1134 reportMismatchedBug("Iterators of different containers used where the "
1135 "same container is expected.", Iter1, Iter2, C, N);
1139 void IteratorChecker::handleBegin(CheckerContext &C, const Expr *CE,
1140 const SVal &RetVal, const SVal &Cont) const {
1141 const auto *ContReg = Cont.getAsRegion();
1145 ContReg = ContReg->getMostDerivedObjectRegion();
1147 // If the container already has a begin symbol then use it. Otherwise first
1148 // create a new one.
1149 auto State = C.getState();
1150 auto BeginSym = getContainerBegin(State, ContReg);
1152 State = createContainerBegin(State, ContReg, CE, C.getASTContext().LongTy,
1153 C.getLocationContext(), C.blockCount());
1154 BeginSym = getContainerBegin(State, ContReg);
1156 State = setIteratorPosition(State, RetVal,
1157 IteratorPosition::getPosition(ContReg, BeginSym));
1158 C.addTransition(State);
1161 void IteratorChecker::handleEnd(CheckerContext &C, const Expr *CE,
1162 const SVal &RetVal, const SVal &Cont) const {
1163 const auto *ContReg = Cont.getAsRegion();
1167 ContReg = ContReg->getMostDerivedObjectRegion();
1169 // If the container already has an end symbol then use it. Otherwise first
1170 // create a new one.
1171 auto State = C.getState();
1172 auto EndSym = getContainerEnd(State, ContReg);
1174 State = createContainerEnd(State, ContReg, CE, C.getASTContext().LongTy,
1175 C.getLocationContext(), C.blockCount());
1176 EndSym = getContainerEnd(State, ContReg);
1178 State = setIteratorPosition(State, RetVal,
1179 IteratorPosition::getPosition(ContReg, EndSym));
1180 C.addTransition(State);
1183 void IteratorChecker::assignToContainer(CheckerContext &C, const Expr *CE,
1185 const MemRegion *Cont) const {
1186 Cont = Cont->getMostDerivedObjectRegion();
1188 auto State = C.getState();
1189 auto &SymMgr = C.getSymbolManager();
1190 auto Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
1191 C.getASTContext().LongTy, C.blockCount());
1192 State = assumeNoOverflow(State, Sym, 4);
1193 State = setIteratorPosition(State, RetVal,
1194 IteratorPosition::getPosition(Cont, Sym));
1195 C.addTransition(State);
1198 void IteratorChecker::handleAssign(CheckerContext &C, const SVal &Cont,
1199 const Expr *CE, const SVal &OldCont) const {
1200 const auto *ContReg = Cont.getAsRegion();
1204 ContReg = ContReg->getMostDerivedObjectRegion();
1206 // Assignment of a new value to a container always invalidates all its
1208 auto State = C.getState();
1209 const auto CData = getContainerData(State, ContReg);
1211 State = invalidateAllIteratorPositions(State, ContReg);
1214 // In case of move, iterators of the old container (except the past-end
1215 // iterators) remain valid but refer to the new container
1216 if (!OldCont.isUndef()) {
1217 const auto *OldContReg = OldCont.getAsRegion();
1219 OldContReg = OldContReg->getMostDerivedObjectRegion();
1220 const auto OldCData = getContainerData(State, OldContReg);
1222 if (const auto OldEndSym = OldCData->getEnd()) {
1223 // If we already assigned an "end" symbol to the old container, then
1224 // first reassign all iterator positions to the new container which
1225 // are not past the container (thus not greater or equal to the
1226 // current "end" symbol).
1227 State = reassignAllIteratorPositionsUnless(State, OldContReg, ContReg,
1229 auto &SymMgr = C.getSymbolManager();
1230 auto &SVB = C.getSValBuilder();
1231 // Then generate and assign a new "end" symbol for the new container.
1233 SymMgr.conjureSymbol(CE, C.getLocationContext(),
1234 C.getASTContext().LongTy, C.blockCount());
1235 State = assumeNoOverflow(State, NewEndSym, 4);
1237 State = setContainerData(State, ContReg, CData->newEnd(NewEndSym));
1239 State = setContainerData(State, ContReg,
1240 ContainerData::fromEnd(NewEndSym));
1242 // Finally, replace the old "end" symbol in the already reassigned
1243 // iterator positions with the new "end" symbol.
1244 State = rebaseSymbolInIteratorPositionsIf(
1245 State, SVB, OldEndSym, NewEndSym, OldEndSym, BO_LT);
1247 // There was no "end" symbol assigned yet to the old container,
1248 // so reassign all iterator positions to the new container.
1249 State = reassignAllIteratorPositions(State, OldContReg, ContReg);
1251 if (const auto OldBeginSym = OldCData->getBegin()) {
1252 // If we already assigned a "begin" symbol to the old container, then
1253 // assign it to the new container and remove it from the old one.
1256 setContainerData(State, ContReg, CData->newBegin(OldBeginSym));
1258 State = setContainerData(State, ContReg,
1259 ContainerData::fromBegin(OldBeginSym));
1262 setContainerData(State, OldContReg, OldCData->newEnd(nullptr));
1265 // There was neither "begin" nor "end" symbol assigned yet to the old
1266 // container, so reassign all iterator positions to the new container.
1267 State = reassignAllIteratorPositions(State, OldContReg, ContReg);
1271 C.addTransition(State);
1274 void IteratorChecker::handleClear(CheckerContext &C, const SVal &Cont) const {
1275 const auto *ContReg = Cont.getAsRegion();
1279 ContReg = ContReg->getMostDerivedObjectRegion();
1281 // The clear() operation invalidates all the iterators, except the past-end
1282 // iterators of list-like containers
1283 auto State = C.getState();
1284 if (!hasSubscriptOperator(State, ContReg) ||
1285 !backModifiable(State, ContReg)) {
1286 const auto CData = getContainerData(State, ContReg);
1288 if (const auto EndSym = CData->getEnd()) {
1290 invalidateAllIteratorPositionsExcept(State, ContReg, EndSym, BO_GE);
1291 C.addTransition(State);
1296 State = invalidateAllIteratorPositions(State, ContReg);
1297 C.addTransition(State);
1300 void IteratorChecker::handlePushBack(CheckerContext &C,
1301 const SVal &Cont) const {
1302 const auto *ContReg = Cont.getAsRegion();
1306 ContReg = ContReg->getMostDerivedObjectRegion();
1308 // For deque-like containers invalidate all iterator positions
1309 auto State = C.getState();
1310 if (hasSubscriptOperator(State, ContReg) && frontModifiable(State, ContReg)) {
1311 State = invalidateAllIteratorPositions(State, ContReg);
1312 C.addTransition(State);
1316 const auto CData = getContainerData(State, ContReg);
1320 // For vector-like containers invalidate the past-end iterator positions
1321 if (const auto EndSym = CData->getEnd()) {
1322 if (hasSubscriptOperator(State, ContReg)) {
1323 State = invalidateIteratorPositions(State, EndSym, BO_GE);
1325 auto &SymMgr = C.getSymbolManager();
1326 auto &BVF = SymMgr.getBasicVals();
1327 auto &SVB = C.getSValBuilder();
1328 const auto newEndSym =
1329 SVB.evalBinOp(State, BO_Add,
1330 nonloc::SymbolVal(EndSym),
1331 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
1332 SymMgr.getType(EndSym)).getAsSymbol();
1333 State = setContainerData(State, ContReg, CData->newEnd(newEndSym));
1335 C.addTransition(State);
1338 void IteratorChecker::handlePopBack(CheckerContext &C, const SVal &Cont) const {
1339 const auto *ContReg = Cont.getAsRegion();
1343 ContReg = ContReg->getMostDerivedObjectRegion();
1345 auto State = C.getState();
1346 const auto CData = getContainerData(State, ContReg);
1350 if (const auto EndSym = CData->getEnd()) {
1351 auto &SymMgr = C.getSymbolManager();
1352 auto &BVF = SymMgr.getBasicVals();
1353 auto &SVB = C.getSValBuilder();
1354 const auto BackSym =
1355 SVB.evalBinOp(State, BO_Sub,
1356 nonloc::SymbolVal(EndSym),
1357 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
1358 SymMgr.getType(EndSym)).getAsSymbol();
1359 // For vector-like and deque-like containers invalidate the last and the
1360 // past-end iterator positions. For list-like containers only invalidate
1361 // the last position
1362 if (hasSubscriptOperator(State, ContReg) &&
1363 backModifiable(State, ContReg)) {
1364 State = invalidateIteratorPositions(State, BackSym, BO_GE);
1365 State = setContainerData(State, ContReg, CData->newEnd(nullptr));
1367 State = invalidateIteratorPositions(State, BackSym, BO_EQ);
1369 auto newEndSym = BackSym;
1370 State = setContainerData(State, ContReg, CData->newEnd(newEndSym));
1371 C.addTransition(State);
1375 void IteratorChecker::handlePushFront(CheckerContext &C,
1376 const SVal &Cont) const {
1377 const auto *ContReg = Cont.getAsRegion();
1381 ContReg = ContReg->getMostDerivedObjectRegion();
1383 // For deque-like containers invalidate all iterator positions
1384 auto State = C.getState();
1385 if (hasSubscriptOperator(State, ContReg)) {
1386 State = invalidateAllIteratorPositions(State, ContReg);
1387 C.addTransition(State);
1389 const auto CData = getContainerData(State, ContReg);
1393 if (const auto BeginSym = CData->getBegin()) {
1394 auto &SymMgr = C.getSymbolManager();
1395 auto &BVF = SymMgr.getBasicVals();
1396 auto &SVB = C.getSValBuilder();
1397 const auto newBeginSym =
1398 SVB.evalBinOp(State, BO_Sub,
1399 nonloc::SymbolVal(BeginSym),
1400 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
1401 SymMgr.getType(BeginSym)).getAsSymbol();
1402 State = setContainerData(State, ContReg, CData->newBegin(newBeginSym));
1403 C.addTransition(State);
1408 void IteratorChecker::handlePopFront(CheckerContext &C,
1409 const SVal &Cont) const {
1410 const auto *ContReg = Cont.getAsRegion();
1414 ContReg = ContReg->getMostDerivedObjectRegion();
1416 auto State = C.getState();
1417 const auto CData = getContainerData(State, ContReg);
1421 // For deque-like containers invalidate all iterator positions. For list-like
1422 // iterators only invalidate the first position
1423 if (const auto BeginSym = CData->getBegin()) {
1424 if (hasSubscriptOperator(State, ContReg)) {
1425 State = invalidateIteratorPositions(State, BeginSym, BO_LE);
1427 State = invalidateIteratorPositions(State, BeginSym, BO_EQ);
1429 auto &SymMgr = C.getSymbolManager();
1430 auto &BVF = SymMgr.getBasicVals();
1431 auto &SVB = C.getSValBuilder();
1432 const auto newBeginSym =
1433 SVB.evalBinOp(State, BO_Add,
1434 nonloc::SymbolVal(BeginSym),
1435 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
1436 SymMgr.getType(BeginSym)).getAsSymbol();
1437 State = setContainerData(State, ContReg, CData->newBegin(newBeginSym));
1438 C.addTransition(State);
1442 void IteratorChecker::handleInsert(CheckerContext &C, const SVal &Iter) const {
1443 auto State = C.getState();
1444 const auto *Pos = getIteratorPosition(State, Iter);
1448 // For deque-like containers invalidate all iterator positions. For
1449 // vector-like containers invalidate iterator positions after the insertion.
1450 const auto *Cont = Pos->getContainer();
1451 if (hasSubscriptOperator(State, Cont) && backModifiable(State, Cont)) {
1452 if (frontModifiable(State, Cont)) {
1453 State = invalidateAllIteratorPositions(State, Cont);
1455 State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE);
1457 if (const auto *CData = getContainerData(State, Cont)) {
1458 if (const auto EndSym = CData->getEnd()) {
1459 State = invalidateIteratorPositions(State, EndSym, BO_GE);
1460 State = setContainerData(State, Cont, CData->newEnd(nullptr));
1463 C.addTransition(State);
1467 void IteratorChecker::handleErase(CheckerContext &C, const SVal &Iter) const {
1468 auto State = C.getState();
1469 const auto *Pos = getIteratorPosition(State, Iter);
1473 // For deque-like containers invalidate all iterator positions. For
1474 // vector-like containers invalidate iterator positions at and after the
1475 // deletion. For list-like containers only invalidate the deleted position.
1476 const auto *Cont = Pos->getContainer();
1477 if (hasSubscriptOperator(State, Cont) && backModifiable(State, Cont)) {
1478 if (frontModifiable(State, Cont)) {
1479 State = invalidateAllIteratorPositions(State, Cont);
1481 State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE);
1483 if (const auto *CData = getContainerData(State, Cont)) {
1484 if (const auto EndSym = CData->getEnd()) {
1485 State = invalidateIteratorPositions(State, EndSym, BO_GE);
1486 State = setContainerData(State, Cont, CData->newEnd(nullptr));
1490 State = invalidateIteratorPositions(State, Pos->getOffset(), BO_EQ);
1492 C.addTransition(State);
1495 void IteratorChecker::handleErase(CheckerContext &C, const SVal &Iter1,
1496 const SVal &Iter2) const {
1497 auto State = C.getState();
1498 const auto *Pos1 = getIteratorPosition(State, Iter1);
1499 const auto *Pos2 = getIteratorPosition(State, Iter2);
1503 // For deque-like containers invalidate all iterator positions. For
1504 // vector-like containers invalidate iterator positions at and after the
1505 // deletion range. For list-like containers only invalidate the deleted
1506 // position range [first..last].
1507 const auto *Cont = Pos1->getContainer();
1508 if (hasSubscriptOperator(State, Cont) && backModifiable(State, Cont)) {
1509 if (frontModifiable(State, Cont)) {
1510 State = invalidateAllIteratorPositions(State, Cont);
1512 State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE);
1514 if (const auto *CData = getContainerData(State, Cont)) {
1515 if (const auto EndSym = CData->getEnd()) {
1516 State = invalidateIteratorPositions(State, EndSym, BO_GE);
1517 State = setContainerData(State, Cont, CData->newEnd(nullptr));
1521 State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE,
1522 Pos2->getOffset(), BO_LT);
1524 C.addTransition(State);
1527 void IteratorChecker::handleEraseAfter(CheckerContext &C,
1528 const SVal &Iter) const {
1529 auto State = C.getState();
1530 const auto *Pos = getIteratorPosition(State, Iter);
1534 // Invalidate the deleted iterator position, which is the position of the
1535 // parameter plus one.
1536 auto &SymMgr = C.getSymbolManager();
1537 auto &BVF = SymMgr.getBasicVals();
1538 auto &SVB = C.getSValBuilder();
1539 const auto NextSym =
1540 SVB.evalBinOp(State, BO_Add,
1541 nonloc::SymbolVal(Pos->getOffset()),
1542 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
1543 SymMgr.getType(Pos->getOffset())).getAsSymbol();
1544 State = invalidateIteratorPositions(State, NextSym, BO_EQ);
1545 C.addTransition(State);
1548 void IteratorChecker::handleEraseAfter(CheckerContext &C, const SVal &Iter1,
1549 const SVal &Iter2) const {
1550 auto State = C.getState();
1551 const auto *Pos1 = getIteratorPosition(State, Iter1);
1552 const auto *Pos2 = getIteratorPosition(State, Iter2);
1556 // Invalidate the deleted iterator position range (first..last)
1557 State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GT,
1558 Pos2->getOffset(), BO_LT);
1559 C.addTransition(State);
1562 IteratorPosition IteratorChecker::advancePosition(CheckerContext &C,
1563 OverloadedOperatorKind Op,
1564 const IteratorPosition &Pos,
1565 const SVal &Distance) const {
1566 auto State = C.getState();
1567 auto &SymMgr = C.getSymbolManager();
1568 auto &SVB = C.getSValBuilder();
1570 assert ((Op == OO_Plus || Op == OO_PlusEqual ||
1571 Op == OO_Minus || Op == OO_MinusEqual) &&
1572 "Advance operator must be one of +, -, += and -=.");
1573 auto BinOp = (Op == OO_Plus || Op == OO_PlusEqual) ? BO_Add : BO_Sub;
1574 if (const auto IntDist = Distance.getAs<nonloc::ConcreteInt>()) {
1575 // For concrete integers we can calculate the new position
1576 return Pos.setTo(SVB.evalBinOp(State, BinOp,
1577 nonloc::SymbolVal(Pos.getOffset()), *IntDist,
1578 SymMgr.getType(Pos.getOffset()))
1581 // For other symbols create a new symbol to keep expressions simple
1582 const auto &LCtx = C.getLocationContext();
1583 const auto NewPosSym = SymMgr.conjureSymbol(nullptr, LCtx,
1584 SymMgr.getType(Pos.getOffset()),
1586 State = assumeNoOverflow(State, NewPosSym, 4);
1587 return Pos.setTo(NewPosSym);
1591 void IteratorChecker::reportOutOfRangeBug(const StringRef &Message,
1592 const SVal &Val, CheckerContext &C,
1593 ExplodedNode *ErrNode) const {
1594 auto R = llvm::make_unique<BugReport>(*OutOfRangeBugType, Message, ErrNode);
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 = llvm::make_unique<BugReport>(*MismatchedBugType, Message, ErrNode);
1604 R->markInteresting(Val1);
1605 R->markInteresting(Val2);
1606 C.emitReport(std::move(R));
1609 void IteratorChecker::reportMismatchedBug(const StringRef &Message,
1610 const SVal &Val, const MemRegion *Reg,
1612 ExplodedNode *ErrNode) const {
1613 auto R = llvm::make_unique<BugReport>(*MismatchedBugType, Message, ErrNode);
1614 R->markInteresting(Val);
1615 R->markInteresting(Reg);
1616 C.emitReport(std::move(R));
1619 void IteratorChecker::reportInvalidatedBug(const StringRef &Message,
1620 const SVal &Val, CheckerContext &C,
1621 ExplodedNode *ErrNode) const {
1622 auto R = llvm::make_unique<BugReport>(*InvalidatedBugType, Message, ErrNode);
1623 R->markInteresting(Val);
1624 C.emitReport(std::move(R));
1629 bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
1630 bool isGreater(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
1631 bool isEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
1632 bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
1633 BinaryOperator::Opcode Opc);
1634 bool compare(ProgramStateRef State, NonLoc NL1, NonLoc NL2,
1635 BinaryOperator::Opcode Opc);
1636 const CXXRecordDecl *getCXXRecordDecl(ProgramStateRef State,
1637 const MemRegion *Reg);
1638 SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB, SymbolRef Expr,
1639 SymbolRef OldSym, SymbolRef NewSym);
1641 bool isIteratorType(const QualType &Type) {
1642 if (Type->isPointerType())
1645 const auto *CRD = Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
1646 return isIterator(CRD);
1649 bool isIterator(const CXXRecordDecl *CRD) {
1653 const auto Name = CRD->getName();
1654 if (!(Name.endswith_lower("iterator") || Name.endswith_lower("iter") ||
1655 Name.endswith_lower("it")))
1658 bool HasCopyCtor = false, HasCopyAssign = true, HasDtor = false,
1659 HasPreIncrOp = false, HasPostIncrOp = false, HasDerefOp = false;
1660 for (const auto *Method : CRD->methods()) {
1661 if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(Method)) {
1662 if (Ctor->isCopyConstructor()) {
1663 HasCopyCtor = !Ctor->isDeleted() && Ctor->getAccess() == AS_public;
1667 if (const auto *Dtor = dyn_cast<CXXDestructorDecl>(Method)) {
1668 HasDtor = !Dtor->isDeleted() && Dtor->getAccess() == AS_public;
1671 if (Method->isCopyAssignmentOperator()) {
1672 HasCopyAssign = !Method->isDeleted() && Method->getAccess() == AS_public;
1675 if (!Method->isOverloadedOperator())
1677 const auto OPK = Method->getOverloadedOperator();
1678 if (OPK == OO_PlusPlus) {
1679 HasPreIncrOp = HasPreIncrOp || (Method->getNumParams() == 0);
1680 HasPostIncrOp = HasPostIncrOp || (Method->getNumParams() == 1);
1683 if (OPK == OO_Star) {
1684 HasDerefOp = (Method->getNumParams() == 0);
1689 return HasCopyCtor && HasCopyAssign && HasDtor && HasPreIncrOp &&
1690 HasPostIncrOp && HasDerefOp;
1693 bool isComparisonOperator(OverloadedOperatorKind OK) {
1694 return OK == OO_EqualEqual || OK == OO_ExclaimEqual || OK == OO_Less ||
1695 OK == OO_LessEqual || OK == OO_Greater || OK == OO_GreaterEqual;
1698 bool isBeginCall(const FunctionDecl *Func) {
1699 const auto *IdInfo = Func->getIdentifier();
1702 return IdInfo->getName().endswith_lower("begin");
1705 bool isEndCall(const FunctionDecl *Func) {
1706 const auto *IdInfo = Func->getIdentifier();
1709 return IdInfo->getName().endswith_lower("end");
1712 bool isAssignCall(const FunctionDecl *Func) {
1713 const auto *IdInfo = Func->getIdentifier();
1716 if (Func->getNumParams() > 2)
1718 return IdInfo->getName() == "assign";
1721 bool isClearCall(const FunctionDecl *Func) {
1722 const auto *IdInfo = Func->getIdentifier();
1725 if (Func->getNumParams() > 0)
1727 return IdInfo->getName() == "clear";
1730 bool isPushBackCall(const FunctionDecl *Func) {
1731 const auto *IdInfo = Func->getIdentifier();
1734 if (Func->getNumParams() != 1)
1736 return IdInfo->getName() == "push_back";
1739 bool isEmplaceBackCall(const FunctionDecl *Func) {
1740 const auto *IdInfo = Func->getIdentifier();
1743 if (Func->getNumParams() < 1)
1745 return IdInfo->getName() == "emplace_back";
1748 bool isPopBackCall(const FunctionDecl *Func) {
1749 const auto *IdInfo = Func->getIdentifier();
1752 if (Func->getNumParams() > 0)
1754 return IdInfo->getName() == "pop_back";
1757 bool isPushFrontCall(const FunctionDecl *Func) {
1758 const auto *IdInfo = Func->getIdentifier();
1761 if (Func->getNumParams() != 1)
1763 return IdInfo->getName() == "push_front";
1766 bool isEmplaceFrontCall(const FunctionDecl *Func) {
1767 const auto *IdInfo = Func->getIdentifier();
1770 if (Func->getNumParams() < 1)
1772 return IdInfo->getName() == "emplace_front";
1775 bool isPopFrontCall(const FunctionDecl *Func) {
1776 const auto *IdInfo = Func->getIdentifier();
1779 if (Func->getNumParams() > 0)
1781 return IdInfo->getName() == "pop_front";
1784 bool isInsertCall(const FunctionDecl *Func) {
1785 const auto *IdInfo = Func->getIdentifier();
1788 if (Func->getNumParams() < 2 || Func->getNumParams() > 3)
1790 if (!isIteratorType(Func->getParamDecl(0)->getType()))
1792 return IdInfo->getName() == "insert";
1795 bool isEmplaceCall(const FunctionDecl *Func) {
1796 const auto *IdInfo = Func->getIdentifier();
1799 if (Func->getNumParams() < 2)
1801 if (!isIteratorType(Func->getParamDecl(0)->getType()))
1803 return IdInfo->getName() == "emplace";
1806 bool isEraseCall(const FunctionDecl *Func) {
1807 const auto *IdInfo = Func->getIdentifier();
1810 if (Func->getNumParams() < 1 || Func->getNumParams() > 2)
1812 if (!isIteratorType(Func->getParamDecl(0)->getType()))
1814 if (Func->getNumParams() == 2 &&
1815 !isIteratorType(Func->getParamDecl(1)->getType()))
1817 return IdInfo->getName() == "erase";
1820 bool isEraseAfterCall(const FunctionDecl *Func) {
1821 const auto *IdInfo = Func->getIdentifier();
1824 if (Func->getNumParams() < 1 || Func->getNumParams() > 2)
1826 if (!isIteratorType(Func->getParamDecl(0)->getType()))
1828 if (Func->getNumParams() == 2 &&
1829 !isIteratorType(Func->getParamDecl(1)->getType()))
1831 return IdInfo->getName() == "erase_after";
1834 bool isAssignmentOperator(OverloadedOperatorKind OK) { return OK == OO_Equal; }
1836 bool isSimpleComparisonOperator(OverloadedOperatorKind OK) {
1837 return OK == OO_EqualEqual || OK == OO_ExclaimEqual;
1840 bool isAccessOperator(OverloadedOperatorKind OK) {
1841 return isDereferenceOperator(OK) || isIncrementOperator(OK) ||
1842 isDecrementOperator(OK) || isRandomIncrOrDecrOperator(OK);
1845 bool isDereferenceOperator(OverloadedOperatorKind OK) {
1846 return OK == OO_Star || OK == OO_Arrow || OK == OO_ArrowStar ||
1850 bool isIncrementOperator(OverloadedOperatorKind OK) {
1851 return OK == OO_PlusPlus;
1854 bool isDecrementOperator(OverloadedOperatorKind OK) {
1855 return OK == OO_MinusMinus;
1858 bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK) {
1859 return OK == OO_Plus || OK == OO_PlusEqual || OK == OO_Minus ||
1860 OK == OO_MinusEqual;
1863 bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg) {
1864 const auto *CRD = getCXXRecordDecl(State, Reg);
1868 for (const auto *Method : CRD->methods()) {
1869 if (!Method->isOverloadedOperator())
1871 const auto OPK = Method->getOverloadedOperator();
1872 if (OPK == OO_Subscript) {
1879 bool frontModifiable(ProgramStateRef State, const MemRegion *Reg) {
1880 const auto *CRD = getCXXRecordDecl(State, Reg);
1884 for (const auto *Method : CRD->methods()) {
1885 if (!Method->getDeclName().isIdentifier())
1887 if (Method->getName() == "push_front" || Method->getName() == "pop_front") {
1894 bool backModifiable(ProgramStateRef State, const MemRegion *Reg) {
1895 const auto *CRD = getCXXRecordDecl(State, Reg);
1899 for (const auto *Method : CRD->methods()) {
1900 if (!Method->getDeclName().isIdentifier())
1902 if (Method->getName() == "push_back" || Method->getName() == "pop_back") {
1909 const CXXRecordDecl *getCXXRecordDecl(ProgramStateRef State,
1910 const MemRegion *Reg) {
1911 auto TI = getDynamicTypeInfo(State, Reg);
1915 auto Type = TI.getType();
1916 if (const auto *RefT = Type->getAs<ReferenceType>()) {
1917 Type = RefT->getPointeeType();
1920 return Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
1923 SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont) {
1924 const auto *CDataPtr = getContainerData(State, Cont);
1928 return CDataPtr->getBegin();
1931 SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont) {
1932 const auto *CDataPtr = getContainerData(State, Cont);
1936 return CDataPtr->getEnd();
1939 ProgramStateRef createContainerBegin(ProgramStateRef State,
1940 const MemRegion *Cont, const Expr *E,
1941 QualType T, const LocationContext *LCtx,
1942 unsigned BlockCount) {
1943 // Only create if it does not exist
1944 const auto *CDataPtr = getContainerData(State, Cont);
1945 if (CDataPtr && CDataPtr->getBegin())
1948 auto &SymMgr = State->getSymbolManager();
1949 const SymbolConjured *Sym = SymMgr.conjureSymbol(E, LCtx, T, BlockCount,
1951 State = assumeNoOverflow(State, Sym, 4);
1954 const auto CData = CDataPtr->newBegin(Sym);
1955 return setContainerData(State, Cont, CData);
1958 const auto CData = ContainerData::fromBegin(Sym);
1959 return setContainerData(State, Cont, CData);
1962 ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
1963 const Expr *E, QualType T,
1964 const LocationContext *LCtx,
1965 unsigned BlockCount) {
1966 // Only create if it does not exist
1967 const auto *CDataPtr = getContainerData(State, Cont);
1968 if (CDataPtr && CDataPtr->getEnd())
1971 auto &SymMgr = State->getSymbolManager();
1972 const SymbolConjured *Sym = SymMgr.conjureSymbol(E, LCtx, T, BlockCount,
1974 State = assumeNoOverflow(State, Sym, 4);
1977 const auto CData = CDataPtr->newEnd(Sym);
1978 return setContainerData(State, Cont, CData);
1981 const auto CData = ContainerData::fromEnd(Sym);
1982 return setContainerData(State, Cont, CData);
1985 const ContainerData *getContainerData(ProgramStateRef State,
1986 const MemRegion *Cont) {
1987 return State->get<ContainerMap>(Cont);
1990 ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
1991 const ContainerData &CData) {
1992 return State->set<ContainerMap>(Cont, CData);
1995 const IteratorPosition *getIteratorPosition(ProgramStateRef State,
1997 if (auto Reg = Val.getAsRegion()) {
1998 Reg = Reg->getMostDerivedObjectRegion();
1999 return State->get<IteratorRegionMap>(Reg);
2000 } else if (const auto Sym = Val.getAsSymbol()) {
2001 return State->get<IteratorSymbolMap>(Sym);
2002 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
2003 return State->get<IteratorRegionMap>(LCVal->getRegion());
2008 ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
2009 const IteratorPosition &Pos) {
2010 if (auto Reg = Val.getAsRegion()) {
2011 Reg = Reg->getMostDerivedObjectRegion();
2012 return State->set<IteratorRegionMap>(Reg, Pos);
2013 } else if (const auto Sym = Val.getAsSymbol()) {
2014 return State->set<IteratorSymbolMap>(Sym, Pos);
2015 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
2016 return State->set<IteratorRegionMap>(LCVal->getRegion(), Pos);
2021 ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val) {
2022 if (auto Reg = Val.getAsRegion()) {
2023 Reg = Reg->getMostDerivedObjectRegion();
2024 return State->remove<IteratorRegionMap>(Reg);
2025 } else if (const auto Sym = Val.getAsSymbol()) {
2026 return State->remove<IteratorSymbolMap>(Sym);
2027 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
2028 return State->remove<IteratorRegionMap>(LCVal->getRegion());
2033 ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1,
2034 SymbolRef Sym2, bool Equal) {
2035 auto &SVB = State->getStateManager().getSValBuilder();
2037 // FIXME: This code should be reworked as follows:
2038 // 1. Subtract the operands using evalBinOp().
2039 // 2. Assume that the result doesn't overflow.
2040 // 3. Compare the result to 0.
2041 // 4. Assume the result of the comparison.
2042 const auto comparison =
2043 SVB.evalBinOp(State, BO_EQ, nonloc::SymbolVal(Sym1),
2044 nonloc::SymbolVal(Sym2), SVB.getConditionType());
2046 assert(comparison.getAs<DefinedSVal>() &&
2047 "Symbol comparison must be a `DefinedSVal`");
2049 auto NewState = State->assume(comparison.castAs<DefinedSVal>(), Equal);
2053 if (const auto CompSym = comparison.getAsSymbol()) {
2054 assert(isa<SymIntExpr>(CompSym) &&
2055 "Symbol comparison must be a `SymIntExpr`");
2056 assert(BinaryOperator::isComparisonOp(
2057 cast<SymIntExpr>(CompSym)->getOpcode()) &&
2058 "Symbol comparison must be a comparison");
2059 return assumeNoOverflow(NewState, cast<SymIntExpr>(CompSym)->getLHS(), 2);
2065 bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont) {
2066 auto RegionMap = State->get<IteratorRegionMap>();
2067 for (const auto Reg : RegionMap) {
2068 if (Reg.second.getContainer() == Cont)
2072 auto SymbolMap = State->get<IteratorSymbolMap>();
2073 for (const auto Sym : SymbolMap) {
2074 if (Sym.second.getContainer() == Cont)
2081 bool isBoundThroughLazyCompoundVal(const Environment &Env,
2082 const MemRegion *Reg) {
2083 for (const auto Binding: Env) {
2084 if (const auto LCVal = Binding.second.getAs<nonloc::LazyCompoundVal>()) {
2085 if (LCVal->getRegion() == Reg)
2093 // This function tells the analyzer's engine that symbols produced by our
2094 // checker, most notably iterator positions, are relatively small.
2095 // A distance between items in the container should not be very large.
2096 // By assuming that it is within around 1/8 of the address space,
2097 // we can help the analyzer perform operations on these symbols
2098 // without being afraid of integer overflows.
2099 // FIXME: Should we provide it as an API, so that all checkers could use it?
2100 ProgramStateRef assumeNoOverflow(ProgramStateRef State, SymbolRef Sym,
2102 SValBuilder &SVB = State->getStateManager().getSValBuilder();
2103 BasicValueFactory &BV = SVB.getBasicValueFactory();
2105 QualType T = Sym->getType();
2106 assert(T->isSignedIntegerOrEnumerationType());
2107 APSIntType AT = BV.getAPSIntType(T);
2109 ProgramStateRef NewState = State;
2111 llvm::APSInt Max = AT.getMaxValue() / AT.getValue(Scale);
2112 SVal IsCappedFromAbove =
2113 SVB.evalBinOpNN(State, BO_LE, nonloc::SymbolVal(Sym),
2114 nonloc::ConcreteInt(Max), SVB.getConditionType());
2115 if (auto DV = IsCappedFromAbove.getAs<DefinedSVal>()) {
2116 NewState = NewState->assume(*DV, true);
2121 llvm::APSInt Min = -Max;
2122 SVal IsCappedFromBelow =
2123 SVB.evalBinOpNN(State, BO_GE, nonloc::SymbolVal(Sym),
2124 nonloc::ConcreteInt(Min), SVB.getConditionType());
2125 if (auto DV = IsCappedFromBelow.getAs<DefinedSVal>()) {
2126 NewState = NewState->assume(*DV, true);
2134 template <typename Condition, typename Process>
2135 ProgramStateRef processIteratorPositions(ProgramStateRef State, Condition Cond,
2137 auto &RegionMapFactory = State->get_context<IteratorRegionMap>();
2138 auto RegionMap = State->get<IteratorRegionMap>();
2139 bool Changed = false;
2140 for (const auto Reg : RegionMap) {
2141 if (Cond(Reg.second)) {
2142 RegionMap = RegionMapFactory.add(RegionMap, Reg.first, Proc(Reg.second));
2148 State = State->set<IteratorRegionMap>(RegionMap);
2150 auto &SymbolMapFactory = State->get_context<IteratorSymbolMap>();
2151 auto SymbolMap = State->get<IteratorSymbolMap>();
2153 for (const auto Sym : SymbolMap) {
2154 if (Cond(Sym.second)) {
2155 SymbolMap = SymbolMapFactory.add(SymbolMap, Sym.first, Proc(Sym.second));
2161 State = State->set<IteratorSymbolMap>(SymbolMap);
2166 ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State,
2167 const MemRegion *Cont) {
2168 auto MatchCont = [&](const IteratorPosition &Pos) {
2169 return Pos.getContainer() == Cont;
2171 auto Invalidate = [&](const IteratorPosition &Pos) {
2172 return Pos.invalidate();
2174 return processIteratorPositions(State, MatchCont, Invalidate);
2178 invalidateAllIteratorPositionsExcept(ProgramStateRef State,
2179 const MemRegion *Cont, SymbolRef Offset,
2180 BinaryOperator::Opcode Opc) {
2181 auto MatchContAndCompare = [&](const IteratorPosition &Pos) {
2182 return Pos.getContainer() == Cont &&
2183 !compare(State, Pos.getOffset(), Offset, Opc);
2185 auto Invalidate = [&](const IteratorPosition &Pos) {
2186 return Pos.invalidate();
2188 return processIteratorPositions(State, MatchContAndCompare, Invalidate);
2191 ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
2193 BinaryOperator::Opcode Opc) {
2194 auto Compare = [&](const IteratorPosition &Pos) {
2195 return compare(State, Pos.getOffset(), Offset, Opc);
2197 auto Invalidate = [&](const IteratorPosition &Pos) {
2198 return Pos.invalidate();
2200 return processIteratorPositions(State, Compare, Invalidate);
2203 ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
2205 BinaryOperator::Opcode Opc1,
2207 BinaryOperator::Opcode Opc2) {
2208 auto Compare = [&](const IteratorPosition &Pos) {
2209 return compare(State, Pos.getOffset(), Offset1, Opc1) &&
2210 compare(State, Pos.getOffset(), Offset2, Opc2);
2212 auto Invalidate = [&](const IteratorPosition &Pos) {
2213 return Pos.invalidate();
2215 return processIteratorPositions(State, Compare, Invalidate);
2218 ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State,
2219 const MemRegion *Cont,
2220 const MemRegion *NewCont) {
2221 auto MatchCont = [&](const IteratorPosition &Pos) {
2222 return Pos.getContainer() == Cont;
2224 auto ReAssign = [&](const IteratorPosition &Pos) {
2225 return Pos.reAssign(NewCont);
2227 return processIteratorPositions(State, MatchCont, ReAssign);
2230 ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State,
2231 const MemRegion *Cont,
2232 const MemRegion *NewCont,
2234 BinaryOperator::Opcode Opc) {
2235 auto MatchContAndCompare = [&](const IteratorPosition &Pos) {
2236 return Pos.getContainer() == Cont &&
2237 !compare(State, Pos.getOffset(), Offset, Opc);
2239 auto ReAssign = [&](const IteratorPosition &Pos) {
2240 return Pos.reAssign(NewCont);
2242 return processIteratorPositions(State, MatchContAndCompare, ReAssign);
2245 // This function rebases symbolic expression `OldSym + Int` to `NewSym + Int`,
2246 // `OldSym - Int` to `NewSym - Int` and `OldSym` to `NewSym` in any iterator
2247 // position offsets where `CondSym` is true.
2248 ProgramStateRef rebaseSymbolInIteratorPositionsIf(
2249 ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym,
2250 SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc) {
2251 auto LessThanEnd = [&](const IteratorPosition &Pos) {
2252 return compare(State, Pos.getOffset(), CondSym, Opc);
2254 auto RebaseSymbol = [&](const IteratorPosition &Pos) {
2255 return Pos.setTo(rebaseSymbol(State, SVB, Pos.getOffset(), OldSym,
2258 return processIteratorPositions(State, LessThanEnd, RebaseSymbol);
2261 // This function rebases symbolic expression `OldExpr + Int` to `NewExpr + Int`,
2262 // `OldExpr - Int` to `NewExpr - Int` and `OldExpr` to `NewExpr` in expression
2264 SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB,
2265 SymbolRef OrigExpr, SymbolRef OldExpr,
2267 auto &SymMgr = SVB.getSymbolManager();
2268 auto Diff = SVB.evalBinOpNN(State, BO_Sub, nonloc::SymbolVal(OrigExpr),
2269 nonloc::SymbolVal(OldExpr),
2270 SymMgr.getType(OrigExpr));
2272 const auto DiffInt = Diff.getAs<nonloc::ConcreteInt>();
2276 return SVB.evalBinOpNN(State, BO_Add, *DiffInt, nonloc::SymbolVal(NewSym),
2277 SymMgr.getType(OrigExpr)).getAsSymbol();
2280 bool isZero(ProgramStateRef State, const NonLoc &Val) {
2281 auto &BVF = State->getBasicVals();
2282 return compare(State, Val,
2283 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))),
2287 bool isPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos) {
2288 const auto *Cont = Pos.getContainer();
2289 const auto *CData = getContainerData(State, Cont);
2293 const auto End = CData->getEnd();
2295 if (isEqual(State, Pos.getOffset(), End)) {
2303 bool isAheadOfRange(ProgramStateRef State, const IteratorPosition &Pos) {
2304 const auto *Cont = Pos.getContainer();
2305 const auto *CData = getContainerData(State, Cont);
2309 const auto Beg = CData->getBegin();
2311 if (isLess(State, Pos.getOffset(), Beg)) {
2319 bool isBehindPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos) {
2320 const auto *Cont = Pos.getContainer();
2321 const auto *CData = getContainerData(State, Cont);
2325 const auto End = CData->getEnd();
2327 if (isGreater(State, Pos.getOffset(), End)) {
2335 bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
2336 return compare(State, Sym1, Sym2, BO_LT);
2339 bool isGreater(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
2340 return compare(State, Sym1, Sym2, BO_GT);
2343 bool isEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
2344 return compare(State, Sym1, Sym2, BO_EQ);
2347 bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
2348 BinaryOperator::Opcode Opc) {
2349 return compare(State, nonloc::SymbolVal(Sym1), nonloc::SymbolVal(Sym2), Opc);
2352 bool compare(ProgramStateRef State, NonLoc NL1, NonLoc NL2,
2353 BinaryOperator::Opcode Opc) {
2354 auto &SVB = State->getStateManager().getSValBuilder();
2356 const auto comparison =
2357 SVB.evalBinOp(State, Opc, NL1, NL2, SVB.getConditionType());
2359 assert(comparison.getAs<DefinedSVal>() &&
2360 "Symbol comparison must be a `DefinedSVal`");
2362 return !State->assume(comparison.castAs<DefinedSVal>(), false);
2367 void ento::registerIteratorModeling(CheckerManager &mgr) {
2368 mgr.registerChecker<IteratorChecker>();
2371 bool ento::shouldRegisterIteratorModeling(const LangOptions &LO) {
2375 #define REGISTER_CHECKER(name) \
2376 void ento::register##name(CheckerManager &Mgr) { \
2377 auto *checker = Mgr.getChecker<IteratorChecker>(); \
2378 checker->ChecksEnabled[IteratorChecker::CK_##name] = true; \
2379 checker->CheckNames[IteratorChecker::CK_##name] = \
2380 Mgr.getCurrentCheckName(); \
2383 bool ento::shouldRegister##name(const LangOptions &LO) { \
2387 REGISTER_CHECKER(IteratorRangeChecker)
2388 REGISTER_CHECKER(MismatchedIteratorChecker)
2389 REGISTER_CHECKER(InvalidatedIteratorChecker)