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/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 68480093f4SDimitry Andric #include "clang/AST/DeclTemplate.h" 69480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 70480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h" 71480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 72480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 73480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" 74480093f4SDimitry Andric 75480093f4SDimitry Andric #include "Iterator.h" 76480093f4SDimitry Andric 77480093f4SDimitry Andric #include <utility> 78480093f4SDimitry Andric 79480093f4SDimitry Andric using namespace clang; 80480093f4SDimitry Andric using namespace ento; 81480093f4SDimitry Andric using namespace iterator; 82480093f4SDimitry Andric 83480093f4SDimitry Andric namespace { 84480093f4SDimitry Andric 85480093f4SDimitry Andric class IteratorModeling 865ffd83dbSDimitry Andric : public Checker<check::PostCall, check::PostStmt<UnaryOperator>, 875ffd83dbSDimitry Andric check::PostStmt<BinaryOperator>, 885ffd83dbSDimitry Andric check::PostStmt<MaterializeTemporaryExpr>, 89480093f4SDimitry Andric check::Bind, check::LiveSymbols, check::DeadSymbols> { 90480093f4SDimitry Andric 915ffd83dbSDimitry Andric using AdvanceFn = void (IteratorModeling::*)(CheckerContext &, const Expr *, 925ffd83dbSDimitry Andric SVal, SVal, SVal) const; 935ffd83dbSDimitry Andric 945ffd83dbSDimitry Andric void handleOverloadedOperator(CheckerContext &C, const CallEvent &Call, 955ffd83dbSDimitry Andric OverloadedOperatorKind Op) const; 965ffd83dbSDimitry Andric void handleAdvanceLikeFunction(CheckerContext &C, const CallEvent &Call, 975ffd83dbSDimitry Andric const Expr *OrigExpr, 985ffd83dbSDimitry Andric const AdvanceFn *Handler) const; 995ffd83dbSDimitry Andric 100480093f4SDimitry Andric void handleComparison(CheckerContext &C, const Expr *CE, SVal RetVal, 101480093f4SDimitry Andric const SVal &LVal, const SVal &RVal, 102480093f4SDimitry Andric OverloadedOperatorKind Op) const; 103480093f4SDimitry Andric void processComparison(CheckerContext &C, ProgramStateRef State, 104480093f4SDimitry Andric SymbolRef Sym1, SymbolRef Sym2, const SVal &RetVal, 105480093f4SDimitry Andric OverloadedOperatorKind Op) const; 106480093f4SDimitry Andric void handleIncrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter, 107480093f4SDimitry Andric bool Postfix) const; 108480093f4SDimitry Andric void handleDecrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter, 109480093f4SDimitry Andric bool Postfix) const; 110480093f4SDimitry Andric void handleRandomIncrOrDecr(CheckerContext &C, const Expr *CE, 111480093f4SDimitry Andric OverloadedOperatorKind Op, const SVal &RetVal, 112*e8d8bef9SDimitry Andric const SVal &Iterator, const SVal &Amount) const; 1135ffd83dbSDimitry Andric void handlePtrIncrOrDecr(CheckerContext &C, const Expr *Iterator, 1145ffd83dbSDimitry Andric OverloadedOperatorKind OK, SVal Offset) const; 1155ffd83dbSDimitry Andric void handleAdvance(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter, 1165ffd83dbSDimitry Andric SVal Amount) const; 1175ffd83dbSDimitry Andric void handlePrev(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter, 1185ffd83dbSDimitry Andric SVal Amount) const; 1195ffd83dbSDimitry Andric void handleNext(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter, 1205ffd83dbSDimitry Andric SVal Amount) const; 121480093f4SDimitry Andric void assignToContainer(CheckerContext &C, const Expr *CE, const SVal &RetVal, 122480093f4SDimitry Andric const MemRegion *Cont) const; 1235ffd83dbSDimitry Andric bool noChangeInAdvance(CheckerContext &C, SVal Iter, const Expr *CE) const; 124480093f4SDimitry Andric void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, 125480093f4SDimitry Andric const char *Sep) const override; 126480093f4SDimitry Andric 1275ffd83dbSDimitry Andric // std::advance, std::prev & std::next 1285ffd83dbSDimitry Andric CallDescriptionMap<AdvanceFn> AdvanceLikeFunctions = { 1295ffd83dbSDimitry Andric // template<class InputIt, class Distance> 1305ffd83dbSDimitry Andric // void advance(InputIt& it, Distance n); 1315ffd83dbSDimitry Andric {{{"std", "advance"}, 2}, &IteratorModeling::handleAdvance}, 1325ffd83dbSDimitry Andric 1335ffd83dbSDimitry Andric // template<class BidirIt> 1345ffd83dbSDimitry Andric // BidirIt prev( 1355ffd83dbSDimitry Andric // BidirIt it, 1365ffd83dbSDimitry Andric // typename std::iterator_traits<BidirIt>::difference_type n = 1); 1375ffd83dbSDimitry Andric {{{"std", "prev"}, 2}, &IteratorModeling::handlePrev}, 1385ffd83dbSDimitry Andric 1395ffd83dbSDimitry Andric // template<class ForwardIt> 1405ffd83dbSDimitry Andric // ForwardIt next( 1415ffd83dbSDimitry Andric // ForwardIt it, 1425ffd83dbSDimitry Andric // typename std::iterator_traits<ForwardIt>::difference_type n = 1); 1435ffd83dbSDimitry Andric {{{"std", "next"}, 2}, &IteratorModeling::handleNext}, 1445ffd83dbSDimitry Andric }; 1455ffd83dbSDimitry Andric 146480093f4SDimitry Andric public: 1475ffd83dbSDimitry Andric IteratorModeling() = default; 148480093f4SDimitry Andric 149480093f4SDimitry Andric void checkPostCall(const CallEvent &Call, CheckerContext &C) const; 150480093f4SDimitry Andric void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const; 1515ffd83dbSDimitry Andric void checkPostStmt(const UnaryOperator *UO, CheckerContext &C) const; 1525ffd83dbSDimitry Andric void checkPostStmt(const BinaryOperator *BO, CheckerContext &C) const; 153480093f4SDimitry Andric void checkPostStmt(const CXXConstructExpr *CCE, CheckerContext &C) const; 154480093f4SDimitry Andric void checkPostStmt(const DeclStmt *DS, 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); 163480093f4SDimitry Andric ProgramStateRef removeIteratorPosition(ProgramStateRef State, const 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 { 265*e8d8bef9SDimitry Andric const ProgramStateRef State = C.getState(); 266*e8d8bef9SDimitry Andric const BinaryOperatorKind OK = BO->getOpcode(); 267*e8d8bef9SDimitry Andric const Expr *const LHS = BO->getLHS(); 268*e8d8bef9SDimitry Andric const Expr *const RHS = BO->getRHS(); 269*e8d8bef9SDimitry Andric const SVal LVal = State->getSVal(LHS, C.getLocationContext()); 270*e8d8bef9SDimitry 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)) { 277*e8d8bef9SDimitry Andric // In case of operator+ the iterator can be either on the LHS (eg.: it + 1), 278*e8d8bef9SDimitry Andric // or on the RHS (eg.: 1 + it). Both cases are modeled. 279*e8d8bef9SDimitry Andric const bool IsIterOnLHS = BO->getLHS()->getType()->isPointerType(); 280*e8d8bef9SDimitry Andric const Expr *const &IterExpr = IsIterOnLHS ? LHS : RHS; 281*e8d8bef9SDimitry Andric const Expr *const &AmountExpr = IsIterOnLHS ? RHS : LHS; 282*e8d8bef9SDimitry Andric 283*e8d8bef9SDimitry Andric // The non-iterator side must have an integral or enumeration type. 284*e8d8bef9SDimitry Andric if (!AmountExpr->getType()->isIntegralOrEnumerationType()) 285*e8d8bef9SDimitry Andric return; 286*e8d8bef9SDimitry Andric const SVal &AmountVal = IsIterOnLHS ? RVal : LVal; 287*e8d8bef9SDimitry Andric handlePtrIncrOrDecr(C, IterExpr, BinaryOperator::getOverloadedOperator(OK), 288*e8d8bef9SDimitry 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>(); 307480093f4SDimitry Andric for (const auto &Reg : RegionMap) { 308480093f4SDimitry Andric const auto Offset = Reg.second.getOffset(); 309480093f4SDimitry Andric for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i) 310480093f4SDimitry Andric if (isa<SymbolData>(*i)) 311480093f4SDimitry Andric SR.markLive(*i); 312480093f4SDimitry Andric } 313480093f4SDimitry Andric 314480093f4SDimitry Andric auto SymbolMap = State->get<IteratorSymbolMap>(); 315480093f4SDimitry Andric for (const auto &Sym : SymbolMap) { 316480093f4SDimitry Andric const auto Offset = Sym.second.getOffset(); 317480093f4SDimitry Andric for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i) 318480093f4SDimitry Andric if (isa<SymbolData>(*i)) 319480093f4SDimitry Andric SR.markLive(*i); 320480093f4SDimitry Andric } 321480093f4SDimitry Andric 322480093f4SDimitry Andric } 323480093f4SDimitry Andric 324480093f4SDimitry Andric void IteratorModeling::checkDeadSymbols(SymbolReaper &SR, 325480093f4SDimitry Andric CheckerContext &C) const { 326480093f4SDimitry Andric // Cleanup 327480093f4SDimitry Andric auto State = C.getState(); 328480093f4SDimitry Andric 329480093f4SDimitry Andric auto RegionMap = State->get<IteratorRegionMap>(); 330480093f4SDimitry Andric for (const auto &Reg : RegionMap) { 331480093f4SDimitry Andric if (!SR.isLiveRegion(Reg.first)) { 332480093f4SDimitry Andric // The region behind the `LazyCompoundVal` is often cleaned up before 333480093f4SDimitry Andric // the `LazyCompoundVal` itself. If there are iterator positions keyed 334480093f4SDimitry Andric // by these regions their cleanup must be deferred. 335480093f4SDimitry Andric if (!isBoundThroughLazyCompoundVal(State->getEnvironment(), Reg.first)) { 336480093f4SDimitry Andric State = State->remove<IteratorRegionMap>(Reg.first); 337480093f4SDimitry Andric } 338480093f4SDimitry Andric } 339480093f4SDimitry Andric } 340480093f4SDimitry Andric 341480093f4SDimitry Andric auto SymbolMap = State->get<IteratorSymbolMap>(); 342480093f4SDimitry Andric for (const auto &Sym : SymbolMap) { 343480093f4SDimitry Andric if (!SR.isLive(Sym.first)) { 344480093f4SDimitry Andric State = State->remove<IteratorSymbolMap>(Sym.first); 345480093f4SDimitry Andric } 346480093f4SDimitry Andric } 347480093f4SDimitry Andric 3485ffd83dbSDimitry Andric C.addTransition(State); 349480093f4SDimitry Andric } 3505ffd83dbSDimitry Andric 3515ffd83dbSDimitry Andric void 3525ffd83dbSDimitry Andric IteratorModeling::handleOverloadedOperator(CheckerContext &C, 3535ffd83dbSDimitry Andric const CallEvent &Call, 3545ffd83dbSDimitry Andric OverloadedOperatorKind Op) const { 3555ffd83dbSDimitry Andric if (isSimpleComparisonOperator(Op)) { 3565ffd83dbSDimitry Andric const auto *OrigExpr = Call.getOriginExpr(); 3575ffd83dbSDimitry Andric if (!OrigExpr) 3585ffd83dbSDimitry Andric return; 3595ffd83dbSDimitry Andric 3605ffd83dbSDimitry Andric if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { 3615ffd83dbSDimitry Andric handleComparison(C, OrigExpr, Call.getReturnValue(), 3625ffd83dbSDimitry Andric InstCall->getCXXThisVal(), Call.getArgSVal(0), Op); 3635ffd83dbSDimitry Andric return; 3645ffd83dbSDimitry Andric } 3655ffd83dbSDimitry Andric 3665ffd83dbSDimitry Andric handleComparison(C, OrigExpr, Call.getReturnValue(), Call.getArgSVal(0), 3675ffd83dbSDimitry Andric Call.getArgSVal(1), Op); 3685ffd83dbSDimitry Andric return; 3695ffd83dbSDimitry Andric } else if (isRandomIncrOrDecrOperator(Op)) { 3705ffd83dbSDimitry Andric const auto *OrigExpr = Call.getOriginExpr(); 3715ffd83dbSDimitry Andric if (!OrigExpr) 3725ffd83dbSDimitry Andric return; 3735ffd83dbSDimitry Andric 3745ffd83dbSDimitry Andric if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { 3755ffd83dbSDimitry Andric if (Call.getNumArgs() >= 1 && 3765ffd83dbSDimitry Andric Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) { 3775ffd83dbSDimitry Andric handleRandomIncrOrDecr(C, OrigExpr, Op, Call.getReturnValue(), 3785ffd83dbSDimitry Andric InstCall->getCXXThisVal(), Call.getArgSVal(0)); 3795ffd83dbSDimitry Andric return; 3805ffd83dbSDimitry Andric } 381*e8d8bef9SDimitry Andric } else if (Call.getNumArgs() >= 2) { 382*e8d8bef9SDimitry Andric const Expr *FirstArg = Call.getArgExpr(0); 383*e8d8bef9SDimitry Andric const Expr *SecondArg = Call.getArgExpr(1); 384*e8d8bef9SDimitry Andric const QualType FirstType = FirstArg->getType(); 385*e8d8bef9SDimitry Andric const QualType SecondType = SecondArg->getType(); 386*e8d8bef9SDimitry Andric 387*e8d8bef9SDimitry Andric if (FirstType->isIntegralOrEnumerationType() || 388*e8d8bef9SDimitry Andric SecondType->isIntegralOrEnumerationType()) { 389*e8d8bef9SDimitry Andric // In case of operator+ the iterator can be either on the LHS (eg.: 390*e8d8bef9SDimitry Andric // it + 1), or on the RHS (eg.: 1 + it). Both cases are modeled. 391*e8d8bef9SDimitry Andric const bool IsIterFirst = FirstType->isStructureOrClassType(); 392*e8d8bef9SDimitry Andric const SVal FirstArg = Call.getArgSVal(0); 393*e8d8bef9SDimitry Andric const SVal SecondArg = Call.getArgSVal(1); 394*e8d8bef9SDimitry Andric const SVal &Iterator = IsIterFirst ? FirstArg : SecondArg; 395*e8d8bef9SDimitry Andric const SVal &Amount = IsIterFirst ? SecondArg : FirstArg; 396*e8d8bef9SDimitry Andric 3975ffd83dbSDimitry Andric handleRandomIncrOrDecr(C, OrigExpr, Op, Call.getReturnValue(), 398*e8d8bef9SDimitry Andric Iterator, Amount); 3995ffd83dbSDimitry Andric return; 4005ffd83dbSDimitry Andric } 4015ffd83dbSDimitry Andric } 4025ffd83dbSDimitry Andric } else if (isIncrementOperator(Op)) { 4035ffd83dbSDimitry Andric if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { 4045ffd83dbSDimitry Andric handleIncrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(), 4055ffd83dbSDimitry Andric Call.getNumArgs()); 4065ffd83dbSDimitry Andric return; 4075ffd83dbSDimitry Andric } 4085ffd83dbSDimitry Andric 4095ffd83dbSDimitry Andric handleIncrement(C, Call.getReturnValue(), Call.getArgSVal(0), 4105ffd83dbSDimitry Andric Call.getNumArgs()); 4115ffd83dbSDimitry Andric return; 4125ffd83dbSDimitry Andric } else if (isDecrementOperator(Op)) { 4135ffd83dbSDimitry Andric if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { 4145ffd83dbSDimitry Andric handleDecrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(), 4155ffd83dbSDimitry Andric Call.getNumArgs()); 4165ffd83dbSDimitry Andric return; 4175ffd83dbSDimitry Andric } 4185ffd83dbSDimitry Andric 4195ffd83dbSDimitry Andric handleDecrement(C, Call.getReturnValue(), Call.getArgSVal(0), 4205ffd83dbSDimitry Andric Call.getNumArgs()); 4215ffd83dbSDimitry Andric return; 422480093f4SDimitry Andric } 423480093f4SDimitry Andric } 424480093f4SDimitry Andric 4255ffd83dbSDimitry Andric void 4265ffd83dbSDimitry Andric IteratorModeling::handleAdvanceLikeFunction(CheckerContext &C, 4275ffd83dbSDimitry Andric const CallEvent &Call, 4285ffd83dbSDimitry Andric const Expr *OrigExpr, 4295ffd83dbSDimitry Andric const AdvanceFn *Handler) const { 4305ffd83dbSDimitry Andric if (!C.wasInlined) { 4315ffd83dbSDimitry Andric (this->**Handler)(C, OrigExpr, Call.getReturnValue(), 4325ffd83dbSDimitry Andric Call.getArgSVal(0), Call.getArgSVal(1)); 4335ffd83dbSDimitry Andric return; 4345ffd83dbSDimitry Andric } 4355ffd83dbSDimitry Andric 4365ffd83dbSDimitry Andric // If std::advance() was inlined, but a non-standard function it calls inside 4375ffd83dbSDimitry Andric // was not, then we have to model it explicitly 4385ffd83dbSDimitry Andric const auto *IdInfo = cast<FunctionDecl>(Call.getDecl())->getIdentifier(); 4395ffd83dbSDimitry Andric if (IdInfo) { 4405ffd83dbSDimitry Andric if (IdInfo->getName() == "advance") { 4415ffd83dbSDimitry Andric if (noChangeInAdvance(C, Call.getArgSVal(0), OrigExpr)) { 4425ffd83dbSDimitry Andric (this->**Handler)(C, OrigExpr, Call.getReturnValue(), 4435ffd83dbSDimitry Andric Call.getArgSVal(0), Call.getArgSVal(1)); 4445ffd83dbSDimitry Andric } 4455ffd83dbSDimitry Andric } 4465ffd83dbSDimitry Andric } 447480093f4SDimitry Andric } 448480093f4SDimitry Andric 449480093f4SDimitry Andric void IteratorModeling::handleComparison(CheckerContext &C, const Expr *CE, 450480093f4SDimitry Andric SVal RetVal, const SVal &LVal, 451480093f4SDimitry Andric const SVal &RVal, 452480093f4SDimitry Andric OverloadedOperatorKind Op) const { 453480093f4SDimitry Andric // Record the operands and the operator of the comparison for the next 454480093f4SDimitry Andric // evalAssume, if the result is a symbolic expression. If it is a concrete 455480093f4SDimitry Andric // value (only one branch is possible), then transfer the state between 456480093f4SDimitry Andric // the operands according to the operator and the result 457480093f4SDimitry Andric auto State = C.getState(); 458480093f4SDimitry Andric const auto *LPos = getIteratorPosition(State, LVal); 459480093f4SDimitry Andric const auto *RPos = getIteratorPosition(State, RVal); 460480093f4SDimitry Andric const MemRegion *Cont = nullptr; 461480093f4SDimitry Andric if (LPos) { 462480093f4SDimitry Andric Cont = LPos->getContainer(); 463480093f4SDimitry Andric } else if (RPos) { 464480093f4SDimitry Andric Cont = RPos->getContainer(); 465480093f4SDimitry Andric } 466480093f4SDimitry Andric if (!Cont) 467480093f4SDimitry Andric return; 468480093f4SDimitry Andric 4695ffd83dbSDimitry Andric // At least one of the iterators has recorded positions. If one of them does 470480093f4SDimitry Andric // not then create a new symbol for the offset. 471480093f4SDimitry Andric SymbolRef Sym; 472480093f4SDimitry Andric if (!LPos || !RPos) { 473480093f4SDimitry Andric auto &SymMgr = C.getSymbolManager(); 474480093f4SDimitry Andric Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(), 475480093f4SDimitry Andric C.getASTContext().LongTy, C.blockCount()); 476480093f4SDimitry Andric State = assumeNoOverflow(State, Sym, 4); 477480093f4SDimitry Andric } 478480093f4SDimitry Andric 479480093f4SDimitry Andric if (!LPos) { 480480093f4SDimitry Andric State = setIteratorPosition(State, LVal, 481480093f4SDimitry Andric IteratorPosition::getPosition(Cont, Sym)); 482480093f4SDimitry Andric LPos = getIteratorPosition(State, LVal); 483480093f4SDimitry Andric } else if (!RPos) { 484480093f4SDimitry Andric State = setIteratorPosition(State, RVal, 485480093f4SDimitry Andric IteratorPosition::getPosition(Cont, Sym)); 486480093f4SDimitry Andric RPos = getIteratorPosition(State, RVal); 487480093f4SDimitry Andric } 488480093f4SDimitry Andric 489*e8d8bef9SDimitry Andric // If the value for which we just tried to set a new iterator position is 490*e8d8bef9SDimitry Andric // an `SVal`for which no iterator position can be set then the setting was 491*e8d8bef9SDimitry Andric // unsuccessful. We cannot handle the comparison in this case. 492*e8d8bef9SDimitry Andric if (!LPos || !RPos) 493*e8d8bef9SDimitry Andric return; 494*e8d8bef9SDimitry Andric 4955ffd83dbSDimitry Andric // We cannot make assumptions on `UnknownVal`. Let us conjure a symbol 496480093f4SDimitry Andric // instead. 497480093f4SDimitry Andric if (RetVal.isUnknown()) { 498480093f4SDimitry Andric auto &SymMgr = C.getSymbolManager(); 499480093f4SDimitry Andric auto *LCtx = C.getLocationContext(); 500480093f4SDimitry Andric RetVal = nonloc::SymbolVal(SymMgr.conjureSymbol( 501480093f4SDimitry Andric CE, LCtx, C.getASTContext().BoolTy, C.blockCount())); 502480093f4SDimitry Andric State = State->BindExpr(CE, LCtx, RetVal); 503480093f4SDimitry Andric } 504480093f4SDimitry Andric 505480093f4SDimitry Andric processComparison(C, State, LPos->getOffset(), RPos->getOffset(), RetVal, Op); 506480093f4SDimitry Andric } 507480093f4SDimitry Andric 508480093f4SDimitry Andric void IteratorModeling::processComparison(CheckerContext &C, 509480093f4SDimitry Andric ProgramStateRef State, SymbolRef Sym1, 510480093f4SDimitry Andric SymbolRef Sym2, const SVal &RetVal, 511480093f4SDimitry Andric OverloadedOperatorKind Op) const { 512480093f4SDimitry Andric if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) { 513480093f4SDimitry Andric if ((State = relateSymbols(State, Sym1, Sym2, 514480093f4SDimitry Andric (Op == OO_EqualEqual) == 515480093f4SDimitry Andric (TruthVal->getValue() != 0)))) { 516480093f4SDimitry Andric C.addTransition(State); 517480093f4SDimitry Andric } else { 518480093f4SDimitry Andric C.generateSink(State, C.getPredecessor()); 519480093f4SDimitry Andric } 520480093f4SDimitry Andric return; 521480093f4SDimitry Andric } 522480093f4SDimitry Andric 523480093f4SDimitry Andric const auto ConditionVal = RetVal.getAs<DefinedSVal>(); 524480093f4SDimitry Andric if (!ConditionVal) 525480093f4SDimitry Andric return; 526480093f4SDimitry Andric 527480093f4SDimitry Andric if (auto StateTrue = relateSymbols(State, Sym1, Sym2, Op == OO_EqualEqual)) { 528480093f4SDimitry Andric StateTrue = StateTrue->assume(*ConditionVal, true); 529480093f4SDimitry Andric C.addTransition(StateTrue); 530480093f4SDimitry Andric } 531480093f4SDimitry Andric 532480093f4SDimitry Andric if (auto StateFalse = relateSymbols(State, Sym1, Sym2, Op != OO_EqualEqual)) { 533480093f4SDimitry Andric StateFalse = StateFalse->assume(*ConditionVal, false); 534480093f4SDimitry Andric C.addTransition(StateFalse); 535480093f4SDimitry Andric } 536480093f4SDimitry Andric } 537480093f4SDimitry Andric 538480093f4SDimitry Andric void IteratorModeling::handleIncrement(CheckerContext &C, const SVal &RetVal, 539480093f4SDimitry Andric const SVal &Iter, bool Postfix) const { 540480093f4SDimitry Andric // Increment the symbolic expressions which represents the position of the 541480093f4SDimitry Andric // iterator 542480093f4SDimitry Andric auto State = C.getState(); 543480093f4SDimitry Andric auto &BVF = C.getSymbolManager().getBasicVals(); 544480093f4SDimitry Andric 545480093f4SDimitry Andric const auto *Pos = getIteratorPosition(State, Iter); 546480093f4SDimitry Andric if (!Pos) 547480093f4SDimitry Andric return; 548480093f4SDimitry Andric 549480093f4SDimitry Andric auto NewState = 550480093f4SDimitry Andric advancePosition(State, Iter, OO_Plus, 551480093f4SDimitry Andric nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1)))); 552480093f4SDimitry Andric assert(NewState && 553480093f4SDimitry Andric "Advancing position by concrete int should always be successful"); 554480093f4SDimitry Andric 555480093f4SDimitry Andric const auto *NewPos = getIteratorPosition(NewState, Iter); 556480093f4SDimitry Andric assert(NewPos && 557480093f4SDimitry Andric "Iterator should have position after successful advancement"); 558480093f4SDimitry Andric 559480093f4SDimitry Andric State = setIteratorPosition(State, Iter, *NewPos); 560480093f4SDimitry Andric State = setIteratorPosition(State, RetVal, Postfix ? *Pos : *NewPos); 561480093f4SDimitry Andric C.addTransition(State); 562480093f4SDimitry Andric } 563480093f4SDimitry Andric 564480093f4SDimitry Andric void IteratorModeling::handleDecrement(CheckerContext &C, const SVal &RetVal, 565480093f4SDimitry Andric const SVal &Iter, bool Postfix) const { 566480093f4SDimitry Andric // Decrement the symbolic expressions which represents the position of the 567480093f4SDimitry Andric // iterator 568480093f4SDimitry Andric auto State = C.getState(); 569480093f4SDimitry Andric auto &BVF = C.getSymbolManager().getBasicVals(); 570480093f4SDimitry Andric 571480093f4SDimitry Andric const auto *Pos = getIteratorPosition(State, Iter); 572480093f4SDimitry Andric if (!Pos) 573480093f4SDimitry Andric return; 574480093f4SDimitry Andric 575480093f4SDimitry Andric auto NewState = 576480093f4SDimitry Andric advancePosition(State, Iter, OO_Minus, 577480093f4SDimitry Andric nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1)))); 578480093f4SDimitry Andric assert(NewState && 579480093f4SDimitry Andric "Advancing position by concrete int should always be successful"); 580480093f4SDimitry Andric 581480093f4SDimitry Andric const auto *NewPos = getIteratorPosition(NewState, Iter); 582480093f4SDimitry Andric assert(NewPos && 583480093f4SDimitry Andric "Iterator should have position after successful advancement"); 584480093f4SDimitry Andric 585480093f4SDimitry Andric State = setIteratorPosition(State, Iter, *NewPos); 586480093f4SDimitry Andric State = setIteratorPosition(State, RetVal, Postfix ? *Pos : *NewPos); 587480093f4SDimitry Andric C.addTransition(State); 588480093f4SDimitry Andric } 589480093f4SDimitry Andric 590*e8d8bef9SDimitry Andric void IteratorModeling::handleRandomIncrOrDecr(CheckerContext &C, const Expr *CE, 591480093f4SDimitry Andric OverloadedOperatorKind Op, 592480093f4SDimitry Andric const SVal &RetVal, 593*e8d8bef9SDimitry Andric const SVal &Iterator, 594*e8d8bef9SDimitry Andric const SVal &Amount) const { 595480093f4SDimitry Andric // Increment or decrement the symbolic expressions which represents the 596480093f4SDimitry Andric // position of the iterator 597480093f4SDimitry Andric auto State = C.getState(); 598480093f4SDimitry Andric 599*e8d8bef9SDimitry Andric const auto *Pos = getIteratorPosition(State, Iterator); 600480093f4SDimitry Andric if (!Pos) 601480093f4SDimitry Andric return; 602480093f4SDimitry Andric 603*e8d8bef9SDimitry Andric const auto *Value = &Amount; 604*e8d8bef9SDimitry Andric SVal Val; 605*e8d8bef9SDimitry Andric if (auto LocAmount = Amount.getAs<Loc>()) { 606*e8d8bef9SDimitry Andric Val = State->getRawSVal(*LocAmount); 607*e8d8bef9SDimitry Andric Value = &Val; 608480093f4SDimitry Andric } 609480093f4SDimitry Andric 610*e8d8bef9SDimitry Andric const auto &TgtVal = 611*e8d8bef9SDimitry Andric (Op == OO_PlusEqual || Op == OO_MinusEqual) ? Iterator : RetVal; 612480093f4SDimitry Andric 6135ffd83dbSDimitry Andric // `AdvancedState` is a state where the position of `LHS` is advanced. We 6145ffd83dbSDimitry Andric // only need this state to retrieve the new position, but we do not want 6155ffd83dbSDimitry Andric // to change the position of `LHS` (in every case). 616*e8d8bef9SDimitry Andric auto AdvancedState = advancePosition(State, Iterator, Op, *Value); 6175ffd83dbSDimitry Andric if (AdvancedState) { 618*e8d8bef9SDimitry Andric const auto *NewPos = getIteratorPosition(AdvancedState, Iterator); 619480093f4SDimitry Andric assert(NewPos && 620480093f4SDimitry Andric "Iterator should have position after successful advancement"); 621480093f4SDimitry Andric 6225ffd83dbSDimitry Andric State = setIteratorPosition(State, TgtVal, *NewPos); 623480093f4SDimitry Andric C.addTransition(State); 624480093f4SDimitry Andric } else { 625480093f4SDimitry Andric assignToContainer(C, CE, TgtVal, Pos->getContainer()); 626480093f4SDimitry Andric } 627480093f4SDimitry Andric } 628480093f4SDimitry Andric 6295ffd83dbSDimitry Andric void IteratorModeling::handlePtrIncrOrDecr(CheckerContext &C, 6305ffd83dbSDimitry Andric const Expr *Iterator, 6315ffd83dbSDimitry Andric OverloadedOperatorKind OK, 6325ffd83dbSDimitry Andric SVal Offset) const { 633*e8d8bef9SDimitry Andric if (!Offset.getAs<DefinedSVal>()) 634*e8d8bef9SDimitry Andric return; 635*e8d8bef9SDimitry Andric 6365ffd83dbSDimitry Andric QualType PtrType = Iterator->getType(); 6375ffd83dbSDimitry Andric if (!PtrType->isPointerType()) 6385ffd83dbSDimitry Andric return; 6395ffd83dbSDimitry Andric QualType ElementType = PtrType->getPointeeType(); 6405ffd83dbSDimitry Andric 6415ffd83dbSDimitry Andric ProgramStateRef State = C.getState(); 6425ffd83dbSDimitry Andric SVal OldVal = State->getSVal(Iterator, C.getLocationContext()); 6435ffd83dbSDimitry Andric 6445ffd83dbSDimitry Andric const IteratorPosition *OldPos = getIteratorPosition(State, OldVal); 6455ffd83dbSDimitry Andric if (!OldPos) 646480093f4SDimitry Andric return; 647480093f4SDimitry Andric 6485ffd83dbSDimitry Andric SVal NewVal; 649*e8d8bef9SDimitry Andric if (OK == OO_Plus || OK == OO_PlusEqual) { 6505ffd83dbSDimitry Andric NewVal = State->getLValue(ElementType, Offset, OldVal); 651*e8d8bef9SDimitry Andric } else { 652*e8d8bef9SDimitry Andric auto &SVB = C.getSValBuilder(); 653*e8d8bef9SDimitry Andric SVal NegatedOffset = SVB.evalMinus(Offset.castAs<NonLoc>()); 6545ffd83dbSDimitry Andric NewVal = State->getLValue(ElementType, NegatedOffset, OldVal); 655480093f4SDimitry Andric } 656480093f4SDimitry Andric 6575ffd83dbSDimitry Andric // `AdvancedState` is a state where the position of `Old` is advanced. We 6585ffd83dbSDimitry Andric // only need this state to retrieve the new position, but we do not want 6595ffd83dbSDimitry Andric // ever to change the position of `OldVal`. 6605ffd83dbSDimitry Andric auto AdvancedState = advancePosition(State, OldVal, OK, Offset); 6615ffd83dbSDimitry Andric if (AdvancedState) { 6625ffd83dbSDimitry Andric const IteratorPosition *NewPos = getIteratorPosition(AdvancedState, OldVal); 6635ffd83dbSDimitry Andric assert(NewPos && 6645ffd83dbSDimitry Andric "Iterator should have position after successful advancement"); 665480093f4SDimitry Andric 6665ffd83dbSDimitry Andric ProgramStateRef NewState = setIteratorPosition(State, NewVal, *NewPos); 6675ffd83dbSDimitry Andric C.addTransition(NewState); 6685ffd83dbSDimitry Andric } else { 6695ffd83dbSDimitry Andric assignToContainer(C, Iterator, NewVal, OldPos->getContainer()); 670480093f4SDimitry Andric } 6715ffd83dbSDimitry Andric } 6725ffd83dbSDimitry Andric 6735ffd83dbSDimitry Andric void IteratorModeling::handleAdvance(CheckerContext &C, const Expr *CE, 6745ffd83dbSDimitry Andric SVal RetVal, SVal Iter, 6755ffd83dbSDimitry Andric SVal Amount) const { 6765ffd83dbSDimitry Andric handleRandomIncrOrDecr(C, CE, OO_PlusEqual, RetVal, Iter, Amount); 6775ffd83dbSDimitry Andric } 6785ffd83dbSDimitry Andric 6795ffd83dbSDimitry Andric void IteratorModeling::handlePrev(CheckerContext &C, const Expr *CE, 6805ffd83dbSDimitry Andric SVal RetVal, SVal Iter, SVal Amount) const { 6815ffd83dbSDimitry Andric handleRandomIncrOrDecr(C, CE, OO_Minus, RetVal, Iter, Amount); 6825ffd83dbSDimitry Andric } 6835ffd83dbSDimitry Andric 6845ffd83dbSDimitry Andric void IteratorModeling::handleNext(CheckerContext &C, const Expr *CE, 6855ffd83dbSDimitry Andric SVal RetVal, SVal Iter, SVal Amount) const { 6865ffd83dbSDimitry Andric handleRandomIncrOrDecr(C, CE, OO_Plus, RetVal, Iter, Amount); 687480093f4SDimitry Andric } 688480093f4SDimitry Andric 689480093f4SDimitry Andric void IteratorModeling::assignToContainer(CheckerContext &C, const Expr *CE, 690480093f4SDimitry Andric const SVal &RetVal, 691480093f4SDimitry Andric const MemRegion *Cont) const { 692480093f4SDimitry Andric Cont = Cont->getMostDerivedObjectRegion(); 693480093f4SDimitry Andric 694480093f4SDimitry Andric auto State = C.getState(); 6955ffd83dbSDimitry Andric const auto *LCtx = C.getLocationContext(); 6965ffd83dbSDimitry Andric State = createIteratorPosition(State, RetVal, Cont, CE, LCtx, C.blockCount()); 6975ffd83dbSDimitry Andric 698480093f4SDimitry Andric C.addTransition(State); 699480093f4SDimitry Andric } 700480093f4SDimitry Andric 7015ffd83dbSDimitry Andric bool IteratorModeling::noChangeInAdvance(CheckerContext &C, SVal Iter, 7025ffd83dbSDimitry Andric const Expr *CE) const { 7035ffd83dbSDimitry Andric // Compare the iterator position before and after the call. (To be called 7045ffd83dbSDimitry Andric // from `checkPostCall()`.) 7055ffd83dbSDimitry Andric const auto StateAfter = C.getState(); 706480093f4SDimitry Andric 7075ffd83dbSDimitry Andric const auto *PosAfter = getIteratorPosition(StateAfter, Iter); 7085ffd83dbSDimitry Andric // If we have no position after the call of `std::advance`, then we are not 7095ffd83dbSDimitry Andric // interested. (Modeling of an inlined `std::advance()` should not remove the 7105ffd83dbSDimitry Andric // position in any case.) 7115ffd83dbSDimitry Andric if (!PosAfter) 7125ffd83dbSDimitry Andric return false; 713480093f4SDimitry Andric 7145ffd83dbSDimitry Andric const ExplodedNode *N = findCallEnter(C.getPredecessor(), CE); 7155ffd83dbSDimitry Andric assert(N && "Any call should have a `CallEnter` node."); 716480093f4SDimitry Andric 7175ffd83dbSDimitry Andric const auto StateBefore = N->getState(); 7185ffd83dbSDimitry Andric const auto *PosBefore = getIteratorPosition(StateBefore, Iter); 719*e8d8bef9SDimitry Andric // FIXME: `std::advance()` should not create a new iterator position but 720*e8d8bef9SDimitry Andric // change existing ones. However, in case of iterators implemented as 721*e8d8bef9SDimitry Andric // pointers the handling of parameters in `std::advance()`-like 722*e8d8bef9SDimitry Andric // functions is still incomplete which may result in cases where 723*e8d8bef9SDimitry Andric // the new position is assigned to the wrong pointer. This causes 724*e8d8bef9SDimitry Andric // crash if we use an assertion here. 725*e8d8bef9SDimitry Andric if (!PosBefore) 726*e8d8bef9SDimitry Andric return false; 727480093f4SDimitry Andric 7285ffd83dbSDimitry Andric return PosBefore->getOffset() == PosAfter->getOffset(); 729480093f4SDimitry Andric } 730480093f4SDimitry Andric 731480093f4SDimitry Andric void IteratorModeling::printState(raw_ostream &Out, ProgramStateRef State, 732480093f4SDimitry Andric const char *NL, const char *Sep) const { 733480093f4SDimitry Andric auto SymbolMap = State->get<IteratorSymbolMap>(); 734480093f4SDimitry Andric auto RegionMap = State->get<IteratorRegionMap>(); 7355ffd83dbSDimitry Andric // Use a counter to add newlines before every line except the first one. 7365ffd83dbSDimitry Andric unsigned Count = 0; 737480093f4SDimitry Andric 738480093f4SDimitry Andric if (!SymbolMap.isEmpty() || !RegionMap.isEmpty()) { 739480093f4SDimitry Andric Out << Sep << "Iterator Positions :" << NL; 740480093f4SDimitry Andric for (const auto &Sym : SymbolMap) { 7415ffd83dbSDimitry Andric if (Count++) 7425ffd83dbSDimitry Andric Out << NL; 7435ffd83dbSDimitry Andric 744480093f4SDimitry Andric Sym.first->dumpToStream(Out); 745480093f4SDimitry Andric Out << " : "; 746480093f4SDimitry Andric const auto Pos = Sym.second; 747480093f4SDimitry Andric Out << (Pos.isValid() ? "Valid" : "Invalid") << " ; Container == "; 748480093f4SDimitry Andric Pos.getContainer()->dumpToStream(Out); 749480093f4SDimitry Andric Out<<" ; Offset == "; 750480093f4SDimitry Andric Pos.getOffset()->dumpToStream(Out); 751480093f4SDimitry Andric } 752480093f4SDimitry Andric 753480093f4SDimitry Andric for (const auto &Reg : RegionMap) { 7545ffd83dbSDimitry Andric if (Count++) 7555ffd83dbSDimitry Andric Out << NL; 7565ffd83dbSDimitry Andric 757480093f4SDimitry Andric Reg.first->dumpToStream(Out); 758480093f4SDimitry Andric Out << " : "; 759480093f4SDimitry Andric const auto Pos = Reg.second; 760480093f4SDimitry Andric Out << (Pos.isValid() ? "Valid" : "Invalid") << " ; Container == "; 761480093f4SDimitry Andric Pos.getContainer()->dumpToStream(Out); 762480093f4SDimitry Andric Out<<" ; Offset == "; 763480093f4SDimitry Andric Pos.getOffset()->dumpToStream(Out); 764480093f4SDimitry Andric } 765480093f4SDimitry Andric } 766480093f4SDimitry Andric } 767480093f4SDimitry Andric 768480093f4SDimitry Andric namespace { 769480093f4SDimitry Andric 770480093f4SDimitry Andric bool isSimpleComparisonOperator(OverloadedOperatorKind OK) { 771480093f4SDimitry Andric return OK == OO_EqualEqual || OK == OO_ExclaimEqual; 772480093f4SDimitry Andric } 773480093f4SDimitry Andric 7745ffd83dbSDimitry Andric bool isSimpleComparisonOperator(BinaryOperatorKind OK) { 7755ffd83dbSDimitry Andric return OK == BO_EQ || OK == BO_NE; 776480093f4SDimitry Andric } 777480093f4SDimitry Andric 778480093f4SDimitry Andric ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val) { 779480093f4SDimitry Andric if (auto Reg = Val.getAsRegion()) { 780480093f4SDimitry Andric Reg = Reg->getMostDerivedObjectRegion(); 781480093f4SDimitry Andric return State->remove<IteratorRegionMap>(Reg); 782480093f4SDimitry Andric } else if (const auto Sym = Val.getAsSymbol()) { 783480093f4SDimitry Andric return State->remove<IteratorSymbolMap>(Sym); 784480093f4SDimitry Andric } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) { 785480093f4SDimitry Andric return State->remove<IteratorRegionMap>(LCVal->getRegion()); 786480093f4SDimitry Andric } 787480093f4SDimitry Andric return nullptr; 788480093f4SDimitry Andric } 789480093f4SDimitry Andric 790480093f4SDimitry Andric ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1, 791480093f4SDimitry Andric SymbolRef Sym2, bool Equal) { 792480093f4SDimitry Andric auto &SVB = State->getStateManager().getSValBuilder(); 793480093f4SDimitry Andric 794480093f4SDimitry Andric // FIXME: This code should be reworked as follows: 795480093f4SDimitry Andric // 1. Subtract the operands using evalBinOp(). 796480093f4SDimitry Andric // 2. Assume that the result doesn't overflow. 797480093f4SDimitry Andric // 3. Compare the result to 0. 798480093f4SDimitry Andric // 4. Assume the result of the comparison. 799480093f4SDimitry Andric const auto comparison = 800480093f4SDimitry Andric SVB.evalBinOp(State, BO_EQ, nonloc::SymbolVal(Sym1), 801480093f4SDimitry Andric nonloc::SymbolVal(Sym2), SVB.getConditionType()); 802480093f4SDimitry Andric 803480093f4SDimitry Andric assert(comparison.getAs<DefinedSVal>() && 804480093f4SDimitry Andric "Symbol comparison must be a `DefinedSVal`"); 805480093f4SDimitry Andric 806480093f4SDimitry Andric auto NewState = State->assume(comparison.castAs<DefinedSVal>(), Equal); 807480093f4SDimitry Andric if (!NewState) 808480093f4SDimitry Andric return nullptr; 809480093f4SDimitry Andric 810480093f4SDimitry Andric if (const auto CompSym = comparison.getAsSymbol()) { 811480093f4SDimitry Andric assert(isa<SymIntExpr>(CompSym) && 812480093f4SDimitry Andric "Symbol comparison must be a `SymIntExpr`"); 813480093f4SDimitry Andric assert(BinaryOperator::isComparisonOp( 814480093f4SDimitry Andric cast<SymIntExpr>(CompSym)->getOpcode()) && 815480093f4SDimitry Andric "Symbol comparison must be a comparison"); 816480093f4SDimitry Andric return assumeNoOverflow(NewState, cast<SymIntExpr>(CompSym)->getLHS(), 2); 817480093f4SDimitry Andric } 818480093f4SDimitry Andric 819480093f4SDimitry Andric return NewState; 820480093f4SDimitry Andric } 821480093f4SDimitry Andric 822480093f4SDimitry Andric bool isBoundThroughLazyCompoundVal(const Environment &Env, 823480093f4SDimitry Andric const MemRegion *Reg) { 824480093f4SDimitry Andric for (const auto &Binding : Env) { 825480093f4SDimitry Andric if (const auto LCVal = Binding.second.getAs<nonloc::LazyCompoundVal>()) { 826480093f4SDimitry Andric if (LCVal->getRegion() == Reg) 827480093f4SDimitry Andric return true; 828480093f4SDimitry Andric } 829480093f4SDimitry Andric } 830480093f4SDimitry Andric 831480093f4SDimitry Andric return false; 832480093f4SDimitry Andric } 833480093f4SDimitry Andric 8345ffd83dbSDimitry Andric const ExplodedNode *findCallEnter(const ExplodedNode *Node, const Expr *Call) { 8355ffd83dbSDimitry Andric while (Node) { 8365ffd83dbSDimitry Andric ProgramPoint PP = Node->getLocation(); 8375ffd83dbSDimitry Andric if (auto Enter = PP.getAs<CallEnter>()) { 8385ffd83dbSDimitry Andric if (Enter->getCallExpr() == Call) 8395ffd83dbSDimitry Andric break; 840480093f4SDimitry Andric } 841480093f4SDimitry Andric 8425ffd83dbSDimitry Andric Node = Node->getFirstPred(); 843480093f4SDimitry Andric } 844480093f4SDimitry Andric 8455ffd83dbSDimitry Andric return Node; 846480093f4SDimitry Andric } 847480093f4SDimitry Andric 848480093f4SDimitry Andric } // namespace 849480093f4SDimitry Andric 850480093f4SDimitry Andric void ento::registerIteratorModeling(CheckerManager &mgr) { 851480093f4SDimitry Andric mgr.registerChecker<IteratorModeling>(); 852480093f4SDimitry Andric } 853480093f4SDimitry Andric 8545ffd83dbSDimitry Andric bool ento::shouldRegisterIteratorModeling(const CheckerManager &mgr) { 855480093f4SDimitry Andric return true; 856480093f4SDimitry Andric } 857