1480093f4SDimitry Andric //===-- IteratorModeling.cpp --------------------------------------*- C++ -*--// 2480093f4SDimitry Andric // 3480093f4SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4480093f4SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5480093f4SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6480093f4SDimitry Andric // 7480093f4SDimitry Andric //===----------------------------------------------------------------------===// 8480093f4SDimitry Andric // 95ffd83dbSDimitry Andric // Defines a modeling-checker for modeling STL iterator-like iterators. 10480093f4SDimitry Andric // 11480093f4SDimitry Andric //===----------------------------------------------------------------------===// 12480093f4SDimitry Andric // 13480093f4SDimitry Andric // In the code, iterator can be represented as a: 14480093f4SDimitry Andric // * type-I: typedef-ed pointer. Operations over such iterator, such as 15480093f4SDimitry Andric // comparisons or increments, are modeled straightforwardly by the 16480093f4SDimitry Andric // analyzer. 17480093f4SDimitry Andric // * type-II: structure with its method bodies available. Operations over such 18480093f4SDimitry Andric // iterator are inlined by the analyzer, and results of modeling 19480093f4SDimitry Andric // these operations are exposing implementation details of the 20480093f4SDimitry Andric // iterators, which is not necessarily helping. 21480093f4SDimitry Andric // * type-III: completely opaque structure. Operations over such iterator are 22480093f4SDimitry Andric // modeled conservatively, producing conjured symbols everywhere. 23480093f4SDimitry Andric // 24480093f4SDimitry Andric // To handle all these types in a common way we introduce a structure called 25480093f4SDimitry Andric // IteratorPosition which is an abstraction of the position the iterator 26480093f4SDimitry Andric // represents using symbolic expressions. The checker handles all the 27480093f4SDimitry Andric // operations on this structure. 28480093f4SDimitry Andric // 29480093f4SDimitry Andric // Additionally, depending on the circumstances, operators of types II and III 30480093f4SDimitry Andric // can be represented as: 31480093f4SDimitry Andric // * type-IIa, type-IIIa: conjured structure symbols - when returned by value 32480093f4SDimitry Andric // from conservatively evaluated methods such as 33480093f4SDimitry Andric // `.begin()`. 34480093f4SDimitry Andric // * type-IIb, type-IIIb: memory regions of iterator-typed objects, such as 35480093f4SDimitry Andric // variables or temporaries, when the iterator object is 36480093f4SDimitry Andric // currently treated as an lvalue. 37480093f4SDimitry Andric // * type-IIc, type-IIIc: compound values of iterator-typed objects, when the 38480093f4SDimitry Andric // iterator object is treated as an rvalue taken of a 39480093f4SDimitry Andric // particular lvalue, eg. a copy of "type-a" iterator 40480093f4SDimitry Andric // object, or an iterator that existed before the 41480093f4SDimitry Andric // analysis has started. 42480093f4SDimitry Andric // 43480093f4SDimitry Andric // To handle any of these three different representations stored in an SVal we 44480093f4SDimitry Andric // use setter and getters functions which separate the three cases. To store 45480093f4SDimitry Andric // them we use a pointer union of symbol and memory region. 46480093f4SDimitry Andric // 47480093f4SDimitry Andric // The checker works the following way: We record the begin and the 48480093f4SDimitry Andric // past-end iterator for all containers whenever their `.begin()` and `.end()` 49480093f4SDimitry Andric // are called. Since the Constraint Manager cannot handle such SVals we need 50480093f4SDimitry Andric // to take over its role. We post-check equality and non-equality comparisons 51480093f4SDimitry Andric // and record that the two sides are equal if we are in the 'equal' branch 52480093f4SDimitry Andric // (true-branch for `==` and false-branch for `!=`). 53480093f4SDimitry Andric // 54480093f4SDimitry Andric // In case of type-I or type-II iterators we get a concrete integer as a result 55480093f4SDimitry Andric // of the comparison (1 or 0) but in case of type-III we only get a Symbol. In 56480093f4SDimitry Andric // this latter case we record the symbol and reload it in evalAssume() and do 57480093f4SDimitry Andric // the propagation there. We also handle (maybe double) negated comparisons 58480093f4SDimitry Andric // which are represented in the form of (x == 0 or x != 0) where x is the 59480093f4SDimitry Andric // comparison itself. 60480093f4SDimitry Andric // 61480093f4SDimitry Andric // Since `SimpleConstraintManager` cannot handle complex symbolic expressions 62480093f4SDimitry Andric // we only use expressions of the format S, S+n or S-n for iterator positions 63480093f4SDimitry Andric // where S is a conjured symbol and n is an unsigned concrete integer. When 64480093f4SDimitry Andric // making an assumption e.g. `S1 + n == S2 + m` we store `S1 - S2 == m - n` as 65480093f4SDimitry Andric // a constraint which we later retrieve when doing an actual comparison. 66480093f4SDimitry Andric 67480093f4SDimitry Andric #include "clang/AST/DeclTemplate.h" 68349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 69480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 70480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h" 71349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" 72480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 73480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 74480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" 7506c3fb27SDimitry Andric #include "llvm/ADT/STLExtras.h" 76480093f4SDimitry Andric 77480093f4SDimitry Andric #include "Iterator.h" 78480093f4SDimitry Andric 79480093f4SDimitry Andric #include <utility> 80480093f4SDimitry Andric 81480093f4SDimitry Andric using namespace clang; 82480093f4SDimitry Andric using namespace ento; 83480093f4SDimitry Andric using namespace iterator; 84480093f4SDimitry Andric 85480093f4SDimitry Andric namespace { 86480093f4SDimitry Andric 87480093f4SDimitry Andric class IteratorModeling 885ffd83dbSDimitry Andric : public Checker<check::PostCall, check::PostStmt<UnaryOperator>, 895ffd83dbSDimitry Andric check::PostStmt<BinaryOperator>, 905ffd83dbSDimitry Andric check::PostStmt<MaterializeTemporaryExpr>, 91480093f4SDimitry Andric check::Bind, check::LiveSymbols, check::DeadSymbols> { 92480093f4SDimitry Andric 935ffd83dbSDimitry Andric using AdvanceFn = void (IteratorModeling::*)(CheckerContext &, const Expr *, 945ffd83dbSDimitry Andric SVal, SVal, SVal) const; 955ffd83dbSDimitry Andric 965ffd83dbSDimitry Andric void handleOverloadedOperator(CheckerContext &C, const CallEvent &Call, 975ffd83dbSDimitry Andric OverloadedOperatorKind Op) const; 985ffd83dbSDimitry Andric void handleAdvanceLikeFunction(CheckerContext &C, const CallEvent &Call, 995ffd83dbSDimitry Andric const Expr *OrigExpr, 1005ffd83dbSDimitry Andric const AdvanceFn *Handler) const; 1015ffd83dbSDimitry Andric 102480093f4SDimitry Andric void handleComparison(CheckerContext &C, const Expr *CE, SVal RetVal, 103647cbc5dSDimitry Andric SVal LVal, SVal RVal, OverloadedOperatorKind Op) const; 104480093f4SDimitry Andric void processComparison(CheckerContext &C, ProgramStateRef State, 105647cbc5dSDimitry Andric SymbolRef Sym1, SymbolRef Sym2, SVal RetVal, 106480093f4SDimitry Andric OverloadedOperatorKind Op) const; 107647cbc5dSDimitry Andric void handleIncrement(CheckerContext &C, SVal RetVal, SVal Iter, 108480093f4SDimitry Andric bool Postfix) const; 109647cbc5dSDimitry Andric void handleDecrement(CheckerContext &C, SVal RetVal, SVal Iter, 110480093f4SDimitry Andric bool Postfix) const; 111480093f4SDimitry Andric void handleRandomIncrOrDecr(CheckerContext &C, const Expr *CE, 112647cbc5dSDimitry Andric OverloadedOperatorKind Op, SVal RetVal, 113647cbc5dSDimitry Andric SVal Iterator, SVal Amount) const; 1145ffd83dbSDimitry Andric void handlePtrIncrOrDecr(CheckerContext &C, const Expr *Iterator, 1155ffd83dbSDimitry Andric OverloadedOperatorKind OK, SVal Offset) const; 1165ffd83dbSDimitry Andric void handleAdvance(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter, 1175ffd83dbSDimitry Andric SVal Amount) const; 1185ffd83dbSDimitry Andric void handlePrev(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter, 1195ffd83dbSDimitry Andric SVal Amount) const; 1205ffd83dbSDimitry Andric void handleNext(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter, 1215ffd83dbSDimitry Andric SVal Amount) const; 122647cbc5dSDimitry Andric void assignToContainer(CheckerContext &C, const Expr *CE, SVal RetVal, 123480093f4SDimitry Andric const MemRegion *Cont) const; 1245ffd83dbSDimitry Andric bool noChangeInAdvance(CheckerContext &C, SVal Iter, const Expr *CE) const; 125480093f4SDimitry Andric void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, 126480093f4SDimitry Andric const char *Sep) const override; 127480093f4SDimitry Andric 1285ffd83dbSDimitry Andric // std::advance, std::prev & std::next 1295ffd83dbSDimitry Andric CallDescriptionMap<AdvanceFn> AdvanceLikeFunctions = { 1305ffd83dbSDimitry Andric // template<class InputIt, class Distance> 1315ffd83dbSDimitry Andric // void advance(InputIt& it, Distance n); 132*0fca6ea1SDimitry Andric {{CDM::SimpleFunc, {"std", "advance"}, 2}, 133*0fca6ea1SDimitry Andric &IteratorModeling::handleAdvance}, 1345ffd83dbSDimitry Andric 1355ffd83dbSDimitry Andric // template<class BidirIt> 1365ffd83dbSDimitry Andric // BidirIt prev( 1375ffd83dbSDimitry Andric // BidirIt it, 1385ffd83dbSDimitry Andric // typename std::iterator_traits<BidirIt>::difference_type n = 1); 139*0fca6ea1SDimitry Andric {{CDM::SimpleFunc, {"std", "prev"}, 2}, &IteratorModeling::handlePrev}, 1405ffd83dbSDimitry Andric 1415ffd83dbSDimitry Andric // template<class ForwardIt> 1425ffd83dbSDimitry Andric // ForwardIt next( 1435ffd83dbSDimitry Andric // ForwardIt it, 1445ffd83dbSDimitry Andric // typename std::iterator_traits<ForwardIt>::difference_type n = 1); 145*0fca6ea1SDimitry Andric {{CDM::SimpleFunc, {"std", "next"}, 2}, &IteratorModeling::handleNext}, 1465ffd83dbSDimitry Andric }; 1475ffd83dbSDimitry Andric 148480093f4SDimitry Andric public: 1495ffd83dbSDimitry Andric IteratorModeling() = default; 150480093f4SDimitry Andric 151480093f4SDimitry Andric void checkPostCall(const CallEvent &Call, CheckerContext &C) const; 152480093f4SDimitry Andric void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const; 1535ffd83dbSDimitry Andric void checkPostStmt(const UnaryOperator *UO, CheckerContext &C) const; 1545ffd83dbSDimitry Andric void checkPostStmt(const BinaryOperator *BO, CheckerContext &C) const; 155480093f4SDimitry Andric void checkPostStmt(const MaterializeTemporaryExpr *MTE, 156480093f4SDimitry Andric CheckerContext &C) const; 157480093f4SDimitry Andric void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const; 158480093f4SDimitry Andric void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; 159480093f4SDimitry Andric }; 160480093f4SDimitry Andric 161480093f4SDimitry Andric bool isSimpleComparisonOperator(OverloadedOperatorKind OK); 1625ffd83dbSDimitry Andric bool isSimpleComparisonOperator(BinaryOperatorKind OK); 163647cbc5dSDimitry Andric ProgramStateRef removeIteratorPosition(ProgramStateRef State, SVal Val); 164480093f4SDimitry Andric ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1, 165480093f4SDimitry Andric SymbolRef Sym2, bool Equal); 166480093f4SDimitry Andric bool isBoundThroughLazyCompoundVal(const Environment &Env, 167480093f4SDimitry Andric const MemRegion *Reg); 1685ffd83dbSDimitry Andric const ExplodedNode *findCallEnter(const ExplodedNode *Node, const Expr *Call); 169480093f4SDimitry Andric 170480093f4SDimitry Andric } // namespace 171480093f4SDimitry Andric 172480093f4SDimitry Andric void IteratorModeling::checkPostCall(const CallEvent &Call, 173480093f4SDimitry Andric CheckerContext &C) const { 174480093f4SDimitry Andric // Record new iterator positions and iterator position changes 175480093f4SDimitry Andric const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); 176480093f4SDimitry Andric if (!Func) 177480093f4SDimitry Andric return; 178480093f4SDimitry Andric 179480093f4SDimitry Andric if (Func->isOverloadedOperator()) { 180480093f4SDimitry Andric const auto Op = Func->getOverloadedOperator(); 1815ffd83dbSDimitry Andric handleOverloadedOperator(C, Call, Op); 182480093f4SDimitry Andric return; 183480093f4SDimitry Andric } 184480093f4SDimitry Andric 185480093f4SDimitry Andric const auto *OrigExpr = Call.getOriginExpr(); 186480093f4SDimitry Andric if (!OrigExpr) 187480093f4SDimitry Andric return; 188480093f4SDimitry Andric 1895ffd83dbSDimitry Andric const AdvanceFn *Handler = AdvanceLikeFunctions.lookup(Call); 1905ffd83dbSDimitry Andric if (Handler) { 1915ffd83dbSDimitry Andric handleAdvanceLikeFunction(C, Call, OrigExpr, Handler); 1925ffd83dbSDimitry Andric return; 1935ffd83dbSDimitry Andric } 1945ffd83dbSDimitry Andric 195480093f4SDimitry Andric if (!isIteratorType(Call.getResultType())) 196480093f4SDimitry Andric return; 197480093f4SDimitry Andric 198480093f4SDimitry Andric auto State = C.getState(); 199480093f4SDimitry Andric 200480093f4SDimitry Andric // Already bound to container? 201480093f4SDimitry Andric if (getIteratorPosition(State, Call.getReturnValue())) 202480093f4SDimitry Andric return; 203480093f4SDimitry Andric 204480093f4SDimitry Andric // Copy-like and move constructors 205480093f4SDimitry Andric if (isa<CXXConstructorCall>(&Call) && Call.getNumArgs() == 1) { 206480093f4SDimitry Andric if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(0))) { 207480093f4SDimitry Andric State = setIteratorPosition(State, Call.getReturnValue(), *Pos); 208480093f4SDimitry Andric if (cast<CXXConstructorDecl>(Func)->isMoveConstructor()) { 209480093f4SDimitry Andric State = removeIteratorPosition(State, Call.getArgSVal(0)); 210480093f4SDimitry Andric } 211480093f4SDimitry Andric C.addTransition(State); 212480093f4SDimitry Andric return; 213480093f4SDimitry Andric } 214480093f4SDimitry Andric } 215480093f4SDimitry Andric 216480093f4SDimitry Andric // Assumption: if return value is an iterator which is not yet bound to a 2175ffd83dbSDimitry Andric // container, then look for the first iterator argument of the 2185ffd83dbSDimitry Andric // same type as the return value and bind the return value to 2195ffd83dbSDimitry Andric // the same container. This approach works for STL algorithms. 220480093f4SDimitry Andric // FIXME: Add a more conservative mode 221480093f4SDimitry Andric for (unsigned i = 0; i < Call.getNumArgs(); ++i) { 2225ffd83dbSDimitry Andric if (isIteratorType(Call.getArgExpr(i)->getType()) && 2235ffd83dbSDimitry Andric Call.getArgExpr(i)->getType().getNonReferenceType().getDesugaredType( 2245ffd83dbSDimitry Andric C.getASTContext()).getTypePtr() == 2255ffd83dbSDimitry Andric Call.getResultType().getDesugaredType(C.getASTContext()).getTypePtr()) { 226480093f4SDimitry Andric if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(i))) { 227480093f4SDimitry Andric assignToContainer(C, OrigExpr, Call.getReturnValue(), 228480093f4SDimitry Andric Pos->getContainer()); 229480093f4SDimitry Andric return; 230480093f4SDimitry Andric } 231480093f4SDimitry Andric } 232480093f4SDimitry Andric } 233480093f4SDimitry Andric } 234480093f4SDimitry Andric 235480093f4SDimitry Andric void IteratorModeling::checkBind(SVal Loc, SVal Val, const Stmt *S, 236480093f4SDimitry Andric CheckerContext &C) const { 237480093f4SDimitry Andric auto State = C.getState(); 238480093f4SDimitry Andric const auto *Pos = getIteratorPosition(State, Val); 239480093f4SDimitry Andric if (Pos) { 240480093f4SDimitry Andric State = setIteratorPosition(State, Loc, *Pos); 241480093f4SDimitry Andric C.addTransition(State); 242480093f4SDimitry Andric } else { 243480093f4SDimitry Andric const auto *OldPos = getIteratorPosition(State, Loc); 244480093f4SDimitry Andric if (OldPos) { 245480093f4SDimitry Andric State = removeIteratorPosition(State, Loc); 246480093f4SDimitry Andric C.addTransition(State); 247480093f4SDimitry Andric } 248480093f4SDimitry Andric } 249480093f4SDimitry Andric } 250480093f4SDimitry Andric 2515ffd83dbSDimitry Andric void IteratorModeling::checkPostStmt(const UnaryOperator *UO, 2525ffd83dbSDimitry Andric CheckerContext &C) const { 2535ffd83dbSDimitry Andric UnaryOperatorKind OK = UO->getOpcode(); 2545ffd83dbSDimitry Andric if (!isIncrementOperator(OK) && !isDecrementOperator(OK)) 2555ffd83dbSDimitry Andric return; 2565ffd83dbSDimitry Andric 2575ffd83dbSDimitry Andric auto &SVB = C.getSValBuilder(); 2585ffd83dbSDimitry Andric handlePtrIncrOrDecr(C, UO->getSubExpr(), 2595ffd83dbSDimitry Andric isIncrementOperator(OK) ? OO_Plus : OO_Minus, 2605ffd83dbSDimitry Andric SVB.makeArrayIndex(1)); 2615ffd83dbSDimitry Andric } 2625ffd83dbSDimitry Andric 2635ffd83dbSDimitry Andric void IteratorModeling::checkPostStmt(const BinaryOperator *BO, 2645ffd83dbSDimitry Andric CheckerContext &C) const { 265e8d8bef9SDimitry Andric const ProgramStateRef State = C.getState(); 266e8d8bef9SDimitry Andric const BinaryOperatorKind OK = BO->getOpcode(); 267e8d8bef9SDimitry Andric const Expr *const LHS = BO->getLHS(); 268e8d8bef9SDimitry Andric const Expr *const RHS = BO->getRHS(); 269e8d8bef9SDimitry Andric const SVal LVal = State->getSVal(LHS, C.getLocationContext()); 270e8d8bef9SDimitry Andric const SVal RVal = State->getSVal(RHS, C.getLocationContext()); 2715ffd83dbSDimitry Andric 2725ffd83dbSDimitry Andric if (isSimpleComparisonOperator(BO->getOpcode())) { 2735ffd83dbSDimitry Andric SVal Result = State->getSVal(BO, C.getLocationContext()); 2745ffd83dbSDimitry Andric handleComparison(C, BO, Result, LVal, RVal, 2755ffd83dbSDimitry Andric BinaryOperator::getOverloadedOperator(OK)); 2765ffd83dbSDimitry Andric } else if (isRandomIncrOrDecrOperator(OK)) { 277e8d8bef9SDimitry Andric // In case of operator+ the iterator can be either on the LHS (eg.: it + 1), 278e8d8bef9SDimitry Andric // or on the RHS (eg.: 1 + it). Both cases are modeled. 279e8d8bef9SDimitry Andric const bool IsIterOnLHS = BO->getLHS()->getType()->isPointerType(); 280e8d8bef9SDimitry Andric const Expr *const &IterExpr = IsIterOnLHS ? LHS : RHS; 281e8d8bef9SDimitry Andric const Expr *const &AmountExpr = IsIterOnLHS ? RHS : LHS; 282e8d8bef9SDimitry Andric 283e8d8bef9SDimitry Andric // The non-iterator side must have an integral or enumeration type. 284e8d8bef9SDimitry Andric if (!AmountExpr->getType()->isIntegralOrEnumerationType()) 285e8d8bef9SDimitry Andric return; 286647cbc5dSDimitry Andric SVal AmountVal = IsIterOnLHS ? RVal : LVal; 287e8d8bef9SDimitry Andric handlePtrIncrOrDecr(C, IterExpr, BinaryOperator::getOverloadedOperator(OK), 288e8d8bef9SDimitry Andric AmountVal); 2895ffd83dbSDimitry Andric } 2905ffd83dbSDimitry Andric } 2915ffd83dbSDimitry Andric 292480093f4SDimitry Andric void IteratorModeling::checkPostStmt(const MaterializeTemporaryExpr *MTE, 293480093f4SDimitry Andric CheckerContext &C) const { 294480093f4SDimitry Andric /* Transfer iterator state to temporary objects */ 295480093f4SDimitry Andric auto State = C.getState(); 296480093f4SDimitry Andric const auto *Pos = getIteratorPosition(State, C.getSVal(MTE->getSubExpr())); 297480093f4SDimitry Andric if (!Pos) 298480093f4SDimitry Andric return; 299480093f4SDimitry Andric State = setIteratorPosition(State, C.getSVal(MTE), *Pos); 300480093f4SDimitry Andric C.addTransition(State); 301480093f4SDimitry Andric } 302480093f4SDimitry Andric 303480093f4SDimitry Andric void IteratorModeling::checkLiveSymbols(ProgramStateRef State, 304480093f4SDimitry Andric SymbolReaper &SR) const { 3055ffd83dbSDimitry Andric // Keep symbolic expressions of iterator positions alive 306480093f4SDimitry Andric auto RegionMap = State->get<IteratorRegionMap>(); 30706c3fb27SDimitry Andric for (const IteratorPosition &Pos : llvm::make_second_range(RegionMap)) { 30806c3fb27SDimitry Andric for (SymbolRef Sym : Pos.getOffset()->symbols()) 30906c3fb27SDimitry Andric if (isa<SymbolData>(Sym)) 31006c3fb27SDimitry Andric SR.markLive(Sym); 311480093f4SDimitry Andric } 312480093f4SDimitry Andric 313480093f4SDimitry Andric auto SymbolMap = State->get<IteratorSymbolMap>(); 31406c3fb27SDimitry Andric for (const IteratorPosition &Pos : llvm::make_second_range(SymbolMap)) { 31506c3fb27SDimitry Andric for (SymbolRef Sym : Pos.getOffset()->symbols()) 31606c3fb27SDimitry Andric if (isa<SymbolData>(Sym)) 31706c3fb27SDimitry Andric SR.markLive(Sym); 318480093f4SDimitry Andric } 319480093f4SDimitry Andric } 320480093f4SDimitry Andric 321480093f4SDimitry Andric void IteratorModeling::checkDeadSymbols(SymbolReaper &SR, 322480093f4SDimitry Andric CheckerContext &C) const { 323480093f4SDimitry Andric // Cleanup 324480093f4SDimitry Andric auto State = C.getState(); 325480093f4SDimitry Andric 326480093f4SDimitry Andric auto RegionMap = State->get<IteratorRegionMap>(); 327480093f4SDimitry Andric for (const auto &Reg : RegionMap) { 328480093f4SDimitry Andric if (!SR.isLiveRegion(Reg.first)) { 329480093f4SDimitry Andric // The region behind the `LazyCompoundVal` is often cleaned up before 330480093f4SDimitry Andric // the `LazyCompoundVal` itself. If there are iterator positions keyed 331480093f4SDimitry Andric // by these regions their cleanup must be deferred. 332480093f4SDimitry Andric if (!isBoundThroughLazyCompoundVal(State->getEnvironment(), Reg.first)) { 333480093f4SDimitry Andric State = State->remove<IteratorRegionMap>(Reg.first); 334480093f4SDimitry Andric } 335480093f4SDimitry Andric } 336480093f4SDimitry Andric } 337480093f4SDimitry Andric 338480093f4SDimitry Andric auto SymbolMap = State->get<IteratorSymbolMap>(); 339480093f4SDimitry Andric for (const auto &Sym : SymbolMap) { 340480093f4SDimitry Andric if (!SR.isLive(Sym.first)) { 341480093f4SDimitry Andric State = State->remove<IteratorSymbolMap>(Sym.first); 342480093f4SDimitry Andric } 343480093f4SDimitry Andric } 344480093f4SDimitry Andric 3455ffd83dbSDimitry Andric C.addTransition(State); 346480093f4SDimitry Andric } 3475ffd83dbSDimitry Andric 3485ffd83dbSDimitry Andric void 3495ffd83dbSDimitry Andric IteratorModeling::handleOverloadedOperator(CheckerContext &C, 3505ffd83dbSDimitry Andric const CallEvent &Call, 3515ffd83dbSDimitry Andric OverloadedOperatorKind Op) const { 3525ffd83dbSDimitry Andric if (isSimpleComparisonOperator(Op)) { 3535ffd83dbSDimitry Andric const auto *OrigExpr = Call.getOriginExpr(); 3545ffd83dbSDimitry Andric if (!OrigExpr) 3555ffd83dbSDimitry Andric return; 3565ffd83dbSDimitry Andric 3575ffd83dbSDimitry Andric if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { 3585ffd83dbSDimitry Andric handleComparison(C, OrigExpr, Call.getReturnValue(), 3595ffd83dbSDimitry Andric InstCall->getCXXThisVal(), Call.getArgSVal(0), Op); 3605ffd83dbSDimitry Andric return; 3615ffd83dbSDimitry Andric } 3625ffd83dbSDimitry Andric 3635ffd83dbSDimitry Andric handleComparison(C, OrigExpr, Call.getReturnValue(), Call.getArgSVal(0), 3645ffd83dbSDimitry Andric Call.getArgSVal(1), Op); 3655ffd83dbSDimitry Andric return; 3665ffd83dbSDimitry Andric } else if (isRandomIncrOrDecrOperator(Op)) { 3675ffd83dbSDimitry Andric const auto *OrigExpr = Call.getOriginExpr(); 3685ffd83dbSDimitry Andric if (!OrigExpr) 3695ffd83dbSDimitry Andric return; 3705ffd83dbSDimitry Andric 3715ffd83dbSDimitry Andric if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { 3725ffd83dbSDimitry Andric if (Call.getNumArgs() >= 1 && 3735ffd83dbSDimitry Andric Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) { 3745ffd83dbSDimitry Andric handleRandomIncrOrDecr(C, OrigExpr, Op, Call.getReturnValue(), 3755ffd83dbSDimitry Andric InstCall->getCXXThisVal(), Call.getArgSVal(0)); 3765ffd83dbSDimitry Andric return; 3775ffd83dbSDimitry Andric } 378e8d8bef9SDimitry Andric } else if (Call.getNumArgs() >= 2) { 379e8d8bef9SDimitry Andric const Expr *FirstArg = Call.getArgExpr(0); 380e8d8bef9SDimitry Andric const Expr *SecondArg = Call.getArgExpr(1); 381e8d8bef9SDimitry Andric const QualType FirstType = FirstArg->getType(); 382e8d8bef9SDimitry Andric const QualType SecondType = SecondArg->getType(); 383e8d8bef9SDimitry Andric 384e8d8bef9SDimitry Andric if (FirstType->isIntegralOrEnumerationType() || 385e8d8bef9SDimitry Andric SecondType->isIntegralOrEnumerationType()) { 386e8d8bef9SDimitry Andric // In case of operator+ the iterator can be either on the LHS (eg.: 387e8d8bef9SDimitry Andric // it + 1), or on the RHS (eg.: 1 + it). Both cases are modeled. 388e8d8bef9SDimitry Andric const bool IsIterFirst = FirstType->isStructureOrClassType(); 389e8d8bef9SDimitry Andric const SVal FirstArg = Call.getArgSVal(0); 390e8d8bef9SDimitry Andric const SVal SecondArg = Call.getArgSVal(1); 391647cbc5dSDimitry Andric SVal Iterator = IsIterFirst ? FirstArg : SecondArg; 392647cbc5dSDimitry Andric SVal Amount = IsIterFirst ? SecondArg : FirstArg; 393e8d8bef9SDimitry Andric 3945ffd83dbSDimitry Andric handleRandomIncrOrDecr(C, OrigExpr, Op, Call.getReturnValue(), 395e8d8bef9SDimitry Andric Iterator, Amount); 3965ffd83dbSDimitry Andric return; 3975ffd83dbSDimitry Andric } 3985ffd83dbSDimitry Andric } 3995ffd83dbSDimitry Andric } else if (isIncrementOperator(Op)) { 4005ffd83dbSDimitry Andric if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { 4015ffd83dbSDimitry Andric handleIncrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(), 4025ffd83dbSDimitry Andric Call.getNumArgs()); 4035ffd83dbSDimitry Andric return; 4045ffd83dbSDimitry Andric } 4055ffd83dbSDimitry Andric 4065ffd83dbSDimitry Andric handleIncrement(C, Call.getReturnValue(), Call.getArgSVal(0), 4075ffd83dbSDimitry Andric Call.getNumArgs()); 4085ffd83dbSDimitry Andric return; 4095ffd83dbSDimitry Andric } else if (isDecrementOperator(Op)) { 4105ffd83dbSDimitry Andric if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { 4115ffd83dbSDimitry Andric handleDecrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(), 4125ffd83dbSDimitry Andric Call.getNumArgs()); 4135ffd83dbSDimitry Andric return; 4145ffd83dbSDimitry Andric } 4155ffd83dbSDimitry Andric 4165ffd83dbSDimitry Andric handleDecrement(C, Call.getReturnValue(), Call.getArgSVal(0), 4175ffd83dbSDimitry Andric Call.getNumArgs()); 4185ffd83dbSDimitry Andric return; 419480093f4SDimitry Andric } 420480093f4SDimitry Andric } 421480093f4SDimitry Andric 4225ffd83dbSDimitry Andric void 4235ffd83dbSDimitry Andric IteratorModeling::handleAdvanceLikeFunction(CheckerContext &C, 4245ffd83dbSDimitry Andric const CallEvent &Call, 4255ffd83dbSDimitry Andric const Expr *OrigExpr, 4265ffd83dbSDimitry Andric const AdvanceFn *Handler) const { 4275ffd83dbSDimitry Andric if (!C.wasInlined) { 4285ffd83dbSDimitry Andric (this->**Handler)(C, OrigExpr, Call.getReturnValue(), 4295ffd83dbSDimitry Andric Call.getArgSVal(0), Call.getArgSVal(1)); 4305ffd83dbSDimitry Andric return; 4315ffd83dbSDimitry Andric } 4325ffd83dbSDimitry Andric 4335ffd83dbSDimitry Andric // If std::advance() was inlined, but a non-standard function it calls inside 4345ffd83dbSDimitry Andric // was not, then we have to model it explicitly 4355ffd83dbSDimitry Andric const auto *IdInfo = cast<FunctionDecl>(Call.getDecl())->getIdentifier(); 4365ffd83dbSDimitry Andric if (IdInfo) { 4375ffd83dbSDimitry Andric if (IdInfo->getName() == "advance") { 4385ffd83dbSDimitry Andric if (noChangeInAdvance(C, Call.getArgSVal(0), OrigExpr)) { 4395ffd83dbSDimitry Andric (this->**Handler)(C, OrigExpr, Call.getReturnValue(), 4405ffd83dbSDimitry Andric Call.getArgSVal(0), Call.getArgSVal(1)); 4415ffd83dbSDimitry Andric } 4425ffd83dbSDimitry Andric } 4435ffd83dbSDimitry Andric } 444480093f4SDimitry Andric } 445480093f4SDimitry Andric 446480093f4SDimitry Andric void IteratorModeling::handleComparison(CheckerContext &C, const Expr *CE, 447647cbc5dSDimitry Andric SVal RetVal, SVal LVal, SVal RVal, 448480093f4SDimitry Andric OverloadedOperatorKind Op) const { 449480093f4SDimitry Andric // Record the operands and the operator of the comparison for the next 450480093f4SDimitry Andric // evalAssume, if the result is a symbolic expression. If it is a concrete 451480093f4SDimitry Andric // value (only one branch is possible), then transfer the state between 452480093f4SDimitry Andric // the operands according to the operator and the result 453480093f4SDimitry Andric auto State = C.getState(); 454480093f4SDimitry Andric const auto *LPos = getIteratorPosition(State, LVal); 455480093f4SDimitry Andric const auto *RPos = getIteratorPosition(State, RVal); 456480093f4SDimitry Andric const MemRegion *Cont = nullptr; 457480093f4SDimitry Andric if (LPos) { 458480093f4SDimitry Andric Cont = LPos->getContainer(); 459480093f4SDimitry Andric } else if (RPos) { 460480093f4SDimitry Andric Cont = RPos->getContainer(); 461480093f4SDimitry Andric } 462480093f4SDimitry Andric if (!Cont) 463480093f4SDimitry Andric return; 464480093f4SDimitry Andric 4655ffd83dbSDimitry Andric // At least one of the iterators has recorded positions. If one of them does 466480093f4SDimitry Andric // not then create a new symbol for the offset. 467480093f4SDimitry Andric SymbolRef Sym; 468480093f4SDimitry Andric if (!LPos || !RPos) { 469480093f4SDimitry Andric auto &SymMgr = C.getSymbolManager(); 470480093f4SDimitry Andric Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(), 471480093f4SDimitry Andric C.getASTContext().LongTy, C.blockCount()); 472480093f4SDimitry Andric State = assumeNoOverflow(State, Sym, 4); 473480093f4SDimitry Andric } 474480093f4SDimitry Andric 475480093f4SDimitry Andric if (!LPos) { 476480093f4SDimitry Andric State = setIteratorPosition(State, LVal, 477480093f4SDimitry Andric IteratorPosition::getPosition(Cont, Sym)); 478480093f4SDimitry Andric LPos = getIteratorPosition(State, LVal); 479480093f4SDimitry Andric } else if (!RPos) { 480480093f4SDimitry Andric State = setIteratorPosition(State, RVal, 481480093f4SDimitry Andric IteratorPosition::getPosition(Cont, Sym)); 482480093f4SDimitry Andric RPos = getIteratorPosition(State, RVal); 483480093f4SDimitry Andric } 484480093f4SDimitry Andric 485e8d8bef9SDimitry Andric // If the value for which we just tried to set a new iterator position is 486e8d8bef9SDimitry Andric // an `SVal`for which no iterator position can be set then the setting was 487e8d8bef9SDimitry Andric // unsuccessful. We cannot handle the comparison in this case. 488e8d8bef9SDimitry Andric if (!LPos || !RPos) 489e8d8bef9SDimitry Andric return; 490e8d8bef9SDimitry Andric 4915ffd83dbSDimitry Andric // We cannot make assumptions on `UnknownVal`. Let us conjure a symbol 492480093f4SDimitry Andric // instead. 493480093f4SDimitry Andric if (RetVal.isUnknown()) { 494480093f4SDimitry Andric auto &SymMgr = C.getSymbolManager(); 495480093f4SDimitry Andric auto *LCtx = C.getLocationContext(); 496480093f4SDimitry Andric RetVal = nonloc::SymbolVal(SymMgr.conjureSymbol( 497480093f4SDimitry Andric CE, LCtx, C.getASTContext().BoolTy, C.blockCount())); 498480093f4SDimitry Andric State = State->BindExpr(CE, LCtx, RetVal); 499480093f4SDimitry Andric } 500480093f4SDimitry Andric 501480093f4SDimitry Andric processComparison(C, State, LPos->getOffset(), RPos->getOffset(), RetVal, Op); 502480093f4SDimitry Andric } 503480093f4SDimitry Andric 504480093f4SDimitry Andric void IteratorModeling::processComparison(CheckerContext &C, 505480093f4SDimitry Andric ProgramStateRef State, SymbolRef Sym1, 506647cbc5dSDimitry Andric SymbolRef Sym2, SVal RetVal, 507480093f4SDimitry Andric OverloadedOperatorKind Op) const { 508480093f4SDimitry Andric if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) { 509480093f4SDimitry Andric if ((State = relateSymbols(State, Sym1, Sym2, 510480093f4SDimitry Andric (Op == OO_EqualEqual) == 511480093f4SDimitry Andric (TruthVal->getValue() != 0)))) { 512480093f4SDimitry Andric C.addTransition(State); 513480093f4SDimitry Andric } else { 514480093f4SDimitry Andric C.generateSink(State, C.getPredecessor()); 515480093f4SDimitry Andric } 516480093f4SDimitry Andric return; 517480093f4SDimitry Andric } 518480093f4SDimitry Andric 519480093f4SDimitry Andric const auto ConditionVal = RetVal.getAs<DefinedSVal>(); 520480093f4SDimitry Andric if (!ConditionVal) 521480093f4SDimitry Andric return; 522480093f4SDimitry Andric 523480093f4SDimitry Andric if (auto StateTrue = relateSymbols(State, Sym1, Sym2, Op == OO_EqualEqual)) { 524480093f4SDimitry Andric StateTrue = StateTrue->assume(*ConditionVal, true); 525480093f4SDimitry Andric C.addTransition(StateTrue); 526480093f4SDimitry Andric } 527480093f4SDimitry Andric 528480093f4SDimitry Andric if (auto StateFalse = relateSymbols(State, Sym1, Sym2, Op != OO_EqualEqual)) { 529480093f4SDimitry Andric StateFalse = StateFalse->assume(*ConditionVal, false); 530480093f4SDimitry Andric C.addTransition(StateFalse); 531480093f4SDimitry Andric } 532480093f4SDimitry Andric } 533480093f4SDimitry Andric 534647cbc5dSDimitry Andric void IteratorModeling::handleIncrement(CheckerContext &C, SVal RetVal, 535647cbc5dSDimitry Andric SVal Iter, bool Postfix) const { 536480093f4SDimitry Andric // Increment the symbolic expressions which represents the position of the 537480093f4SDimitry Andric // iterator 538480093f4SDimitry Andric auto State = C.getState(); 539480093f4SDimitry Andric auto &BVF = C.getSymbolManager().getBasicVals(); 540480093f4SDimitry Andric 541480093f4SDimitry Andric const auto *Pos = getIteratorPosition(State, Iter); 542480093f4SDimitry Andric if (!Pos) 543480093f4SDimitry Andric return; 544480093f4SDimitry Andric 545480093f4SDimitry Andric auto NewState = 546480093f4SDimitry Andric advancePosition(State, Iter, OO_Plus, 547480093f4SDimitry Andric nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1)))); 548480093f4SDimitry Andric assert(NewState && 549480093f4SDimitry Andric "Advancing position by concrete int should always be successful"); 550480093f4SDimitry Andric 551480093f4SDimitry Andric const auto *NewPos = getIteratorPosition(NewState, Iter); 552480093f4SDimitry Andric assert(NewPos && 553480093f4SDimitry Andric "Iterator should have position after successful advancement"); 554480093f4SDimitry Andric 555480093f4SDimitry Andric State = setIteratorPosition(State, Iter, *NewPos); 556480093f4SDimitry Andric State = setIteratorPosition(State, RetVal, Postfix ? *Pos : *NewPos); 557480093f4SDimitry Andric C.addTransition(State); 558480093f4SDimitry Andric } 559480093f4SDimitry Andric 560647cbc5dSDimitry Andric void IteratorModeling::handleDecrement(CheckerContext &C, SVal RetVal, 561647cbc5dSDimitry Andric SVal Iter, bool Postfix) const { 562480093f4SDimitry Andric // Decrement the symbolic expressions which represents the position of the 563480093f4SDimitry Andric // iterator 564480093f4SDimitry Andric auto State = C.getState(); 565480093f4SDimitry Andric auto &BVF = C.getSymbolManager().getBasicVals(); 566480093f4SDimitry Andric 567480093f4SDimitry Andric const auto *Pos = getIteratorPosition(State, Iter); 568480093f4SDimitry Andric if (!Pos) 569480093f4SDimitry Andric return; 570480093f4SDimitry Andric 571480093f4SDimitry Andric auto NewState = 572480093f4SDimitry Andric advancePosition(State, Iter, OO_Minus, 573480093f4SDimitry Andric nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1)))); 574480093f4SDimitry Andric assert(NewState && 575480093f4SDimitry Andric "Advancing position by concrete int should always be successful"); 576480093f4SDimitry Andric 577480093f4SDimitry Andric const auto *NewPos = getIteratorPosition(NewState, Iter); 578480093f4SDimitry Andric assert(NewPos && 579480093f4SDimitry Andric "Iterator should have position after successful advancement"); 580480093f4SDimitry Andric 581480093f4SDimitry Andric State = setIteratorPosition(State, Iter, *NewPos); 582480093f4SDimitry Andric State = setIteratorPosition(State, RetVal, Postfix ? *Pos : *NewPos); 583480093f4SDimitry Andric C.addTransition(State); 584480093f4SDimitry Andric } 585480093f4SDimitry Andric 586e8d8bef9SDimitry Andric void IteratorModeling::handleRandomIncrOrDecr(CheckerContext &C, const Expr *CE, 587480093f4SDimitry Andric OverloadedOperatorKind Op, 588647cbc5dSDimitry Andric SVal RetVal, SVal Iterator, 589647cbc5dSDimitry Andric SVal Amount) const { 590480093f4SDimitry Andric // Increment or decrement the symbolic expressions which represents the 591480093f4SDimitry Andric // position of the iterator 592480093f4SDimitry Andric auto State = C.getState(); 593480093f4SDimitry Andric 594e8d8bef9SDimitry Andric const auto *Pos = getIteratorPosition(State, Iterator); 595480093f4SDimitry Andric if (!Pos) 596480093f4SDimitry Andric return; 597480093f4SDimitry Andric 598e8d8bef9SDimitry Andric const auto *Value = &Amount; 599e8d8bef9SDimitry Andric SVal Val; 600e8d8bef9SDimitry Andric if (auto LocAmount = Amount.getAs<Loc>()) { 601e8d8bef9SDimitry Andric Val = State->getRawSVal(*LocAmount); 602e8d8bef9SDimitry Andric Value = &Val; 603480093f4SDimitry Andric } 604480093f4SDimitry Andric 605e8d8bef9SDimitry Andric const auto &TgtVal = 606e8d8bef9SDimitry Andric (Op == OO_PlusEqual || Op == OO_MinusEqual) ? Iterator : RetVal; 607480093f4SDimitry Andric 6085ffd83dbSDimitry Andric // `AdvancedState` is a state where the position of `LHS` is advanced. We 6095ffd83dbSDimitry Andric // only need this state to retrieve the new position, but we do not want 6105ffd83dbSDimitry Andric // to change the position of `LHS` (in every case). 611e8d8bef9SDimitry Andric auto AdvancedState = advancePosition(State, Iterator, Op, *Value); 6125ffd83dbSDimitry Andric if (AdvancedState) { 613e8d8bef9SDimitry Andric const auto *NewPos = getIteratorPosition(AdvancedState, Iterator); 614480093f4SDimitry Andric assert(NewPos && 615480093f4SDimitry Andric "Iterator should have position after successful advancement"); 616480093f4SDimitry Andric 6175ffd83dbSDimitry Andric State = setIteratorPosition(State, TgtVal, *NewPos); 618480093f4SDimitry Andric C.addTransition(State); 619480093f4SDimitry Andric } else { 620480093f4SDimitry Andric assignToContainer(C, CE, TgtVal, Pos->getContainer()); 621480093f4SDimitry Andric } 622480093f4SDimitry Andric } 623480093f4SDimitry Andric 6245ffd83dbSDimitry Andric void IteratorModeling::handlePtrIncrOrDecr(CheckerContext &C, 6255ffd83dbSDimitry Andric const Expr *Iterator, 6265ffd83dbSDimitry Andric OverloadedOperatorKind OK, 6275ffd83dbSDimitry Andric SVal Offset) const { 62881ad6265SDimitry Andric if (!isa<DefinedSVal>(Offset)) 629e8d8bef9SDimitry Andric return; 630e8d8bef9SDimitry Andric 6315ffd83dbSDimitry Andric QualType PtrType = Iterator->getType(); 6325ffd83dbSDimitry Andric if (!PtrType->isPointerType()) 6335ffd83dbSDimitry Andric return; 6345ffd83dbSDimitry Andric QualType ElementType = PtrType->getPointeeType(); 6355ffd83dbSDimitry Andric 6365ffd83dbSDimitry Andric ProgramStateRef State = C.getState(); 6375ffd83dbSDimitry Andric SVal OldVal = State->getSVal(Iterator, C.getLocationContext()); 6385ffd83dbSDimitry Andric 6395ffd83dbSDimitry Andric const IteratorPosition *OldPos = getIteratorPosition(State, OldVal); 6405ffd83dbSDimitry Andric if (!OldPos) 641480093f4SDimitry Andric return; 642480093f4SDimitry Andric 6435ffd83dbSDimitry Andric SVal NewVal; 644e8d8bef9SDimitry Andric if (OK == OO_Plus || OK == OO_PlusEqual) { 6455ffd83dbSDimitry Andric NewVal = State->getLValue(ElementType, Offset, OldVal); 646e8d8bef9SDimitry Andric } else { 647e8d8bef9SDimitry Andric auto &SVB = C.getSValBuilder(); 648e8d8bef9SDimitry Andric SVal NegatedOffset = SVB.evalMinus(Offset.castAs<NonLoc>()); 6495ffd83dbSDimitry Andric NewVal = State->getLValue(ElementType, NegatedOffset, OldVal); 650480093f4SDimitry Andric } 651480093f4SDimitry Andric 6525ffd83dbSDimitry Andric // `AdvancedState` is a state where the position of `Old` is advanced. We 6535ffd83dbSDimitry Andric // only need this state to retrieve the new position, but we do not want 6545ffd83dbSDimitry Andric // ever to change the position of `OldVal`. 6555ffd83dbSDimitry Andric auto AdvancedState = advancePosition(State, OldVal, OK, Offset); 6565ffd83dbSDimitry Andric if (AdvancedState) { 6575ffd83dbSDimitry Andric const IteratorPosition *NewPos = getIteratorPosition(AdvancedState, OldVal); 6585ffd83dbSDimitry Andric assert(NewPos && 6595ffd83dbSDimitry Andric "Iterator should have position after successful advancement"); 660480093f4SDimitry Andric 6615ffd83dbSDimitry Andric ProgramStateRef NewState = setIteratorPosition(State, NewVal, *NewPos); 6625ffd83dbSDimitry Andric C.addTransition(NewState); 6635ffd83dbSDimitry Andric } else { 6645ffd83dbSDimitry Andric assignToContainer(C, Iterator, NewVal, OldPos->getContainer()); 665480093f4SDimitry Andric } 6665ffd83dbSDimitry Andric } 6675ffd83dbSDimitry Andric 6685ffd83dbSDimitry Andric void IteratorModeling::handleAdvance(CheckerContext &C, const Expr *CE, 6695ffd83dbSDimitry Andric SVal RetVal, SVal Iter, 6705ffd83dbSDimitry Andric SVal Amount) const { 6715ffd83dbSDimitry Andric handleRandomIncrOrDecr(C, CE, OO_PlusEqual, RetVal, Iter, Amount); 6725ffd83dbSDimitry Andric } 6735ffd83dbSDimitry Andric 6745ffd83dbSDimitry Andric void IteratorModeling::handlePrev(CheckerContext &C, const Expr *CE, 6755ffd83dbSDimitry Andric SVal RetVal, SVal Iter, SVal Amount) const { 6765ffd83dbSDimitry Andric handleRandomIncrOrDecr(C, CE, OO_Minus, RetVal, Iter, Amount); 6775ffd83dbSDimitry Andric } 6785ffd83dbSDimitry Andric 6795ffd83dbSDimitry Andric void IteratorModeling::handleNext(CheckerContext &C, const Expr *CE, 6805ffd83dbSDimitry Andric SVal RetVal, SVal Iter, SVal Amount) const { 6815ffd83dbSDimitry Andric handleRandomIncrOrDecr(C, CE, OO_Plus, RetVal, Iter, Amount); 682480093f4SDimitry Andric } 683480093f4SDimitry Andric 684480093f4SDimitry Andric void IteratorModeling::assignToContainer(CheckerContext &C, const Expr *CE, 685647cbc5dSDimitry Andric SVal RetVal, 686480093f4SDimitry Andric const MemRegion *Cont) const { 687480093f4SDimitry Andric Cont = Cont->getMostDerivedObjectRegion(); 688480093f4SDimitry Andric 689480093f4SDimitry Andric auto State = C.getState(); 6905ffd83dbSDimitry Andric const auto *LCtx = C.getLocationContext(); 6915ffd83dbSDimitry Andric State = createIteratorPosition(State, RetVal, Cont, CE, LCtx, C.blockCount()); 6925ffd83dbSDimitry Andric 693480093f4SDimitry Andric C.addTransition(State); 694480093f4SDimitry Andric } 695480093f4SDimitry Andric 6965ffd83dbSDimitry Andric bool IteratorModeling::noChangeInAdvance(CheckerContext &C, SVal Iter, 6975ffd83dbSDimitry Andric const Expr *CE) const { 6985ffd83dbSDimitry Andric // Compare the iterator position before and after the call. (To be called 6995ffd83dbSDimitry Andric // from `checkPostCall()`.) 7005ffd83dbSDimitry Andric const auto StateAfter = C.getState(); 701480093f4SDimitry Andric 7025ffd83dbSDimitry Andric const auto *PosAfter = getIteratorPosition(StateAfter, Iter); 7035ffd83dbSDimitry Andric // If we have no position after the call of `std::advance`, then we are not 7045ffd83dbSDimitry Andric // interested. (Modeling of an inlined `std::advance()` should not remove the 7055ffd83dbSDimitry Andric // position in any case.) 7065ffd83dbSDimitry Andric if (!PosAfter) 7075ffd83dbSDimitry Andric return false; 708480093f4SDimitry Andric 7095ffd83dbSDimitry Andric const ExplodedNode *N = findCallEnter(C.getPredecessor(), CE); 7105ffd83dbSDimitry Andric assert(N && "Any call should have a `CallEnter` node."); 711480093f4SDimitry Andric 7125ffd83dbSDimitry Andric const auto StateBefore = N->getState(); 7135ffd83dbSDimitry Andric const auto *PosBefore = getIteratorPosition(StateBefore, Iter); 714e8d8bef9SDimitry Andric // FIXME: `std::advance()` should not create a new iterator position but 715e8d8bef9SDimitry Andric // change existing ones. However, in case of iterators implemented as 716e8d8bef9SDimitry Andric // pointers the handling of parameters in `std::advance()`-like 717e8d8bef9SDimitry Andric // functions is still incomplete which may result in cases where 718e8d8bef9SDimitry Andric // the new position is assigned to the wrong pointer. This causes 719e8d8bef9SDimitry Andric // crash if we use an assertion here. 720e8d8bef9SDimitry Andric if (!PosBefore) 721e8d8bef9SDimitry Andric return false; 722480093f4SDimitry Andric 7235ffd83dbSDimitry Andric return PosBefore->getOffset() == PosAfter->getOffset(); 724480093f4SDimitry Andric } 725480093f4SDimitry Andric 726480093f4SDimitry Andric void IteratorModeling::printState(raw_ostream &Out, ProgramStateRef State, 727480093f4SDimitry Andric const char *NL, const char *Sep) const { 728480093f4SDimitry Andric auto SymbolMap = State->get<IteratorSymbolMap>(); 729480093f4SDimitry Andric auto RegionMap = State->get<IteratorRegionMap>(); 7305ffd83dbSDimitry Andric // Use a counter to add newlines before every line except the first one. 7315ffd83dbSDimitry Andric unsigned Count = 0; 732480093f4SDimitry Andric 733480093f4SDimitry Andric if (!SymbolMap.isEmpty() || !RegionMap.isEmpty()) { 734480093f4SDimitry Andric Out << Sep << "Iterator Positions :" << NL; 735480093f4SDimitry Andric for (const auto &Sym : SymbolMap) { 7365ffd83dbSDimitry Andric if (Count++) 7375ffd83dbSDimitry Andric Out << NL; 7385ffd83dbSDimitry Andric 739480093f4SDimitry Andric Sym.first->dumpToStream(Out); 740480093f4SDimitry Andric Out << " : "; 741480093f4SDimitry Andric const auto Pos = Sym.second; 742480093f4SDimitry Andric Out << (Pos.isValid() ? "Valid" : "Invalid") << " ; Container == "; 743480093f4SDimitry Andric Pos.getContainer()->dumpToStream(Out); 744480093f4SDimitry Andric Out<<" ; Offset == "; 745480093f4SDimitry Andric Pos.getOffset()->dumpToStream(Out); 746480093f4SDimitry Andric } 747480093f4SDimitry Andric 748480093f4SDimitry Andric for (const auto &Reg : RegionMap) { 7495ffd83dbSDimitry Andric if (Count++) 7505ffd83dbSDimitry Andric Out << NL; 7515ffd83dbSDimitry Andric 752480093f4SDimitry Andric Reg.first->dumpToStream(Out); 753480093f4SDimitry Andric Out << " : "; 754480093f4SDimitry Andric const auto Pos = Reg.second; 755480093f4SDimitry Andric Out << (Pos.isValid() ? "Valid" : "Invalid") << " ; Container == "; 756480093f4SDimitry Andric Pos.getContainer()->dumpToStream(Out); 757480093f4SDimitry Andric Out<<" ; Offset == "; 758480093f4SDimitry Andric Pos.getOffset()->dumpToStream(Out); 759480093f4SDimitry Andric } 760480093f4SDimitry Andric } 761480093f4SDimitry Andric } 762480093f4SDimitry Andric 763480093f4SDimitry Andric namespace { 764480093f4SDimitry Andric 765480093f4SDimitry Andric bool isSimpleComparisonOperator(OverloadedOperatorKind OK) { 766480093f4SDimitry Andric return OK == OO_EqualEqual || OK == OO_ExclaimEqual; 767480093f4SDimitry Andric } 768480093f4SDimitry Andric 7695ffd83dbSDimitry Andric bool isSimpleComparisonOperator(BinaryOperatorKind OK) { 7705ffd83dbSDimitry Andric return OK == BO_EQ || OK == BO_NE; 771480093f4SDimitry Andric } 772480093f4SDimitry Andric 773647cbc5dSDimitry Andric ProgramStateRef removeIteratorPosition(ProgramStateRef State, SVal Val) { 774480093f4SDimitry Andric if (auto Reg = Val.getAsRegion()) { 775480093f4SDimitry Andric Reg = Reg->getMostDerivedObjectRegion(); 776480093f4SDimitry Andric return State->remove<IteratorRegionMap>(Reg); 777480093f4SDimitry Andric } else if (const auto Sym = Val.getAsSymbol()) { 778480093f4SDimitry Andric return State->remove<IteratorSymbolMap>(Sym); 779480093f4SDimitry Andric } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) { 780480093f4SDimitry Andric return State->remove<IteratorRegionMap>(LCVal->getRegion()); 781480093f4SDimitry Andric } 782480093f4SDimitry Andric return nullptr; 783480093f4SDimitry Andric } 784480093f4SDimitry Andric 785480093f4SDimitry Andric ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1, 786480093f4SDimitry Andric SymbolRef Sym2, bool Equal) { 787480093f4SDimitry Andric auto &SVB = State->getStateManager().getSValBuilder(); 788480093f4SDimitry Andric 789480093f4SDimitry Andric // FIXME: This code should be reworked as follows: 790480093f4SDimitry Andric // 1. Subtract the operands using evalBinOp(). 791480093f4SDimitry Andric // 2. Assume that the result doesn't overflow. 792480093f4SDimitry Andric // 3. Compare the result to 0. 793480093f4SDimitry Andric // 4. Assume the result of the comparison. 794480093f4SDimitry Andric const auto comparison = 795480093f4SDimitry Andric SVB.evalBinOp(State, BO_EQ, nonloc::SymbolVal(Sym1), 796480093f4SDimitry Andric nonloc::SymbolVal(Sym2), SVB.getConditionType()); 797480093f4SDimitry Andric 79881ad6265SDimitry Andric assert(isa<DefinedSVal>(comparison) && 799480093f4SDimitry Andric "Symbol comparison must be a `DefinedSVal`"); 800480093f4SDimitry Andric 801480093f4SDimitry Andric auto NewState = State->assume(comparison.castAs<DefinedSVal>(), Equal); 802480093f4SDimitry Andric if (!NewState) 803480093f4SDimitry Andric return nullptr; 804480093f4SDimitry Andric 805480093f4SDimitry Andric if (const auto CompSym = comparison.getAsSymbol()) { 806480093f4SDimitry Andric assert(isa<SymIntExpr>(CompSym) && 807480093f4SDimitry Andric "Symbol comparison must be a `SymIntExpr`"); 808480093f4SDimitry Andric assert(BinaryOperator::isComparisonOp( 809480093f4SDimitry Andric cast<SymIntExpr>(CompSym)->getOpcode()) && 810480093f4SDimitry Andric "Symbol comparison must be a comparison"); 811480093f4SDimitry Andric return assumeNoOverflow(NewState, cast<SymIntExpr>(CompSym)->getLHS(), 2); 812480093f4SDimitry Andric } 813480093f4SDimitry Andric 814480093f4SDimitry Andric return NewState; 815480093f4SDimitry Andric } 816480093f4SDimitry Andric 817480093f4SDimitry Andric bool isBoundThroughLazyCompoundVal(const Environment &Env, 818480093f4SDimitry Andric const MemRegion *Reg) { 819480093f4SDimitry Andric for (const auto &Binding : Env) { 820480093f4SDimitry Andric if (const auto LCVal = Binding.second.getAs<nonloc::LazyCompoundVal>()) { 821480093f4SDimitry Andric if (LCVal->getRegion() == Reg) 822480093f4SDimitry Andric return true; 823480093f4SDimitry Andric } 824480093f4SDimitry Andric } 825480093f4SDimitry Andric 826480093f4SDimitry Andric return false; 827480093f4SDimitry Andric } 828480093f4SDimitry Andric 8295ffd83dbSDimitry Andric const ExplodedNode *findCallEnter(const ExplodedNode *Node, const Expr *Call) { 8305ffd83dbSDimitry Andric while (Node) { 8315ffd83dbSDimitry Andric ProgramPoint PP = Node->getLocation(); 8325ffd83dbSDimitry Andric if (auto Enter = PP.getAs<CallEnter>()) { 8335ffd83dbSDimitry Andric if (Enter->getCallExpr() == Call) 8345ffd83dbSDimitry Andric break; 835480093f4SDimitry Andric } 836480093f4SDimitry Andric 8375ffd83dbSDimitry Andric Node = Node->getFirstPred(); 838480093f4SDimitry Andric } 839480093f4SDimitry Andric 8405ffd83dbSDimitry Andric return Node; 841480093f4SDimitry Andric } 842480093f4SDimitry Andric 843480093f4SDimitry Andric } // namespace 844480093f4SDimitry Andric 845480093f4SDimitry Andric void ento::registerIteratorModeling(CheckerManager &mgr) { 846480093f4SDimitry Andric mgr.registerChecker<IteratorModeling>(); 847480093f4SDimitry Andric } 848480093f4SDimitry Andric 8495ffd83dbSDimitry Andric bool ento::shouldRegisterIteratorModeling(const CheckerManager &mgr) { 850480093f4SDimitry Andric return true; 851480093f4SDimitry Andric } 852