1 //===-- IteratorModeling.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"
80 using namespace clang;
82 using namespace iterator;
86 class IteratorModeling
87 : public Checker<check::PostCall, check::PostStmt<MaterializeTemporaryExpr>,
88 check::Bind, check::LiveSymbols, check::DeadSymbols> {
90 void handleComparison(CheckerContext &C, const Expr *CE, SVal RetVal,
91 const SVal &LVal, const SVal &RVal,
92 OverloadedOperatorKind Op) const;
93 void processComparison(CheckerContext &C, ProgramStateRef State,
94 SymbolRef Sym1, SymbolRef Sym2, const SVal &RetVal,
95 OverloadedOperatorKind Op) const;
96 void handleIncrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
98 void handleDecrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
100 void handleRandomIncrOrDecr(CheckerContext &C, const Expr *CE,
101 OverloadedOperatorKind Op, const SVal &RetVal,
102 const SVal &LHS, const SVal &RHS) const;
103 void handleBegin(CheckerContext &C, const Expr *CE, const SVal &RetVal,
104 const SVal &Cont) const;
105 void handleEnd(CheckerContext &C, const Expr *CE, const SVal &RetVal,
106 const SVal &Cont) const;
107 void assignToContainer(CheckerContext &C, const Expr *CE, const SVal &RetVal,
108 const MemRegion *Cont) const;
109 void handleAssign(CheckerContext &C, const SVal &Cont,
110 const Expr *CE = nullptr,
111 const SVal &OldCont = UndefinedVal()) const;
112 void handleClear(CheckerContext &C, const SVal &Cont) const;
113 void handlePushBack(CheckerContext &C, const SVal &Cont) const;
114 void handlePopBack(CheckerContext &C, const SVal &Cont) const;
115 void handlePushFront(CheckerContext &C, const SVal &Cont) const;
116 void handlePopFront(CheckerContext &C, const SVal &Cont) const;
117 void handleInsert(CheckerContext &C, const SVal &Iter) const;
118 void handleErase(CheckerContext &C, const SVal &Iter) const;
119 void handleErase(CheckerContext &C, const SVal &Iter1,
120 const SVal &Iter2) const;
121 void handleEraseAfter(CheckerContext &C, const SVal &Iter) const;
122 void handleEraseAfter(CheckerContext &C, const SVal &Iter1,
123 const SVal &Iter2) const;
124 void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
125 const char *Sep) const override;
128 IteratorModeling() {}
130 void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
131 void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const;
132 void checkPostStmt(const CXXConstructExpr *CCE, CheckerContext &C) const;
133 void checkPostStmt(const DeclStmt *DS, CheckerContext &C) const;
134 void checkPostStmt(const MaterializeTemporaryExpr *MTE,
135 CheckerContext &C) const;
136 void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
137 void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
140 bool isBeginCall(const FunctionDecl *Func);
141 bool isEndCall(const FunctionDecl *Func);
142 bool isAssignCall(const FunctionDecl *Func);
143 bool isClearCall(const FunctionDecl *Func);
144 bool isPushBackCall(const FunctionDecl *Func);
145 bool isEmplaceBackCall(const FunctionDecl *Func);
146 bool isPopBackCall(const FunctionDecl *Func);
147 bool isPushFrontCall(const FunctionDecl *Func);
148 bool isEmplaceFrontCall(const FunctionDecl *Func);
149 bool isPopFrontCall(const FunctionDecl *Func);
150 bool isAssignmentOperator(OverloadedOperatorKind OK);
151 bool isSimpleComparisonOperator(OverloadedOperatorKind OK);
152 bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg);
153 bool frontModifiable(ProgramStateRef State, const MemRegion *Reg);
154 bool backModifiable(ProgramStateRef State, const MemRegion *Reg);
155 SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont);
156 SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont);
157 ProgramStateRef createContainerBegin(ProgramStateRef State,
158 const MemRegion *Cont, const Expr *E,
159 QualType T, const LocationContext *LCtx,
160 unsigned BlockCount);
161 ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
162 const Expr *E, QualType T,
163 const LocationContext *LCtx,
164 unsigned BlockCount);
165 ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
166 const ContainerData &CData);
167 ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val);
168 ProgramStateRef assumeNoOverflow(ProgramStateRef State, SymbolRef Sym,
170 ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State,
171 const MemRegion *Cont);
173 invalidateAllIteratorPositionsExcept(ProgramStateRef State,
174 const MemRegion *Cont, SymbolRef Offset,
175 BinaryOperator::Opcode Opc);
176 ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
178 BinaryOperator::Opcode Opc);
179 ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
181 BinaryOperator::Opcode Opc1,
183 BinaryOperator::Opcode Opc2);
184 ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State,
185 const MemRegion *Cont,
186 const MemRegion *NewCont);
187 ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State,
188 const MemRegion *Cont,
189 const MemRegion *NewCont,
191 BinaryOperator::Opcode Opc);
192 ProgramStateRef rebaseSymbolInIteratorPositionsIf(
193 ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym,
194 SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc);
195 ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1,
196 SymbolRef Sym2, bool Equal);
197 SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB, SymbolRef Expr,
198 SymbolRef OldSym, SymbolRef NewSym);
199 bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont);
200 bool isBoundThroughLazyCompoundVal(const Environment &Env,
201 const MemRegion *Reg);
205 void IteratorModeling::checkPostCall(const CallEvent &Call,
206 CheckerContext &C) const {
207 // Record new iterator positions and iterator position changes
208 const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
212 if (Func->isOverloadedOperator()) {
213 const auto Op = Func->getOverloadedOperator();
214 if (isAssignmentOperator(Op)) {
215 // Overloaded 'operator=' must be a non-static member function.
216 const auto *InstCall = cast<CXXInstanceCall>(&Call);
217 if (cast<CXXMethodDecl>(Func)->isMoveAssignmentOperator()) {
218 handleAssign(C, InstCall->getCXXThisVal(), Call.getOriginExpr(),
223 handleAssign(C, InstCall->getCXXThisVal());
225 } else if (isSimpleComparisonOperator(Op)) {
226 const auto *OrigExpr = Call.getOriginExpr();
230 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
231 handleComparison(C, OrigExpr, Call.getReturnValue(),
232 InstCall->getCXXThisVal(), Call.getArgSVal(0), Op);
236 handleComparison(C, OrigExpr, Call.getReturnValue(), Call.getArgSVal(0),
237 Call.getArgSVal(1), Op);
239 } else if (isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) {
240 const auto *OrigExpr = Call.getOriginExpr();
244 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
245 if (Call.getNumArgs() >= 1 &&
246 Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) {
247 handleRandomIncrOrDecr(C, OrigExpr, Func->getOverloadedOperator(),
248 Call.getReturnValue(),
249 InstCall->getCXXThisVal(), Call.getArgSVal(0));
253 if (Call.getNumArgs() >= 2 &&
254 Call.getArgExpr(1)->getType()->isIntegralOrEnumerationType()) {
255 handleRandomIncrOrDecr(C, OrigExpr, Func->getOverloadedOperator(),
256 Call.getReturnValue(), Call.getArgSVal(0),
261 } else if (isIncrementOperator(Func->getOverloadedOperator())) {
262 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
263 handleIncrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
268 handleIncrement(C, Call.getReturnValue(), Call.getArgSVal(0),
271 } else if (isDecrementOperator(Func->getOverloadedOperator())) {
272 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
273 handleDecrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
278 handleDecrement(C, Call.getReturnValue(), Call.getArgSVal(0),
283 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
284 if (isAssignCall(Func)) {
285 handleAssign(C, InstCall->getCXXThisVal());
289 if (isClearCall(Func)) {
290 handleClear(C, InstCall->getCXXThisVal());
294 if (isPushBackCall(Func) || isEmplaceBackCall(Func)) {
295 handlePushBack(C, InstCall->getCXXThisVal());
299 if (isPopBackCall(Func)) {
300 handlePopBack(C, InstCall->getCXXThisVal());
304 if (isPushFrontCall(Func) || isEmplaceFrontCall(Func)) {
305 handlePushFront(C, InstCall->getCXXThisVal());
309 if (isPopFrontCall(Func)) {
310 handlePopFront(C, InstCall->getCXXThisVal());
314 if (isInsertCall(Func) || isEmplaceCall(Func)) {
315 handleInsert(C, Call.getArgSVal(0));
319 if (isEraseCall(Func)) {
320 if (Call.getNumArgs() == 1) {
321 handleErase(C, Call.getArgSVal(0));
325 if (Call.getNumArgs() == 2) {
326 handleErase(C, Call.getArgSVal(0), Call.getArgSVal(1));
331 if (isEraseAfterCall(Func)) {
332 if (Call.getNumArgs() == 1) {
333 handleEraseAfter(C, Call.getArgSVal(0));
337 if (Call.getNumArgs() == 2) {
338 handleEraseAfter(C, Call.getArgSVal(0), Call.getArgSVal(1));
344 const auto *OrigExpr = Call.getOriginExpr();
348 if (!isIteratorType(Call.getResultType()))
351 auto State = C.getState();
353 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
354 if (isBeginCall(Func)) {
355 handleBegin(C, OrigExpr, Call.getReturnValue(),
356 InstCall->getCXXThisVal());
360 if (isEndCall(Func)) {
361 handleEnd(C, OrigExpr, Call.getReturnValue(),
362 InstCall->getCXXThisVal());
367 // Already bound to container?
368 if (getIteratorPosition(State, Call.getReturnValue()))
371 // Copy-like and move constructors
372 if (isa<CXXConstructorCall>(&Call) && Call.getNumArgs() == 1) {
373 if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(0))) {
374 State = setIteratorPosition(State, Call.getReturnValue(), *Pos);
375 if (cast<CXXConstructorDecl>(Func)->isMoveConstructor()) {
376 State = removeIteratorPosition(State, Call.getArgSVal(0));
378 C.addTransition(State);
383 // Assumption: if return value is an iterator which is not yet bound to a
384 // container, then look for the first iterator argument, and
385 // bind the return value to the same container. This approach
386 // works for STL algorithms.
387 // FIXME: Add a more conservative mode
388 for (unsigned i = 0; i < Call.getNumArgs(); ++i) {
389 if (isIteratorType(Call.getArgExpr(i)->getType())) {
390 if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(i))) {
391 assignToContainer(C, OrigExpr, Call.getReturnValue(),
392 Pos->getContainer());
400 void IteratorModeling::checkBind(SVal Loc, SVal Val, const Stmt *S,
401 CheckerContext &C) const {
402 auto State = C.getState();
403 const auto *Pos = getIteratorPosition(State, Val);
405 State = setIteratorPosition(State, Loc, *Pos);
406 C.addTransition(State);
408 const auto *OldPos = getIteratorPosition(State, Loc);
410 State = removeIteratorPosition(State, Loc);
411 C.addTransition(State);
416 void IteratorModeling::checkPostStmt(const MaterializeTemporaryExpr *MTE,
417 CheckerContext &C) const {
418 /* Transfer iterator state to temporary objects */
419 auto State = C.getState();
420 const auto *Pos = getIteratorPosition(State, C.getSVal(MTE->getSubExpr()));
423 State = setIteratorPosition(State, C.getSVal(MTE), *Pos);
424 C.addTransition(State);
427 void IteratorModeling::checkLiveSymbols(ProgramStateRef State,
428 SymbolReaper &SR) const {
429 // Keep symbolic expressions of iterator positions, container begins and ends
431 auto RegionMap = State->get<IteratorRegionMap>();
432 for (const auto &Reg : RegionMap) {
433 const auto Offset = Reg.second.getOffset();
434 for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
435 if (isa<SymbolData>(*i))
439 auto SymbolMap = State->get<IteratorSymbolMap>();
440 for (const auto &Sym : SymbolMap) {
441 const auto Offset = Sym.second.getOffset();
442 for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
443 if (isa<SymbolData>(*i))
447 auto ContMap = State->get<ContainerMap>();
448 for (const auto &Cont : ContMap) {
449 const auto CData = Cont.second;
450 if (CData.getBegin()) {
451 SR.markLive(CData.getBegin());
452 if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getBegin()))
453 SR.markLive(SIE->getLHS());
455 if (CData.getEnd()) {
456 SR.markLive(CData.getEnd());
457 if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getEnd()))
458 SR.markLive(SIE->getLHS());
463 void IteratorModeling::checkDeadSymbols(SymbolReaper &SR,
464 CheckerContext &C) const {
466 auto State = C.getState();
468 auto RegionMap = State->get<IteratorRegionMap>();
469 for (const auto &Reg : RegionMap) {
470 if (!SR.isLiveRegion(Reg.first)) {
471 // The region behind the `LazyCompoundVal` is often cleaned up before
472 // the `LazyCompoundVal` itself. If there are iterator positions keyed
473 // by these regions their cleanup must be deferred.
474 if (!isBoundThroughLazyCompoundVal(State->getEnvironment(), Reg.first)) {
475 State = State->remove<IteratorRegionMap>(Reg.first);
480 auto SymbolMap = State->get<IteratorSymbolMap>();
481 for (const auto &Sym : SymbolMap) {
482 if (!SR.isLive(Sym.first)) {
483 State = State->remove<IteratorSymbolMap>(Sym.first);
487 auto ContMap = State->get<ContainerMap>();
488 for (const auto &Cont : ContMap) {
489 if (!SR.isLiveRegion(Cont.first)) {
490 // We must keep the container data while it has live iterators to be able
491 // to compare them to the begin and the end of the container.
492 if (!hasLiveIterators(State, Cont.first)) {
493 State = State->remove<ContainerMap>(Cont.first);
498 C.addTransition(State);
501 void IteratorModeling::handleComparison(CheckerContext &C, const Expr *CE,
502 SVal RetVal, const SVal &LVal,
504 OverloadedOperatorKind Op) const {
505 // Record the operands and the operator of the comparison for the next
506 // evalAssume, if the result is a symbolic expression. If it is a concrete
507 // value (only one branch is possible), then transfer the state between
508 // the operands according to the operator and the result
509 auto State = C.getState();
510 const auto *LPos = getIteratorPosition(State, LVal);
511 const auto *RPos = getIteratorPosition(State, RVal);
512 const MemRegion *Cont = nullptr;
514 Cont = LPos->getContainer();
516 Cont = RPos->getContainer();
521 // At least one of the iterators have recorded positions. If one of them has
522 // not then create a new symbol for the offset.
524 if (!LPos || !RPos) {
525 auto &SymMgr = C.getSymbolManager();
526 Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
527 C.getASTContext().LongTy, C.blockCount());
528 State = assumeNoOverflow(State, Sym, 4);
532 State = setIteratorPosition(State, LVal,
533 IteratorPosition::getPosition(Cont, Sym));
534 LPos = getIteratorPosition(State, LVal);
536 State = setIteratorPosition(State, RVal,
537 IteratorPosition::getPosition(Cont, Sym));
538 RPos = getIteratorPosition(State, RVal);
541 // We cannot make assumpotions on `UnknownVal`. Let us conjure a symbol
543 if (RetVal.isUnknown()) {
544 auto &SymMgr = C.getSymbolManager();
545 auto *LCtx = C.getLocationContext();
546 RetVal = nonloc::SymbolVal(SymMgr.conjureSymbol(
547 CE, LCtx, C.getASTContext().BoolTy, C.blockCount()));
548 State = State->BindExpr(CE, LCtx, RetVal);
551 processComparison(C, State, LPos->getOffset(), RPos->getOffset(), RetVal, Op);
554 void IteratorModeling::processComparison(CheckerContext &C,
555 ProgramStateRef State, SymbolRef Sym1,
556 SymbolRef Sym2, const SVal &RetVal,
557 OverloadedOperatorKind Op) const {
558 if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) {
559 if ((State = relateSymbols(State, Sym1, Sym2,
560 (Op == OO_EqualEqual) ==
561 (TruthVal->getValue() != 0)))) {
562 C.addTransition(State);
564 C.generateSink(State, C.getPredecessor());
569 const auto ConditionVal = RetVal.getAs<DefinedSVal>();
573 if (auto StateTrue = relateSymbols(State, Sym1, Sym2, Op == OO_EqualEqual)) {
574 StateTrue = StateTrue->assume(*ConditionVal, true);
575 C.addTransition(StateTrue);
578 if (auto StateFalse = relateSymbols(State, Sym1, Sym2, Op != OO_EqualEqual)) {
579 StateFalse = StateFalse->assume(*ConditionVal, false);
580 C.addTransition(StateFalse);
584 void IteratorModeling::handleIncrement(CheckerContext &C, const SVal &RetVal,
585 const SVal &Iter, bool Postfix) const {
586 // Increment the symbolic expressions which represents the position of the
588 auto State = C.getState();
589 auto &BVF = C.getSymbolManager().getBasicVals();
591 const auto *Pos = getIteratorPosition(State, Iter);
596 advancePosition(State, Iter, OO_Plus,
597 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
599 "Advancing position by concrete int should always be successful");
601 const auto *NewPos = getIteratorPosition(NewState, Iter);
603 "Iterator should have position after successful advancement");
605 State = setIteratorPosition(State, Iter, *NewPos);
606 State = setIteratorPosition(State, RetVal, Postfix ? *Pos : *NewPos);
607 C.addTransition(State);
610 void IteratorModeling::handleDecrement(CheckerContext &C, const SVal &RetVal,
611 const SVal &Iter, bool Postfix) const {
612 // Decrement the symbolic expressions which represents the position of the
614 auto State = C.getState();
615 auto &BVF = C.getSymbolManager().getBasicVals();
617 const auto *Pos = getIteratorPosition(State, Iter);
622 advancePosition(State, Iter, OO_Minus,
623 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
625 "Advancing position by concrete int should always be successful");
627 const auto *NewPos = getIteratorPosition(NewState, Iter);
629 "Iterator should have position after successful advancement");
631 State = setIteratorPosition(State, Iter, *NewPos);
632 State = setIteratorPosition(State, RetVal, Postfix ? *Pos : *NewPos);
633 C.addTransition(State);
636 void IteratorModeling::handleRandomIncrOrDecr(CheckerContext &C,
638 OverloadedOperatorKind Op,
641 const SVal &RHS) const {
642 // Increment or decrement the symbolic expressions which represents the
643 // position of the iterator
644 auto State = C.getState();
646 const auto *Pos = getIteratorPosition(State, LHS);
650 const auto *value = &RHS;
651 if (auto loc = RHS.getAs<Loc>()) {
652 const auto val = State->getRawSVal(*loc);
656 auto &TgtVal = (Op == OO_PlusEqual || Op == OO_MinusEqual) ? LHS : RetVal;
659 advancePosition(State, LHS, Op, *value);
661 const auto *NewPos = getIteratorPosition(NewState, LHS);
663 "Iterator should have position after successful advancement");
665 State = setIteratorPosition(NewState, TgtVal, *NewPos);
666 C.addTransition(State);
668 assignToContainer(C, CE, TgtVal, Pos->getContainer());
672 void IteratorModeling::handleBegin(CheckerContext &C, const Expr *CE,
673 const SVal &RetVal, const SVal &Cont) const {
674 const auto *ContReg = Cont.getAsRegion();
678 ContReg = ContReg->getMostDerivedObjectRegion();
680 // If the container already has a begin symbol then use it. Otherwise first
682 auto State = C.getState();
683 auto BeginSym = getContainerBegin(State, ContReg);
685 State = createContainerBegin(State, ContReg, CE, C.getASTContext().LongTy,
686 C.getLocationContext(), C.blockCount());
687 BeginSym = getContainerBegin(State, ContReg);
689 State = setIteratorPosition(State, RetVal,
690 IteratorPosition::getPosition(ContReg, BeginSym));
691 C.addTransition(State);
694 void IteratorModeling::handleEnd(CheckerContext &C, const Expr *CE,
695 const SVal &RetVal, const SVal &Cont) const {
696 const auto *ContReg = Cont.getAsRegion();
700 ContReg = ContReg->getMostDerivedObjectRegion();
702 // If the container already has an end symbol then use it. Otherwise first
704 auto State = C.getState();
705 auto EndSym = getContainerEnd(State, ContReg);
707 State = createContainerEnd(State, ContReg, CE, C.getASTContext().LongTy,
708 C.getLocationContext(), C.blockCount());
709 EndSym = getContainerEnd(State, ContReg);
711 State = setIteratorPosition(State, RetVal,
712 IteratorPosition::getPosition(ContReg, EndSym));
713 C.addTransition(State);
716 void IteratorModeling::assignToContainer(CheckerContext &C, const Expr *CE,
718 const MemRegion *Cont) const {
719 Cont = Cont->getMostDerivedObjectRegion();
721 auto State = C.getState();
722 auto &SymMgr = C.getSymbolManager();
723 auto Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
724 C.getASTContext().LongTy, C.blockCount());
725 State = assumeNoOverflow(State, Sym, 4);
726 State = setIteratorPosition(State, RetVal,
727 IteratorPosition::getPosition(Cont, Sym));
728 C.addTransition(State);
731 void IteratorModeling::handleAssign(CheckerContext &C, const SVal &Cont,
732 const Expr *CE, const SVal &OldCont) const {
733 const auto *ContReg = Cont.getAsRegion();
737 ContReg = ContReg->getMostDerivedObjectRegion();
739 // Assignment of a new value to a container always invalidates all its
741 auto State = C.getState();
742 const auto CData = getContainerData(State, ContReg);
744 State = invalidateAllIteratorPositions(State, ContReg);
747 // In case of move, iterators of the old container (except the past-end
748 // iterators) remain valid but refer to the new container
749 if (!OldCont.isUndef()) {
750 const auto *OldContReg = OldCont.getAsRegion();
752 OldContReg = OldContReg->getMostDerivedObjectRegion();
753 const auto OldCData = getContainerData(State, OldContReg);
755 if (const auto OldEndSym = OldCData->getEnd()) {
756 // If we already assigned an "end" symbol to the old container, then
757 // first reassign all iterator positions to the new container which
758 // are not past the container (thus not greater or equal to the
759 // current "end" symbol).
760 State = reassignAllIteratorPositionsUnless(State, OldContReg, ContReg,
762 auto &SymMgr = C.getSymbolManager();
763 auto &SVB = C.getSValBuilder();
764 // Then generate and assign a new "end" symbol for the new container.
766 SymMgr.conjureSymbol(CE, C.getLocationContext(),
767 C.getASTContext().LongTy, C.blockCount());
768 State = assumeNoOverflow(State, NewEndSym, 4);
770 State = setContainerData(State, ContReg, CData->newEnd(NewEndSym));
772 State = setContainerData(State, ContReg,
773 ContainerData::fromEnd(NewEndSym));
775 // Finally, replace the old "end" symbol in the already reassigned
776 // iterator positions with the new "end" symbol.
777 State = rebaseSymbolInIteratorPositionsIf(
778 State, SVB, OldEndSym, NewEndSym, OldEndSym, BO_LT);
780 // There was no "end" symbol assigned yet to the old container,
781 // so reassign all iterator positions to the new container.
782 State = reassignAllIteratorPositions(State, OldContReg, ContReg);
784 if (const auto OldBeginSym = OldCData->getBegin()) {
785 // If we already assigned a "begin" symbol to the old container, then
786 // assign it to the new container and remove it from the old one.
789 setContainerData(State, ContReg, CData->newBegin(OldBeginSym));
791 State = setContainerData(State, ContReg,
792 ContainerData::fromBegin(OldBeginSym));
795 setContainerData(State, OldContReg, OldCData->newEnd(nullptr));
798 // There was neither "begin" nor "end" symbol assigned yet to the old
799 // container, so reassign all iterator positions to the new container.
800 State = reassignAllIteratorPositions(State, OldContReg, ContReg);
804 C.addTransition(State);
807 void IteratorModeling::handleClear(CheckerContext &C, const SVal &Cont) const {
808 const auto *ContReg = Cont.getAsRegion();
812 ContReg = ContReg->getMostDerivedObjectRegion();
814 // The clear() operation invalidates all the iterators, except the past-end
815 // iterators of list-like containers
816 auto State = C.getState();
817 if (!hasSubscriptOperator(State, ContReg) ||
818 !backModifiable(State, ContReg)) {
819 const auto CData = getContainerData(State, ContReg);
821 if (const auto EndSym = CData->getEnd()) {
823 invalidateAllIteratorPositionsExcept(State, ContReg, EndSym, BO_GE);
824 C.addTransition(State);
829 State = invalidateAllIteratorPositions(State, ContReg);
830 C.addTransition(State);
833 void IteratorModeling::handlePushBack(CheckerContext &C,
834 const SVal &Cont) const {
835 const auto *ContReg = Cont.getAsRegion();
839 ContReg = ContReg->getMostDerivedObjectRegion();
841 // For deque-like containers invalidate all iterator positions
842 auto State = C.getState();
843 if (hasSubscriptOperator(State, ContReg) && frontModifiable(State, ContReg)) {
844 State = invalidateAllIteratorPositions(State, ContReg);
845 C.addTransition(State);
849 const auto CData = getContainerData(State, ContReg);
853 // For vector-like containers invalidate the past-end iterator positions
854 if (const auto EndSym = CData->getEnd()) {
855 if (hasSubscriptOperator(State, ContReg)) {
856 State = invalidateIteratorPositions(State, EndSym, BO_GE);
858 auto &SymMgr = C.getSymbolManager();
859 auto &BVF = SymMgr.getBasicVals();
860 auto &SVB = C.getSValBuilder();
861 const auto newEndSym =
862 SVB.evalBinOp(State, BO_Add,
863 nonloc::SymbolVal(EndSym),
864 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
865 SymMgr.getType(EndSym)).getAsSymbol();
866 State = setContainerData(State, ContReg, CData->newEnd(newEndSym));
868 C.addTransition(State);
871 void IteratorModeling::handlePopBack(CheckerContext &C,
872 const SVal &Cont) const {
873 const auto *ContReg = Cont.getAsRegion();
877 ContReg = ContReg->getMostDerivedObjectRegion();
879 auto State = C.getState();
880 const auto CData = getContainerData(State, ContReg);
884 if (const auto EndSym = CData->getEnd()) {
885 auto &SymMgr = C.getSymbolManager();
886 auto &BVF = SymMgr.getBasicVals();
887 auto &SVB = C.getSValBuilder();
889 SVB.evalBinOp(State, BO_Sub,
890 nonloc::SymbolVal(EndSym),
891 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
892 SymMgr.getType(EndSym)).getAsSymbol();
893 // For vector-like and deque-like containers invalidate the last and the
894 // past-end iterator positions. For list-like containers only invalidate
896 if (hasSubscriptOperator(State, ContReg) &&
897 backModifiable(State, ContReg)) {
898 State = invalidateIteratorPositions(State, BackSym, BO_GE);
899 State = setContainerData(State, ContReg, CData->newEnd(nullptr));
901 State = invalidateIteratorPositions(State, BackSym, BO_EQ);
903 auto newEndSym = BackSym;
904 State = setContainerData(State, ContReg, CData->newEnd(newEndSym));
905 C.addTransition(State);
909 void IteratorModeling::handlePushFront(CheckerContext &C,
910 const SVal &Cont) const {
911 const auto *ContReg = Cont.getAsRegion();
915 ContReg = ContReg->getMostDerivedObjectRegion();
917 // For deque-like containers invalidate all iterator positions
918 auto State = C.getState();
919 if (hasSubscriptOperator(State, ContReg)) {
920 State = invalidateAllIteratorPositions(State, ContReg);
921 C.addTransition(State);
923 const auto CData = getContainerData(State, ContReg);
927 if (const auto BeginSym = CData->getBegin()) {
928 auto &SymMgr = C.getSymbolManager();
929 auto &BVF = SymMgr.getBasicVals();
930 auto &SVB = C.getSValBuilder();
931 const auto newBeginSym =
932 SVB.evalBinOp(State, BO_Sub,
933 nonloc::SymbolVal(BeginSym),
934 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
935 SymMgr.getType(BeginSym)).getAsSymbol();
936 State = setContainerData(State, ContReg, CData->newBegin(newBeginSym));
937 C.addTransition(State);
942 void IteratorModeling::handlePopFront(CheckerContext &C,
943 const SVal &Cont) const {
944 const auto *ContReg = Cont.getAsRegion();
948 ContReg = ContReg->getMostDerivedObjectRegion();
950 auto State = C.getState();
951 const auto CData = getContainerData(State, ContReg);
955 // For deque-like containers invalidate all iterator positions. For list-like
956 // iterators only invalidate the first position
957 if (const auto BeginSym = CData->getBegin()) {
958 if (hasSubscriptOperator(State, ContReg)) {
959 State = invalidateIteratorPositions(State, BeginSym, BO_LE);
961 State = invalidateIteratorPositions(State, BeginSym, BO_EQ);
963 auto &SymMgr = C.getSymbolManager();
964 auto &BVF = SymMgr.getBasicVals();
965 auto &SVB = C.getSValBuilder();
966 const auto newBeginSym =
967 SVB.evalBinOp(State, BO_Add,
968 nonloc::SymbolVal(BeginSym),
969 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
970 SymMgr.getType(BeginSym)).getAsSymbol();
971 State = setContainerData(State, ContReg, CData->newBegin(newBeginSym));
972 C.addTransition(State);
976 void IteratorModeling::handleInsert(CheckerContext &C, const SVal &Iter) const {
977 auto State = C.getState();
978 const auto *Pos = getIteratorPosition(State, Iter);
982 // For deque-like containers invalidate all iterator positions. For
983 // vector-like containers invalidate iterator positions after the insertion.
984 const auto *Cont = Pos->getContainer();
985 if (hasSubscriptOperator(State, Cont) && backModifiable(State, Cont)) {
986 if (frontModifiable(State, Cont)) {
987 State = invalidateAllIteratorPositions(State, Cont);
989 State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE);
991 if (const auto *CData = getContainerData(State, Cont)) {
992 if (const auto EndSym = CData->getEnd()) {
993 State = invalidateIteratorPositions(State, EndSym, BO_GE);
994 State = setContainerData(State, Cont, CData->newEnd(nullptr));
997 C.addTransition(State);
1001 void IteratorModeling::handleErase(CheckerContext &C, const SVal &Iter) const {
1002 auto State = C.getState();
1003 const auto *Pos = getIteratorPosition(State, Iter);
1007 // For deque-like containers invalidate all iterator positions. For
1008 // vector-like containers invalidate iterator positions at and after the
1009 // deletion. For list-like containers only invalidate the deleted position.
1010 const auto *Cont = Pos->getContainer();
1011 if (hasSubscriptOperator(State, Cont) && backModifiable(State, Cont)) {
1012 if (frontModifiable(State, Cont)) {
1013 State = invalidateAllIteratorPositions(State, Cont);
1015 State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE);
1017 if (const auto *CData = getContainerData(State, Cont)) {
1018 if (const auto EndSym = CData->getEnd()) {
1019 State = invalidateIteratorPositions(State, EndSym, BO_GE);
1020 State = setContainerData(State, Cont, CData->newEnd(nullptr));
1024 State = invalidateIteratorPositions(State, Pos->getOffset(), BO_EQ);
1026 C.addTransition(State);
1029 void IteratorModeling::handleErase(CheckerContext &C, const SVal &Iter1,
1030 const SVal &Iter2) const {
1031 auto State = C.getState();
1032 const auto *Pos1 = getIteratorPosition(State, Iter1);
1033 const auto *Pos2 = getIteratorPosition(State, Iter2);
1037 // For deque-like containers invalidate all iterator positions. For
1038 // vector-like containers invalidate iterator positions at and after the
1039 // deletion range. For list-like containers only invalidate the deleted
1040 // position range [first..last].
1041 const auto *Cont = Pos1->getContainer();
1042 if (hasSubscriptOperator(State, Cont) && backModifiable(State, Cont)) {
1043 if (frontModifiable(State, Cont)) {
1044 State = invalidateAllIteratorPositions(State, Cont);
1046 State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE);
1048 if (const auto *CData = getContainerData(State, Cont)) {
1049 if (const auto EndSym = CData->getEnd()) {
1050 State = invalidateIteratorPositions(State, EndSym, BO_GE);
1051 State = setContainerData(State, Cont, CData->newEnd(nullptr));
1055 State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE,
1056 Pos2->getOffset(), BO_LT);
1058 C.addTransition(State);
1061 void IteratorModeling::handleEraseAfter(CheckerContext &C,
1062 const SVal &Iter) const {
1063 auto State = C.getState();
1064 const auto *Pos = getIteratorPosition(State, Iter);
1068 // Invalidate the deleted iterator position, which is the position of the
1069 // parameter plus one.
1070 auto &SymMgr = C.getSymbolManager();
1071 auto &BVF = SymMgr.getBasicVals();
1072 auto &SVB = C.getSValBuilder();
1073 const auto NextSym =
1074 SVB.evalBinOp(State, BO_Add,
1075 nonloc::SymbolVal(Pos->getOffset()),
1076 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
1077 SymMgr.getType(Pos->getOffset())).getAsSymbol();
1078 State = invalidateIteratorPositions(State, NextSym, BO_EQ);
1079 C.addTransition(State);
1082 void IteratorModeling::handleEraseAfter(CheckerContext &C, const SVal &Iter1,
1083 const SVal &Iter2) const {
1084 auto State = C.getState();
1085 const auto *Pos1 = getIteratorPosition(State, Iter1);
1086 const auto *Pos2 = getIteratorPosition(State, Iter2);
1090 // Invalidate the deleted iterator position range (first..last)
1091 State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GT,
1092 Pos2->getOffset(), BO_LT);
1093 C.addTransition(State);
1096 void IteratorModeling::printState(raw_ostream &Out, ProgramStateRef State,
1097 const char *NL, const char *Sep) const {
1099 auto ContMap = State->get<ContainerMap>();
1101 if (!ContMap.isEmpty()) {
1102 Out << Sep << "Container Data :" << NL;
1103 for (const auto &Cont : ContMap) {
1104 Cont.first->dumpToStream(Out);
1106 const auto CData = Cont.second;
1107 if (CData.getBegin())
1108 CData.getBegin()->dumpToStream(Out);
1113 CData.getEnd()->dumpToStream(Out);
1120 auto SymbolMap = State->get<IteratorSymbolMap>();
1121 auto RegionMap = State->get<IteratorRegionMap>();
1123 if (!SymbolMap.isEmpty() || !RegionMap.isEmpty()) {
1124 Out << Sep << "Iterator Positions :" << NL;
1125 for (const auto &Sym : SymbolMap) {
1126 Sym.first->dumpToStream(Out);
1128 const auto Pos = Sym.second;
1129 Out << (Pos.isValid() ? "Valid" : "Invalid") << " ; Container == ";
1130 Pos.getContainer()->dumpToStream(Out);
1131 Out<<" ; Offset == ";
1132 Pos.getOffset()->dumpToStream(Out);
1135 for (const auto &Reg : RegionMap) {
1136 Reg.first->dumpToStream(Out);
1138 const auto Pos = Reg.second;
1139 Out << (Pos.isValid() ? "Valid" : "Invalid") << " ; Container == ";
1140 Pos.getContainer()->dumpToStream(Out);
1141 Out<<" ; Offset == ";
1142 Pos.getOffset()->dumpToStream(Out);
1150 const CXXRecordDecl *getCXXRecordDecl(ProgramStateRef State,
1151 const MemRegion *Reg);
1153 bool isBeginCall(const FunctionDecl *Func) {
1154 const auto *IdInfo = Func->getIdentifier();
1157 return IdInfo->getName().endswith_lower("begin");
1160 bool isEndCall(const FunctionDecl *Func) {
1161 const auto *IdInfo = Func->getIdentifier();
1164 return IdInfo->getName().endswith_lower("end");
1167 bool isAssignCall(const FunctionDecl *Func) {
1168 const auto *IdInfo = Func->getIdentifier();
1171 if (Func->getNumParams() > 2)
1173 return IdInfo->getName() == "assign";
1176 bool isClearCall(const FunctionDecl *Func) {
1177 const auto *IdInfo = Func->getIdentifier();
1180 if (Func->getNumParams() > 0)
1182 return IdInfo->getName() == "clear";
1185 bool isPushBackCall(const FunctionDecl *Func) {
1186 const auto *IdInfo = Func->getIdentifier();
1189 if (Func->getNumParams() != 1)
1191 return IdInfo->getName() == "push_back";
1194 bool isEmplaceBackCall(const FunctionDecl *Func) {
1195 const auto *IdInfo = Func->getIdentifier();
1198 if (Func->getNumParams() < 1)
1200 return IdInfo->getName() == "emplace_back";
1203 bool isPopBackCall(const FunctionDecl *Func) {
1204 const auto *IdInfo = Func->getIdentifier();
1207 if (Func->getNumParams() > 0)
1209 return IdInfo->getName() == "pop_back";
1212 bool isPushFrontCall(const FunctionDecl *Func) {
1213 const auto *IdInfo = Func->getIdentifier();
1216 if (Func->getNumParams() != 1)
1218 return IdInfo->getName() == "push_front";
1221 bool isEmplaceFrontCall(const FunctionDecl *Func) {
1222 const auto *IdInfo = Func->getIdentifier();
1225 if (Func->getNumParams() < 1)
1227 return IdInfo->getName() == "emplace_front";
1230 bool isPopFrontCall(const FunctionDecl *Func) {
1231 const auto *IdInfo = Func->getIdentifier();
1234 if (Func->getNumParams() > 0)
1236 return IdInfo->getName() == "pop_front";
1239 bool isAssignmentOperator(OverloadedOperatorKind OK) { return OK == OO_Equal; }
1241 bool isSimpleComparisonOperator(OverloadedOperatorKind OK) {
1242 return OK == OO_EqualEqual || OK == OO_ExclaimEqual;
1245 bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg) {
1246 const auto *CRD = getCXXRecordDecl(State, Reg);
1250 for (const auto *Method : CRD->methods()) {
1251 if (!Method->isOverloadedOperator())
1253 const auto OPK = Method->getOverloadedOperator();
1254 if (OPK == OO_Subscript) {
1261 bool frontModifiable(ProgramStateRef State, const MemRegion *Reg) {
1262 const auto *CRD = getCXXRecordDecl(State, Reg);
1266 for (const auto *Method : CRD->methods()) {
1267 if (!Method->getDeclName().isIdentifier())
1269 if (Method->getName() == "push_front" || Method->getName() == "pop_front") {
1276 bool backModifiable(ProgramStateRef State, const MemRegion *Reg) {
1277 const auto *CRD = getCXXRecordDecl(State, Reg);
1281 for (const auto *Method : CRD->methods()) {
1282 if (!Method->getDeclName().isIdentifier())
1284 if (Method->getName() == "push_back" || Method->getName() == "pop_back") {
1291 const CXXRecordDecl *getCXXRecordDecl(ProgramStateRef State,
1292 const MemRegion *Reg) {
1293 auto TI = getDynamicTypeInfo(State, Reg);
1297 auto Type = TI.getType();
1298 if (const auto *RefT = Type->getAs<ReferenceType>()) {
1299 Type = RefT->getPointeeType();
1302 return Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
1305 SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont) {
1306 const auto *CDataPtr = getContainerData(State, Cont);
1310 return CDataPtr->getBegin();
1313 SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont) {
1314 const auto *CDataPtr = getContainerData(State, Cont);
1318 return CDataPtr->getEnd();
1321 ProgramStateRef createContainerBegin(ProgramStateRef State,
1322 const MemRegion *Cont, const Expr *E,
1323 QualType T, const LocationContext *LCtx,
1324 unsigned BlockCount) {
1325 // Only create if it does not exist
1326 const auto *CDataPtr = getContainerData(State, Cont);
1327 if (CDataPtr && CDataPtr->getBegin())
1330 auto &SymMgr = State->getSymbolManager();
1331 const SymbolConjured *Sym = SymMgr.conjureSymbol(E, LCtx, T, BlockCount,
1333 State = assumeNoOverflow(State, Sym, 4);
1336 const auto CData = CDataPtr->newBegin(Sym);
1337 return setContainerData(State, Cont, CData);
1340 const auto CData = ContainerData::fromBegin(Sym);
1341 return setContainerData(State, Cont, CData);
1344 ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
1345 const Expr *E, QualType T,
1346 const LocationContext *LCtx,
1347 unsigned BlockCount) {
1348 // Only create if it does not exist
1349 const auto *CDataPtr = getContainerData(State, Cont);
1350 if (CDataPtr && CDataPtr->getEnd())
1353 auto &SymMgr = State->getSymbolManager();
1354 const SymbolConjured *Sym = SymMgr.conjureSymbol(E, LCtx, T, BlockCount,
1356 State = assumeNoOverflow(State, Sym, 4);
1359 const auto CData = CDataPtr->newEnd(Sym);
1360 return setContainerData(State, Cont, CData);
1363 const auto CData = ContainerData::fromEnd(Sym);
1364 return setContainerData(State, Cont, CData);
1367 ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
1368 const ContainerData &CData) {
1369 return State->set<ContainerMap>(Cont, CData);
1372 ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val) {
1373 if (auto Reg = Val.getAsRegion()) {
1374 Reg = Reg->getMostDerivedObjectRegion();
1375 return State->remove<IteratorRegionMap>(Reg);
1376 } else if (const auto Sym = Val.getAsSymbol()) {
1377 return State->remove<IteratorSymbolMap>(Sym);
1378 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
1379 return State->remove<IteratorRegionMap>(LCVal->getRegion());
1384 // This function tells the analyzer's engine that symbols produced by our
1385 // checker, most notably iterator positions, are relatively small.
1386 // A distance between items in the container should not be very large.
1387 // By assuming that it is within around 1/8 of the address space,
1388 // we can help the analyzer perform operations on these symbols
1389 // without being afraid of integer overflows.
1390 // FIXME: Should we provide it as an API, so that all checkers could use it?
1391 ProgramStateRef assumeNoOverflow(ProgramStateRef State, SymbolRef Sym,
1393 SValBuilder &SVB = State->getStateManager().getSValBuilder();
1394 BasicValueFactory &BV = SVB.getBasicValueFactory();
1396 QualType T = Sym->getType();
1397 assert(T->isSignedIntegerOrEnumerationType());
1398 APSIntType AT = BV.getAPSIntType(T);
1400 ProgramStateRef NewState = State;
1402 llvm::APSInt Max = AT.getMaxValue() / AT.getValue(Scale);
1403 SVal IsCappedFromAbove =
1404 SVB.evalBinOpNN(State, BO_LE, nonloc::SymbolVal(Sym),
1405 nonloc::ConcreteInt(Max), SVB.getConditionType());
1406 if (auto DV = IsCappedFromAbove.getAs<DefinedSVal>()) {
1407 NewState = NewState->assume(*DV, true);
1412 llvm::APSInt Min = -Max;
1413 SVal IsCappedFromBelow =
1414 SVB.evalBinOpNN(State, BO_GE, nonloc::SymbolVal(Sym),
1415 nonloc::ConcreteInt(Min), SVB.getConditionType());
1416 if (auto DV = IsCappedFromBelow.getAs<DefinedSVal>()) {
1417 NewState = NewState->assume(*DV, true);
1425 ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1,
1426 SymbolRef Sym2, bool Equal) {
1427 auto &SVB = State->getStateManager().getSValBuilder();
1429 // FIXME: This code should be reworked as follows:
1430 // 1. Subtract the operands using evalBinOp().
1431 // 2. Assume that the result doesn't overflow.
1432 // 3. Compare the result to 0.
1433 // 4. Assume the result of the comparison.
1434 const auto comparison =
1435 SVB.evalBinOp(State, BO_EQ, nonloc::SymbolVal(Sym1),
1436 nonloc::SymbolVal(Sym2), SVB.getConditionType());
1438 assert(comparison.getAs<DefinedSVal>() &&
1439 "Symbol comparison must be a `DefinedSVal`");
1441 auto NewState = State->assume(comparison.castAs<DefinedSVal>(), Equal);
1445 if (const auto CompSym = comparison.getAsSymbol()) {
1446 assert(isa<SymIntExpr>(CompSym) &&
1447 "Symbol comparison must be a `SymIntExpr`");
1448 assert(BinaryOperator::isComparisonOp(
1449 cast<SymIntExpr>(CompSym)->getOpcode()) &&
1450 "Symbol comparison must be a comparison");
1451 return assumeNoOverflow(NewState, cast<SymIntExpr>(CompSym)->getLHS(), 2);
1457 bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont) {
1458 auto RegionMap = State->get<IteratorRegionMap>();
1459 for (const auto &Reg : RegionMap) {
1460 if (Reg.second.getContainer() == Cont)
1464 auto SymbolMap = State->get<IteratorSymbolMap>();
1465 for (const auto &Sym : SymbolMap) {
1466 if (Sym.second.getContainer() == Cont)
1473 bool isBoundThroughLazyCompoundVal(const Environment &Env,
1474 const MemRegion *Reg) {
1475 for (const auto &Binding : Env) {
1476 if (const auto LCVal = Binding.second.getAs<nonloc::LazyCompoundVal>()) {
1477 if (LCVal->getRegion() == Reg)
1485 template <typename Condition, typename Process>
1486 ProgramStateRef processIteratorPositions(ProgramStateRef State, Condition Cond,
1488 auto &RegionMapFactory = State->get_context<IteratorRegionMap>();
1489 auto RegionMap = State->get<IteratorRegionMap>();
1490 bool Changed = false;
1491 for (const auto &Reg : RegionMap) {
1492 if (Cond(Reg.second)) {
1493 RegionMap = RegionMapFactory.add(RegionMap, Reg.first, Proc(Reg.second));
1499 State = State->set<IteratorRegionMap>(RegionMap);
1501 auto &SymbolMapFactory = State->get_context<IteratorSymbolMap>();
1502 auto SymbolMap = State->get<IteratorSymbolMap>();
1504 for (const auto &Sym : SymbolMap) {
1505 if (Cond(Sym.second)) {
1506 SymbolMap = SymbolMapFactory.add(SymbolMap, Sym.first, Proc(Sym.second));
1512 State = State->set<IteratorSymbolMap>(SymbolMap);
1517 ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State,
1518 const MemRegion *Cont) {
1519 auto MatchCont = [&](const IteratorPosition &Pos) {
1520 return Pos.getContainer() == Cont;
1522 auto Invalidate = [&](const IteratorPosition &Pos) {
1523 return Pos.invalidate();
1525 return processIteratorPositions(State, MatchCont, Invalidate);
1529 invalidateAllIteratorPositionsExcept(ProgramStateRef State,
1530 const MemRegion *Cont, SymbolRef Offset,
1531 BinaryOperator::Opcode Opc) {
1532 auto MatchContAndCompare = [&](const IteratorPosition &Pos) {
1533 return Pos.getContainer() == Cont &&
1534 !compare(State, Pos.getOffset(), Offset, Opc);
1536 auto Invalidate = [&](const IteratorPosition &Pos) {
1537 return Pos.invalidate();
1539 return processIteratorPositions(State, MatchContAndCompare, Invalidate);
1542 ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
1544 BinaryOperator::Opcode Opc) {
1545 auto Compare = [&](const IteratorPosition &Pos) {
1546 return compare(State, Pos.getOffset(), Offset, Opc);
1548 auto Invalidate = [&](const IteratorPosition &Pos) {
1549 return Pos.invalidate();
1551 return processIteratorPositions(State, Compare, Invalidate);
1554 ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
1556 BinaryOperator::Opcode Opc1,
1558 BinaryOperator::Opcode Opc2) {
1559 auto Compare = [&](const IteratorPosition &Pos) {
1560 return compare(State, Pos.getOffset(), Offset1, Opc1) &&
1561 compare(State, Pos.getOffset(), Offset2, Opc2);
1563 auto Invalidate = [&](const IteratorPosition &Pos) {
1564 return Pos.invalidate();
1566 return processIteratorPositions(State, Compare, Invalidate);
1569 ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State,
1570 const MemRegion *Cont,
1571 const MemRegion *NewCont) {
1572 auto MatchCont = [&](const IteratorPosition &Pos) {
1573 return Pos.getContainer() == Cont;
1575 auto ReAssign = [&](const IteratorPosition &Pos) {
1576 return Pos.reAssign(NewCont);
1578 return processIteratorPositions(State, MatchCont, ReAssign);
1581 ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State,
1582 const MemRegion *Cont,
1583 const MemRegion *NewCont,
1585 BinaryOperator::Opcode Opc) {
1586 auto MatchContAndCompare = [&](const IteratorPosition &Pos) {
1587 return Pos.getContainer() == Cont &&
1588 !compare(State, Pos.getOffset(), Offset, Opc);
1590 auto ReAssign = [&](const IteratorPosition &Pos) {
1591 return Pos.reAssign(NewCont);
1593 return processIteratorPositions(State, MatchContAndCompare, ReAssign);
1596 // This function rebases symbolic expression `OldSym + Int` to `NewSym + Int`,
1597 // `OldSym - Int` to `NewSym - Int` and `OldSym` to `NewSym` in any iterator
1598 // position offsets where `CondSym` is true.
1599 ProgramStateRef rebaseSymbolInIteratorPositionsIf(
1600 ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym,
1601 SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc) {
1602 auto LessThanEnd = [&](const IteratorPosition &Pos) {
1603 return compare(State, Pos.getOffset(), CondSym, Opc);
1605 auto RebaseSymbol = [&](const IteratorPosition &Pos) {
1606 return Pos.setTo(rebaseSymbol(State, SVB, Pos.getOffset(), OldSym,
1609 return processIteratorPositions(State, LessThanEnd, RebaseSymbol);
1612 // This function rebases symbolic expression `OldExpr + Int` to `NewExpr + Int`,
1613 // `OldExpr - Int` to `NewExpr - Int` and `OldExpr` to `NewExpr` in expression
1615 SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB,
1616 SymbolRef OrigExpr, SymbolRef OldExpr,
1618 auto &SymMgr = SVB.getSymbolManager();
1619 auto Diff = SVB.evalBinOpNN(State, BO_Sub, nonloc::SymbolVal(OrigExpr),
1620 nonloc::SymbolVal(OldExpr),
1621 SymMgr.getType(OrigExpr));
1623 const auto DiffInt = Diff.getAs<nonloc::ConcreteInt>();
1627 return SVB.evalBinOpNN(State, BO_Add, *DiffInt, nonloc::SymbolVal(NewSym),
1628 SymMgr.getType(OrigExpr)).getAsSymbol();
1633 void ento::registerIteratorModeling(CheckerManager &mgr) {
1634 mgr.registerChecker<IteratorModeling>();
1637 bool ento::shouldRegisterIteratorModeling(const LangOptions &LO) {