1e5dd7070Spatrick //===-- IteratorModeling.cpp --------------------------------------*- C++ -*--// 2e5dd7070Spatrick // 3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information. 5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6e5dd7070Spatrick // 7e5dd7070Spatrick //===----------------------------------------------------------------------===// 8e5dd7070Spatrick // 9*ec727ea7Spatrick // Defines a modeling-checker for modeling STL iterator-like iterators. 10e5dd7070Spatrick // 11e5dd7070Spatrick //===----------------------------------------------------------------------===// 12e5dd7070Spatrick // 13e5dd7070Spatrick // In the code, iterator can be represented as a: 14e5dd7070Spatrick // * type-I: typedef-ed pointer. Operations over such iterator, such as 15e5dd7070Spatrick // comparisons or increments, are modeled straightforwardly by the 16e5dd7070Spatrick // analyzer. 17e5dd7070Spatrick // * type-II: structure with its method bodies available. Operations over such 18e5dd7070Spatrick // iterator are inlined by the analyzer, and results of modeling 19e5dd7070Spatrick // these operations are exposing implementation details of the 20e5dd7070Spatrick // iterators, which is not necessarily helping. 21e5dd7070Spatrick // * type-III: completely opaque structure. Operations over such iterator are 22e5dd7070Spatrick // modeled conservatively, producing conjured symbols everywhere. 23e5dd7070Spatrick // 24e5dd7070Spatrick // To handle all these types in a common way we introduce a structure called 25e5dd7070Spatrick // IteratorPosition which is an abstraction of the position the iterator 26e5dd7070Spatrick // represents using symbolic expressions. The checker handles all the 27e5dd7070Spatrick // operations on this structure. 28e5dd7070Spatrick // 29e5dd7070Spatrick // Additionally, depending on the circumstances, operators of types II and III 30e5dd7070Spatrick // can be represented as: 31e5dd7070Spatrick // * type-IIa, type-IIIa: conjured structure symbols - when returned by value 32e5dd7070Spatrick // from conservatively evaluated methods such as 33e5dd7070Spatrick // `.begin()`. 34e5dd7070Spatrick // * type-IIb, type-IIIb: memory regions of iterator-typed objects, such as 35e5dd7070Spatrick // variables or temporaries, when the iterator object is 36e5dd7070Spatrick // currently treated as an lvalue. 37e5dd7070Spatrick // * type-IIc, type-IIIc: compound values of iterator-typed objects, when the 38e5dd7070Spatrick // iterator object is treated as an rvalue taken of a 39e5dd7070Spatrick // particular lvalue, eg. a copy of "type-a" iterator 40e5dd7070Spatrick // object, or an iterator that existed before the 41e5dd7070Spatrick // analysis has started. 42e5dd7070Spatrick // 43e5dd7070Spatrick // To handle any of these three different representations stored in an SVal we 44e5dd7070Spatrick // use setter and getters functions which separate the three cases. To store 45e5dd7070Spatrick // them we use a pointer union of symbol and memory region. 46e5dd7070Spatrick // 47e5dd7070Spatrick // The checker works the following way: We record the begin and the 48e5dd7070Spatrick // past-end iterator for all containers whenever their `.begin()` and `.end()` 49e5dd7070Spatrick // are called. Since the Constraint Manager cannot handle such SVals we need 50e5dd7070Spatrick // to take over its role. We post-check equality and non-equality comparisons 51e5dd7070Spatrick // and record that the two sides are equal if we are in the 'equal' branch 52e5dd7070Spatrick // (true-branch for `==` and false-branch for `!=`). 53e5dd7070Spatrick // 54e5dd7070Spatrick // In case of type-I or type-II iterators we get a concrete integer as a result 55e5dd7070Spatrick // of the comparison (1 or 0) but in case of type-III we only get a Symbol. In 56e5dd7070Spatrick // this latter case we record the symbol and reload it in evalAssume() and do 57e5dd7070Spatrick // the propagation there. We also handle (maybe double) negated comparisons 58e5dd7070Spatrick // which are represented in the form of (x == 0 or x != 0) where x is the 59e5dd7070Spatrick // comparison itself. 60e5dd7070Spatrick // 61e5dd7070Spatrick // Since `SimpleConstraintManager` cannot handle complex symbolic expressions 62e5dd7070Spatrick // we only use expressions of the format S, S+n or S-n for iterator positions 63e5dd7070Spatrick // where S is a conjured symbol and n is an unsigned concrete integer. When 64e5dd7070Spatrick // making an assumption e.g. `S1 + n == S2 + m` we store `S1 - S2 == m - n` as 65e5dd7070Spatrick // a constraint which we later retrieve when doing an actual comparison. 66e5dd7070Spatrick 67e5dd7070Spatrick #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 68e5dd7070Spatrick #include "clang/AST/DeclTemplate.h" 69e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 70e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/Checker.h" 71e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 72e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 73e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" 74e5dd7070Spatrick 75e5dd7070Spatrick #include "Iterator.h" 76e5dd7070Spatrick 77e5dd7070Spatrick #include <utility> 78e5dd7070Spatrick 79e5dd7070Spatrick using namespace clang; 80e5dd7070Spatrick using namespace ento; 81e5dd7070Spatrick using namespace iterator; 82e5dd7070Spatrick 83e5dd7070Spatrick namespace { 84e5dd7070Spatrick 85e5dd7070Spatrick class IteratorModeling 86*ec727ea7Spatrick : public Checker<check::PostCall, check::PostStmt<UnaryOperator>, 87*ec727ea7Spatrick check::PostStmt<BinaryOperator>, 88*ec727ea7Spatrick check::PostStmt<MaterializeTemporaryExpr>, 89e5dd7070Spatrick check::Bind, check::LiveSymbols, check::DeadSymbols> { 90e5dd7070Spatrick 91*ec727ea7Spatrick using AdvanceFn = void (IteratorModeling::*)(CheckerContext &, const Expr *, 92*ec727ea7Spatrick SVal, SVal, SVal) const; 93*ec727ea7Spatrick 94*ec727ea7Spatrick void handleOverloadedOperator(CheckerContext &C, const CallEvent &Call, 95*ec727ea7Spatrick OverloadedOperatorKind Op) const; 96*ec727ea7Spatrick void handleAdvanceLikeFunction(CheckerContext &C, const CallEvent &Call, 97*ec727ea7Spatrick const Expr *OrigExpr, 98*ec727ea7Spatrick const AdvanceFn *Handler) const; 99*ec727ea7Spatrick 100e5dd7070Spatrick void handleComparison(CheckerContext &C, const Expr *CE, SVal RetVal, 101e5dd7070Spatrick const SVal &LVal, const SVal &RVal, 102e5dd7070Spatrick OverloadedOperatorKind Op) const; 103e5dd7070Spatrick void processComparison(CheckerContext &C, ProgramStateRef State, 104e5dd7070Spatrick SymbolRef Sym1, SymbolRef Sym2, const SVal &RetVal, 105e5dd7070Spatrick OverloadedOperatorKind Op) const; 106e5dd7070Spatrick void handleIncrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter, 107e5dd7070Spatrick bool Postfix) const; 108e5dd7070Spatrick void handleDecrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter, 109e5dd7070Spatrick bool Postfix) const; 110e5dd7070Spatrick void handleRandomIncrOrDecr(CheckerContext &C, const Expr *CE, 111e5dd7070Spatrick OverloadedOperatorKind Op, const SVal &RetVal, 112e5dd7070Spatrick const SVal &LHS, const SVal &RHS) const; 113*ec727ea7Spatrick void handlePtrIncrOrDecr(CheckerContext &C, const Expr *Iterator, 114*ec727ea7Spatrick OverloadedOperatorKind OK, SVal Offset) const; 115*ec727ea7Spatrick void handleAdvance(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter, 116*ec727ea7Spatrick SVal Amount) const; 117*ec727ea7Spatrick void handlePrev(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter, 118*ec727ea7Spatrick SVal Amount) const; 119*ec727ea7Spatrick void handleNext(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter, 120*ec727ea7Spatrick SVal Amount) const; 121e5dd7070Spatrick void assignToContainer(CheckerContext &C, const Expr *CE, const SVal &RetVal, 122e5dd7070Spatrick const MemRegion *Cont) const; 123*ec727ea7Spatrick bool noChangeInAdvance(CheckerContext &C, SVal Iter, const Expr *CE) const; 124e5dd7070Spatrick void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, 125e5dd7070Spatrick const char *Sep) const override; 126e5dd7070Spatrick 127*ec727ea7Spatrick // std::advance, std::prev & std::next 128*ec727ea7Spatrick CallDescriptionMap<AdvanceFn> AdvanceLikeFunctions = { 129*ec727ea7Spatrick // template<class InputIt, class Distance> 130*ec727ea7Spatrick // void advance(InputIt& it, Distance n); 131*ec727ea7Spatrick {{{"std", "advance"}, 2}, &IteratorModeling::handleAdvance}, 132*ec727ea7Spatrick 133*ec727ea7Spatrick // template<class BidirIt> 134*ec727ea7Spatrick // BidirIt prev( 135*ec727ea7Spatrick // BidirIt it, 136*ec727ea7Spatrick // typename std::iterator_traits<BidirIt>::difference_type n = 1); 137*ec727ea7Spatrick {{{"std", "prev"}, 2}, &IteratorModeling::handlePrev}, 138*ec727ea7Spatrick 139*ec727ea7Spatrick // template<class ForwardIt> 140*ec727ea7Spatrick // ForwardIt next( 141*ec727ea7Spatrick // ForwardIt it, 142*ec727ea7Spatrick // typename std::iterator_traits<ForwardIt>::difference_type n = 1); 143*ec727ea7Spatrick {{{"std", "next"}, 2}, &IteratorModeling::handleNext}, 144*ec727ea7Spatrick }; 145*ec727ea7Spatrick 146e5dd7070Spatrick public: 147*ec727ea7Spatrick IteratorModeling() = default; 148e5dd7070Spatrick 149e5dd7070Spatrick void checkPostCall(const CallEvent &Call, CheckerContext &C) const; 150e5dd7070Spatrick void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const; 151*ec727ea7Spatrick void checkPostStmt(const UnaryOperator *UO, CheckerContext &C) const; 152*ec727ea7Spatrick void checkPostStmt(const BinaryOperator *BO, CheckerContext &C) const; 153e5dd7070Spatrick void checkPostStmt(const CXXConstructExpr *CCE, CheckerContext &C) const; 154e5dd7070Spatrick void checkPostStmt(const DeclStmt *DS, CheckerContext &C) const; 155e5dd7070Spatrick void checkPostStmt(const MaterializeTemporaryExpr *MTE, 156e5dd7070Spatrick CheckerContext &C) const; 157e5dd7070Spatrick void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const; 158e5dd7070Spatrick void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; 159e5dd7070Spatrick }; 160e5dd7070Spatrick 161e5dd7070Spatrick bool isSimpleComparisonOperator(OverloadedOperatorKind OK); 162*ec727ea7Spatrick bool isSimpleComparisonOperator(BinaryOperatorKind OK); 163e5dd7070Spatrick ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val); 164e5dd7070Spatrick ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1, 165e5dd7070Spatrick SymbolRef Sym2, bool Equal); 166e5dd7070Spatrick bool isBoundThroughLazyCompoundVal(const Environment &Env, 167e5dd7070Spatrick const MemRegion *Reg); 168*ec727ea7Spatrick const ExplodedNode *findCallEnter(const ExplodedNode *Node, const Expr *Call); 169e5dd7070Spatrick 170e5dd7070Spatrick } // namespace 171e5dd7070Spatrick 172e5dd7070Spatrick void IteratorModeling::checkPostCall(const CallEvent &Call, 173e5dd7070Spatrick CheckerContext &C) const { 174e5dd7070Spatrick // Record new iterator positions and iterator position changes 175e5dd7070Spatrick const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); 176e5dd7070Spatrick if (!Func) 177e5dd7070Spatrick return; 178e5dd7070Spatrick 179e5dd7070Spatrick if (Func->isOverloadedOperator()) { 180e5dd7070Spatrick const auto Op = Func->getOverloadedOperator(); 181*ec727ea7Spatrick handleOverloadedOperator(C, Call, Op); 182e5dd7070Spatrick return; 183e5dd7070Spatrick } 184e5dd7070Spatrick 185e5dd7070Spatrick const auto *OrigExpr = Call.getOriginExpr(); 186e5dd7070Spatrick if (!OrigExpr) 187e5dd7070Spatrick return; 188e5dd7070Spatrick 189*ec727ea7Spatrick const AdvanceFn *Handler = AdvanceLikeFunctions.lookup(Call); 190*ec727ea7Spatrick if (Handler) { 191*ec727ea7Spatrick handleAdvanceLikeFunction(C, Call, OrigExpr, Handler); 192*ec727ea7Spatrick return; 193*ec727ea7Spatrick } 194*ec727ea7Spatrick 195e5dd7070Spatrick if (!isIteratorType(Call.getResultType())) 196e5dd7070Spatrick return; 197e5dd7070Spatrick 198e5dd7070Spatrick auto State = C.getState(); 199e5dd7070Spatrick 200e5dd7070Spatrick // Already bound to container? 201e5dd7070Spatrick if (getIteratorPosition(State, Call.getReturnValue())) 202e5dd7070Spatrick return; 203e5dd7070Spatrick 204e5dd7070Spatrick // Copy-like and move constructors 205e5dd7070Spatrick if (isa<CXXConstructorCall>(&Call) && Call.getNumArgs() == 1) { 206e5dd7070Spatrick if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(0))) { 207e5dd7070Spatrick State = setIteratorPosition(State, Call.getReturnValue(), *Pos); 208e5dd7070Spatrick if (cast<CXXConstructorDecl>(Func)->isMoveConstructor()) { 209e5dd7070Spatrick State = removeIteratorPosition(State, Call.getArgSVal(0)); 210e5dd7070Spatrick } 211e5dd7070Spatrick C.addTransition(State); 212e5dd7070Spatrick return; 213e5dd7070Spatrick } 214e5dd7070Spatrick } 215e5dd7070Spatrick 216e5dd7070Spatrick // Assumption: if return value is an iterator which is not yet bound to a 217*ec727ea7Spatrick // container, then look for the first iterator argument of the 218*ec727ea7Spatrick // same type as the return value and bind the return value to 219*ec727ea7Spatrick // the same container. This approach works for STL algorithms. 220e5dd7070Spatrick // FIXME: Add a more conservative mode 221e5dd7070Spatrick for (unsigned i = 0; i < Call.getNumArgs(); ++i) { 222*ec727ea7Spatrick if (isIteratorType(Call.getArgExpr(i)->getType()) && 223*ec727ea7Spatrick Call.getArgExpr(i)->getType().getNonReferenceType().getDesugaredType( 224*ec727ea7Spatrick C.getASTContext()).getTypePtr() == 225*ec727ea7Spatrick Call.getResultType().getDesugaredType(C.getASTContext()).getTypePtr()) { 226e5dd7070Spatrick if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(i))) { 227e5dd7070Spatrick assignToContainer(C, OrigExpr, Call.getReturnValue(), 228e5dd7070Spatrick Pos->getContainer()); 229e5dd7070Spatrick return; 230e5dd7070Spatrick } 231e5dd7070Spatrick } 232e5dd7070Spatrick } 233e5dd7070Spatrick } 234e5dd7070Spatrick 235e5dd7070Spatrick void IteratorModeling::checkBind(SVal Loc, SVal Val, const Stmt *S, 236e5dd7070Spatrick CheckerContext &C) const { 237e5dd7070Spatrick auto State = C.getState(); 238e5dd7070Spatrick const auto *Pos = getIteratorPosition(State, Val); 239e5dd7070Spatrick if (Pos) { 240e5dd7070Spatrick State = setIteratorPosition(State, Loc, *Pos); 241e5dd7070Spatrick C.addTransition(State); 242e5dd7070Spatrick } else { 243e5dd7070Spatrick const auto *OldPos = getIteratorPosition(State, Loc); 244e5dd7070Spatrick if (OldPos) { 245e5dd7070Spatrick State = removeIteratorPosition(State, Loc); 246e5dd7070Spatrick C.addTransition(State); 247e5dd7070Spatrick } 248e5dd7070Spatrick } 249e5dd7070Spatrick } 250e5dd7070Spatrick 251*ec727ea7Spatrick void IteratorModeling::checkPostStmt(const UnaryOperator *UO, 252*ec727ea7Spatrick CheckerContext &C) const { 253*ec727ea7Spatrick UnaryOperatorKind OK = UO->getOpcode(); 254*ec727ea7Spatrick if (!isIncrementOperator(OK) && !isDecrementOperator(OK)) 255*ec727ea7Spatrick return; 256*ec727ea7Spatrick 257*ec727ea7Spatrick auto &SVB = C.getSValBuilder(); 258*ec727ea7Spatrick handlePtrIncrOrDecr(C, UO->getSubExpr(), 259*ec727ea7Spatrick isIncrementOperator(OK) ? OO_Plus : OO_Minus, 260*ec727ea7Spatrick SVB.makeArrayIndex(1)); 261*ec727ea7Spatrick } 262*ec727ea7Spatrick 263*ec727ea7Spatrick void IteratorModeling::checkPostStmt(const BinaryOperator *BO, 264*ec727ea7Spatrick CheckerContext &C) const { 265*ec727ea7Spatrick ProgramStateRef State = C.getState(); 266*ec727ea7Spatrick BinaryOperatorKind OK = BO->getOpcode(); 267*ec727ea7Spatrick SVal RVal = State->getSVal(BO->getRHS(), C.getLocationContext()); 268*ec727ea7Spatrick 269*ec727ea7Spatrick if (isSimpleComparisonOperator(BO->getOpcode())) { 270*ec727ea7Spatrick SVal LVal = State->getSVal(BO->getLHS(), C.getLocationContext()); 271*ec727ea7Spatrick SVal Result = State->getSVal(BO, C.getLocationContext()); 272*ec727ea7Spatrick handleComparison(C, BO, Result, LVal, RVal, 273*ec727ea7Spatrick BinaryOperator::getOverloadedOperator(OK)); 274*ec727ea7Spatrick } else if (isRandomIncrOrDecrOperator(OK)) { 275*ec727ea7Spatrick handlePtrIncrOrDecr(C, BO->getLHS(), 276*ec727ea7Spatrick BinaryOperator::getOverloadedOperator(OK), RVal); 277*ec727ea7Spatrick } 278*ec727ea7Spatrick } 279*ec727ea7Spatrick 280e5dd7070Spatrick void IteratorModeling::checkPostStmt(const MaterializeTemporaryExpr *MTE, 281e5dd7070Spatrick CheckerContext &C) const { 282e5dd7070Spatrick /* Transfer iterator state to temporary objects */ 283e5dd7070Spatrick auto State = C.getState(); 284e5dd7070Spatrick const auto *Pos = getIteratorPosition(State, C.getSVal(MTE->getSubExpr())); 285e5dd7070Spatrick if (!Pos) 286e5dd7070Spatrick return; 287e5dd7070Spatrick State = setIteratorPosition(State, C.getSVal(MTE), *Pos); 288e5dd7070Spatrick C.addTransition(State); 289e5dd7070Spatrick } 290e5dd7070Spatrick 291e5dd7070Spatrick void IteratorModeling::checkLiveSymbols(ProgramStateRef State, 292e5dd7070Spatrick SymbolReaper &SR) const { 293*ec727ea7Spatrick // Keep symbolic expressions of iterator positions alive 294e5dd7070Spatrick auto RegionMap = State->get<IteratorRegionMap>(); 295e5dd7070Spatrick for (const auto &Reg : RegionMap) { 296e5dd7070Spatrick const auto Offset = Reg.second.getOffset(); 297e5dd7070Spatrick for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i) 298e5dd7070Spatrick if (isa<SymbolData>(*i)) 299e5dd7070Spatrick SR.markLive(*i); 300e5dd7070Spatrick } 301e5dd7070Spatrick 302e5dd7070Spatrick auto SymbolMap = State->get<IteratorSymbolMap>(); 303e5dd7070Spatrick for (const auto &Sym : SymbolMap) { 304e5dd7070Spatrick const auto Offset = Sym.second.getOffset(); 305e5dd7070Spatrick for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i) 306e5dd7070Spatrick if (isa<SymbolData>(*i)) 307e5dd7070Spatrick SR.markLive(*i); 308e5dd7070Spatrick } 309e5dd7070Spatrick 310e5dd7070Spatrick } 311e5dd7070Spatrick 312e5dd7070Spatrick void IteratorModeling::checkDeadSymbols(SymbolReaper &SR, 313e5dd7070Spatrick CheckerContext &C) const { 314e5dd7070Spatrick // Cleanup 315e5dd7070Spatrick auto State = C.getState(); 316e5dd7070Spatrick 317e5dd7070Spatrick auto RegionMap = State->get<IteratorRegionMap>(); 318e5dd7070Spatrick for (const auto &Reg : RegionMap) { 319e5dd7070Spatrick if (!SR.isLiveRegion(Reg.first)) { 320e5dd7070Spatrick // The region behind the `LazyCompoundVal` is often cleaned up before 321e5dd7070Spatrick // the `LazyCompoundVal` itself. If there are iterator positions keyed 322e5dd7070Spatrick // by these regions their cleanup must be deferred. 323e5dd7070Spatrick if (!isBoundThroughLazyCompoundVal(State->getEnvironment(), Reg.first)) { 324e5dd7070Spatrick State = State->remove<IteratorRegionMap>(Reg.first); 325e5dd7070Spatrick } 326e5dd7070Spatrick } 327e5dd7070Spatrick } 328e5dd7070Spatrick 329e5dd7070Spatrick auto SymbolMap = State->get<IteratorSymbolMap>(); 330e5dd7070Spatrick for (const auto &Sym : SymbolMap) { 331e5dd7070Spatrick if (!SR.isLive(Sym.first)) { 332e5dd7070Spatrick State = State->remove<IteratorSymbolMap>(Sym.first); 333e5dd7070Spatrick } 334e5dd7070Spatrick } 335e5dd7070Spatrick 336*ec727ea7Spatrick C.addTransition(State); 337e5dd7070Spatrick } 338*ec727ea7Spatrick 339*ec727ea7Spatrick void 340*ec727ea7Spatrick IteratorModeling::handleOverloadedOperator(CheckerContext &C, 341*ec727ea7Spatrick const CallEvent &Call, 342*ec727ea7Spatrick OverloadedOperatorKind Op) const { 343*ec727ea7Spatrick if (isSimpleComparisonOperator(Op)) { 344*ec727ea7Spatrick const auto *OrigExpr = Call.getOriginExpr(); 345*ec727ea7Spatrick if (!OrigExpr) 346*ec727ea7Spatrick return; 347*ec727ea7Spatrick 348*ec727ea7Spatrick if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { 349*ec727ea7Spatrick handleComparison(C, OrigExpr, Call.getReturnValue(), 350*ec727ea7Spatrick InstCall->getCXXThisVal(), Call.getArgSVal(0), Op); 351*ec727ea7Spatrick return; 352*ec727ea7Spatrick } 353*ec727ea7Spatrick 354*ec727ea7Spatrick handleComparison(C, OrigExpr, Call.getReturnValue(), Call.getArgSVal(0), 355*ec727ea7Spatrick Call.getArgSVal(1), Op); 356*ec727ea7Spatrick return; 357*ec727ea7Spatrick } else if (isRandomIncrOrDecrOperator(Op)) { 358*ec727ea7Spatrick const auto *OrigExpr = Call.getOriginExpr(); 359*ec727ea7Spatrick if (!OrigExpr) 360*ec727ea7Spatrick return; 361*ec727ea7Spatrick 362*ec727ea7Spatrick if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { 363*ec727ea7Spatrick if (Call.getNumArgs() >= 1 && 364*ec727ea7Spatrick Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) { 365*ec727ea7Spatrick handleRandomIncrOrDecr(C, OrigExpr, Op, Call.getReturnValue(), 366*ec727ea7Spatrick InstCall->getCXXThisVal(), Call.getArgSVal(0)); 367*ec727ea7Spatrick return; 368*ec727ea7Spatrick } 369*ec727ea7Spatrick } else { 370*ec727ea7Spatrick if (Call.getNumArgs() >= 2 && 371*ec727ea7Spatrick Call.getArgExpr(1)->getType()->isIntegralOrEnumerationType()) { 372*ec727ea7Spatrick handleRandomIncrOrDecr(C, OrigExpr, Op, Call.getReturnValue(), 373*ec727ea7Spatrick Call.getArgSVal(0), Call.getArgSVal(1)); 374*ec727ea7Spatrick return; 375*ec727ea7Spatrick } 376*ec727ea7Spatrick } 377*ec727ea7Spatrick } else if (isIncrementOperator(Op)) { 378*ec727ea7Spatrick if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { 379*ec727ea7Spatrick handleIncrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(), 380*ec727ea7Spatrick Call.getNumArgs()); 381*ec727ea7Spatrick return; 382*ec727ea7Spatrick } 383*ec727ea7Spatrick 384*ec727ea7Spatrick handleIncrement(C, Call.getReturnValue(), Call.getArgSVal(0), 385*ec727ea7Spatrick Call.getNumArgs()); 386*ec727ea7Spatrick return; 387*ec727ea7Spatrick } else if (isDecrementOperator(Op)) { 388*ec727ea7Spatrick if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { 389*ec727ea7Spatrick handleDecrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(), 390*ec727ea7Spatrick Call.getNumArgs()); 391*ec727ea7Spatrick return; 392*ec727ea7Spatrick } 393*ec727ea7Spatrick 394*ec727ea7Spatrick handleDecrement(C, Call.getReturnValue(), Call.getArgSVal(0), 395*ec727ea7Spatrick Call.getNumArgs()); 396*ec727ea7Spatrick return; 397e5dd7070Spatrick } 398e5dd7070Spatrick } 399e5dd7070Spatrick 400*ec727ea7Spatrick void 401*ec727ea7Spatrick IteratorModeling::handleAdvanceLikeFunction(CheckerContext &C, 402*ec727ea7Spatrick const CallEvent &Call, 403*ec727ea7Spatrick const Expr *OrigExpr, 404*ec727ea7Spatrick const AdvanceFn *Handler) const { 405*ec727ea7Spatrick if (!C.wasInlined) { 406*ec727ea7Spatrick (this->**Handler)(C, OrigExpr, Call.getReturnValue(), 407*ec727ea7Spatrick Call.getArgSVal(0), Call.getArgSVal(1)); 408*ec727ea7Spatrick return; 409*ec727ea7Spatrick } 410*ec727ea7Spatrick 411*ec727ea7Spatrick // If std::advance() was inlined, but a non-standard function it calls inside 412*ec727ea7Spatrick // was not, then we have to model it explicitly 413*ec727ea7Spatrick const auto *IdInfo = cast<FunctionDecl>(Call.getDecl())->getIdentifier(); 414*ec727ea7Spatrick if (IdInfo) { 415*ec727ea7Spatrick if (IdInfo->getName() == "advance") { 416*ec727ea7Spatrick if (noChangeInAdvance(C, Call.getArgSVal(0), OrigExpr)) { 417*ec727ea7Spatrick (this->**Handler)(C, OrigExpr, Call.getReturnValue(), 418*ec727ea7Spatrick Call.getArgSVal(0), Call.getArgSVal(1)); 419*ec727ea7Spatrick } 420*ec727ea7Spatrick } 421*ec727ea7Spatrick } 422e5dd7070Spatrick } 423e5dd7070Spatrick 424e5dd7070Spatrick void IteratorModeling::handleComparison(CheckerContext &C, const Expr *CE, 425e5dd7070Spatrick SVal RetVal, const SVal &LVal, 426e5dd7070Spatrick const SVal &RVal, 427e5dd7070Spatrick OverloadedOperatorKind Op) const { 428e5dd7070Spatrick // Record the operands and the operator of the comparison for the next 429e5dd7070Spatrick // evalAssume, if the result is a symbolic expression. If it is a concrete 430e5dd7070Spatrick // value (only one branch is possible), then transfer the state between 431e5dd7070Spatrick // the operands according to the operator and the result 432e5dd7070Spatrick auto State = C.getState(); 433e5dd7070Spatrick const auto *LPos = getIteratorPosition(State, LVal); 434e5dd7070Spatrick const auto *RPos = getIteratorPosition(State, RVal); 435e5dd7070Spatrick const MemRegion *Cont = nullptr; 436e5dd7070Spatrick if (LPos) { 437e5dd7070Spatrick Cont = LPos->getContainer(); 438e5dd7070Spatrick } else if (RPos) { 439e5dd7070Spatrick Cont = RPos->getContainer(); 440e5dd7070Spatrick } 441e5dd7070Spatrick if (!Cont) 442e5dd7070Spatrick return; 443e5dd7070Spatrick 444*ec727ea7Spatrick // At least one of the iterators has recorded positions. If one of them does 445e5dd7070Spatrick // not then create a new symbol for the offset. 446e5dd7070Spatrick SymbolRef Sym; 447e5dd7070Spatrick if (!LPos || !RPos) { 448e5dd7070Spatrick auto &SymMgr = C.getSymbolManager(); 449e5dd7070Spatrick Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(), 450e5dd7070Spatrick C.getASTContext().LongTy, C.blockCount()); 451e5dd7070Spatrick State = assumeNoOverflow(State, Sym, 4); 452e5dd7070Spatrick } 453e5dd7070Spatrick 454e5dd7070Spatrick if (!LPos) { 455e5dd7070Spatrick State = setIteratorPosition(State, LVal, 456e5dd7070Spatrick IteratorPosition::getPosition(Cont, Sym)); 457e5dd7070Spatrick LPos = getIteratorPosition(State, LVal); 458e5dd7070Spatrick } else if (!RPos) { 459e5dd7070Spatrick State = setIteratorPosition(State, RVal, 460e5dd7070Spatrick IteratorPosition::getPosition(Cont, Sym)); 461e5dd7070Spatrick RPos = getIteratorPosition(State, RVal); 462e5dd7070Spatrick } 463e5dd7070Spatrick 464*ec727ea7Spatrick // We cannot make assumptions on `UnknownVal`. Let us conjure a symbol 465e5dd7070Spatrick // instead. 466e5dd7070Spatrick if (RetVal.isUnknown()) { 467e5dd7070Spatrick auto &SymMgr = C.getSymbolManager(); 468e5dd7070Spatrick auto *LCtx = C.getLocationContext(); 469e5dd7070Spatrick RetVal = nonloc::SymbolVal(SymMgr.conjureSymbol( 470e5dd7070Spatrick CE, LCtx, C.getASTContext().BoolTy, C.blockCount())); 471e5dd7070Spatrick State = State->BindExpr(CE, LCtx, RetVal); 472e5dd7070Spatrick } 473e5dd7070Spatrick 474e5dd7070Spatrick processComparison(C, State, LPos->getOffset(), RPos->getOffset(), RetVal, Op); 475e5dd7070Spatrick } 476e5dd7070Spatrick 477e5dd7070Spatrick void IteratorModeling::processComparison(CheckerContext &C, 478e5dd7070Spatrick ProgramStateRef State, SymbolRef Sym1, 479e5dd7070Spatrick SymbolRef Sym2, const SVal &RetVal, 480e5dd7070Spatrick OverloadedOperatorKind Op) const { 481e5dd7070Spatrick if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) { 482e5dd7070Spatrick if ((State = relateSymbols(State, Sym1, Sym2, 483e5dd7070Spatrick (Op == OO_EqualEqual) == 484e5dd7070Spatrick (TruthVal->getValue() != 0)))) { 485e5dd7070Spatrick C.addTransition(State); 486e5dd7070Spatrick } else { 487e5dd7070Spatrick C.generateSink(State, C.getPredecessor()); 488e5dd7070Spatrick } 489e5dd7070Spatrick return; 490e5dd7070Spatrick } 491e5dd7070Spatrick 492e5dd7070Spatrick const auto ConditionVal = RetVal.getAs<DefinedSVal>(); 493e5dd7070Spatrick if (!ConditionVal) 494e5dd7070Spatrick return; 495e5dd7070Spatrick 496e5dd7070Spatrick if (auto StateTrue = relateSymbols(State, Sym1, Sym2, Op == OO_EqualEqual)) { 497e5dd7070Spatrick StateTrue = StateTrue->assume(*ConditionVal, true); 498e5dd7070Spatrick C.addTransition(StateTrue); 499e5dd7070Spatrick } 500e5dd7070Spatrick 501e5dd7070Spatrick if (auto StateFalse = relateSymbols(State, Sym1, Sym2, Op != OO_EqualEqual)) { 502e5dd7070Spatrick StateFalse = StateFalse->assume(*ConditionVal, false); 503e5dd7070Spatrick C.addTransition(StateFalse); 504e5dd7070Spatrick } 505e5dd7070Spatrick } 506e5dd7070Spatrick 507e5dd7070Spatrick void IteratorModeling::handleIncrement(CheckerContext &C, const SVal &RetVal, 508e5dd7070Spatrick const SVal &Iter, bool Postfix) const { 509e5dd7070Spatrick // Increment the symbolic expressions which represents the position of the 510e5dd7070Spatrick // iterator 511e5dd7070Spatrick auto State = C.getState(); 512e5dd7070Spatrick auto &BVF = C.getSymbolManager().getBasicVals(); 513e5dd7070Spatrick 514e5dd7070Spatrick const auto *Pos = getIteratorPosition(State, Iter); 515e5dd7070Spatrick if (!Pos) 516e5dd7070Spatrick return; 517e5dd7070Spatrick 518e5dd7070Spatrick auto NewState = 519e5dd7070Spatrick advancePosition(State, Iter, OO_Plus, 520e5dd7070Spatrick nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1)))); 521e5dd7070Spatrick assert(NewState && 522e5dd7070Spatrick "Advancing position by concrete int should always be successful"); 523e5dd7070Spatrick 524e5dd7070Spatrick const auto *NewPos = getIteratorPosition(NewState, Iter); 525e5dd7070Spatrick assert(NewPos && 526e5dd7070Spatrick "Iterator should have position after successful advancement"); 527e5dd7070Spatrick 528e5dd7070Spatrick State = setIteratorPosition(State, Iter, *NewPos); 529e5dd7070Spatrick State = setIteratorPosition(State, RetVal, Postfix ? *Pos : *NewPos); 530e5dd7070Spatrick C.addTransition(State); 531e5dd7070Spatrick } 532e5dd7070Spatrick 533e5dd7070Spatrick void IteratorModeling::handleDecrement(CheckerContext &C, const SVal &RetVal, 534e5dd7070Spatrick const SVal &Iter, bool Postfix) const { 535e5dd7070Spatrick // Decrement the symbolic expressions which represents the position of the 536e5dd7070Spatrick // iterator 537e5dd7070Spatrick auto State = C.getState(); 538e5dd7070Spatrick auto &BVF = C.getSymbolManager().getBasicVals(); 539e5dd7070Spatrick 540e5dd7070Spatrick const auto *Pos = getIteratorPosition(State, Iter); 541e5dd7070Spatrick if (!Pos) 542e5dd7070Spatrick return; 543e5dd7070Spatrick 544e5dd7070Spatrick auto NewState = 545e5dd7070Spatrick advancePosition(State, Iter, OO_Minus, 546e5dd7070Spatrick nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1)))); 547e5dd7070Spatrick assert(NewState && 548e5dd7070Spatrick "Advancing position by concrete int should always be successful"); 549e5dd7070Spatrick 550e5dd7070Spatrick const auto *NewPos = getIteratorPosition(NewState, Iter); 551e5dd7070Spatrick assert(NewPos && 552e5dd7070Spatrick "Iterator should have position after successful advancement"); 553e5dd7070Spatrick 554e5dd7070Spatrick State = setIteratorPosition(State, Iter, *NewPos); 555e5dd7070Spatrick State = setIteratorPosition(State, RetVal, Postfix ? *Pos : *NewPos); 556e5dd7070Spatrick C.addTransition(State); 557e5dd7070Spatrick } 558e5dd7070Spatrick 559e5dd7070Spatrick void IteratorModeling::handleRandomIncrOrDecr(CheckerContext &C, 560e5dd7070Spatrick const Expr *CE, 561e5dd7070Spatrick OverloadedOperatorKind Op, 562e5dd7070Spatrick const SVal &RetVal, 563e5dd7070Spatrick const SVal &LHS, 564e5dd7070Spatrick const SVal &RHS) const { 565e5dd7070Spatrick // Increment or decrement the symbolic expressions which represents the 566e5dd7070Spatrick // position of the iterator 567e5dd7070Spatrick auto State = C.getState(); 568e5dd7070Spatrick 569e5dd7070Spatrick const auto *Pos = getIteratorPosition(State, LHS); 570e5dd7070Spatrick if (!Pos) 571e5dd7070Spatrick return; 572e5dd7070Spatrick 573e5dd7070Spatrick const auto *value = &RHS; 574*ec727ea7Spatrick SVal val; 575e5dd7070Spatrick if (auto loc = RHS.getAs<Loc>()) { 576*ec727ea7Spatrick val = State->getRawSVal(*loc); 577e5dd7070Spatrick value = &val; 578e5dd7070Spatrick } 579e5dd7070Spatrick 580e5dd7070Spatrick auto &TgtVal = (Op == OO_PlusEqual || Op == OO_MinusEqual) ? LHS : RetVal; 581e5dd7070Spatrick 582*ec727ea7Spatrick // `AdvancedState` is a state where the position of `LHS` is advanced. We 583*ec727ea7Spatrick // only need this state to retrieve the new position, but we do not want 584*ec727ea7Spatrick // to change the position of `LHS` (in every case). 585*ec727ea7Spatrick auto AdvancedState = advancePosition(State, LHS, Op, *value); 586*ec727ea7Spatrick if (AdvancedState) { 587*ec727ea7Spatrick const auto *NewPos = getIteratorPosition(AdvancedState, LHS); 588e5dd7070Spatrick assert(NewPos && 589e5dd7070Spatrick "Iterator should have position after successful advancement"); 590e5dd7070Spatrick 591*ec727ea7Spatrick State = setIteratorPosition(State, TgtVal, *NewPos); 592e5dd7070Spatrick C.addTransition(State); 593e5dd7070Spatrick } else { 594e5dd7070Spatrick assignToContainer(C, CE, TgtVal, Pos->getContainer()); 595e5dd7070Spatrick } 596e5dd7070Spatrick } 597e5dd7070Spatrick 598*ec727ea7Spatrick void IteratorModeling::handlePtrIncrOrDecr(CheckerContext &C, 599*ec727ea7Spatrick const Expr *Iterator, 600*ec727ea7Spatrick OverloadedOperatorKind OK, 601*ec727ea7Spatrick SVal Offset) const { 602*ec727ea7Spatrick QualType PtrType = Iterator->getType(); 603*ec727ea7Spatrick if (!PtrType->isPointerType()) 604*ec727ea7Spatrick return; 605*ec727ea7Spatrick QualType ElementType = PtrType->getPointeeType(); 606*ec727ea7Spatrick 607*ec727ea7Spatrick ProgramStateRef State = C.getState(); 608*ec727ea7Spatrick SVal OldVal = State->getSVal(Iterator, C.getLocationContext()); 609*ec727ea7Spatrick 610*ec727ea7Spatrick const IteratorPosition *OldPos = getIteratorPosition(State, OldVal); 611*ec727ea7Spatrick if (!OldPos) 612e5dd7070Spatrick return; 613e5dd7070Spatrick 614*ec727ea7Spatrick SVal NewVal; 615*ec727ea7Spatrick if (OK == OO_Plus || OK == OO_PlusEqual) 616*ec727ea7Spatrick NewVal = State->getLValue(ElementType, Offset, OldVal); 617*ec727ea7Spatrick else { 618*ec727ea7Spatrick const llvm::APSInt &OffsetInt = 619*ec727ea7Spatrick Offset.castAs<nonloc::ConcreteInt>().getValue(); 620*ec727ea7Spatrick auto &BVF = C.getSymbolManager().getBasicVals(); 621*ec727ea7Spatrick SVal NegatedOffset = nonloc::ConcreteInt(BVF.getValue(-OffsetInt)); 622*ec727ea7Spatrick NewVal = State->getLValue(ElementType, NegatedOffset, OldVal); 623e5dd7070Spatrick } 624e5dd7070Spatrick 625*ec727ea7Spatrick // `AdvancedState` is a state where the position of `Old` is advanced. We 626*ec727ea7Spatrick // only need this state to retrieve the new position, but we do not want 627*ec727ea7Spatrick // ever to change the position of `OldVal`. 628*ec727ea7Spatrick auto AdvancedState = advancePosition(State, OldVal, OK, Offset); 629*ec727ea7Spatrick if (AdvancedState) { 630*ec727ea7Spatrick const IteratorPosition *NewPos = getIteratorPosition(AdvancedState, OldVal); 631*ec727ea7Spatrick assert(NewPos && 632*ec727ea7Spatrick "Iterator should have position after successful advancement"); 633e5dd7070Spatrick 634*ec727ea7Spatrick ProgramStateRef NewState = setIteratorPosition(State, NewVal, *NewPos); 635*ec727ea7Spatrick C.addTransition(NewState); 636*ec727ea7Spatrick } else { 637*ec727ea7Spatrick assignToContainer(C, Iterator, NewVal, OldPos->getContainer()); 638e5dd7070Spatrick } 639*ec727ea7Spatrick } 640*ec727ea7Spatrick 641*ec727ea7Spatrick void IteratorModeling::handleAdvance(CheckerContext &C, const Expr *CE, 642*ec727ea7Spatrick SVal RetVal, SVal Iter, 643*ec727ea7Spatrick SVal Amount) const { 644*ec727ea7Spatrick handleRandomIncrOrDecr(C, CE, OO_PlusEqual, RetVal, Iter, Amount); 645*ec727ea7Spatrick } 646*ec727ea7Spatrick 647*ec727ea7Spatrick void IteratorModeling::handlePrev(CheckerContext &C, const Expr *CE, 648*ec727ea7Spatrick SVal RetVal, SVal Iter, SVal Amount) const { 649*ec727ea7Spatrick handleRandomIncrOrDecr(C, CE, OO_Minus, RetVal, Iter, Amount); 650*ec727ea7Spatrick } 651*ec727ea7Spatrick 652*ec727ea7Spatrick void IteratorModeling::handleNext(CheckerContext &C, const Expr *CE, 653*ec727ea7Spatrick SVal RetVal, SVal Iter, SVal Amount) const { 654*ec727ea7Spatrick handleRandomIncrOrDecr(C, CE, OO_Plus, RetVal, Iter, Amount); 655e5dd7070Spatrick } 656e5dd7070Spatrick 657e5dd7070Spatrick void IteratorModeling::assignToContainer(CheckerContext &C, const Expr *CE, 658e5dd7070Spatrick const SVal &RetVal, 659e5dd7070Spatrick const MemRegion *Cont) const { 660e5dd7070Spatrick Cont = Cont->getMostDerivedObjectRegion(); 661e5dd7070Spatrick 662e5dd7070Spatrick auto State = C.getState(); 663*ec727ea7Spatrick const auto *LCtx = C.getLocationContext(); 664*ec727ea7Spatrick State = createIteratorPosition(State, RetVal, Cont, CE, LCtx, C.blockCount()); 665*ec727ea7Spatrick 666e5dd7070Spatrick C.addTransition(State); 667e5dd7070Spatrick } 668e5dd7070Spatrick 669*ec727ea7Spatrick bool IteratorModeling::noChangeInAdvance(CheckerContext &C, SVal Iter, 670*ec727ea7Spatrick const Expr *CE) const { 671*ec727ea7Spatrick // Compare the iterator position before and after the call. (To be called 672*ec727ea7Spatrick // from `checkPostCall()`.) 673*ec727ea7Spatrick const auto StateAfter = C.getState(); 674e5dd7070Spatrick 675*ec727ea7Spatrick const auto *PosAfter = getIteratorPosition(StateAfter, Iter); 676*ec727ea7Spatrick // If we have no position after the call of `std::advance`, then we are not 677*ec727ea7Spatrick // interested. (Modeling of an inlined `std::advance()` should not remove the 678*ec727ea7Spatrick // position in any case.) 679*ec727ea7Spatrick if (!PosAfter) 680*ec727ea7Spatrick return false; 681e5dd7070Spatrick 682*ec727ea7Spatrick const ExplodedNode *N = findCallEnter(C.getPredecessor(), CE); 683*ec727ea7Spatrick assert(N && "Any call should have a `CallEnter` node."); 684e5dd7070Spatrick 685*ec727ea7Spatrick const auto StateBefore = N->getState(); 686*ec727ea7Spatrick const auto *PosBefore = getIteratorPosition(StateBefore, Iter); 687e5dd7070Spatrick 688*ec727ea7Spatrick assert(PosBefore && "`std::advance() should not create new iterator " 689*ec727ea7Spatrick "position but change existing ones"); 690e5dd7070Spatrick 691*ec727ea7Spatrick return PosBefore->getOffset() == PosAfter->getOffset(); 692e5dd7070Spatrick } 693e5dd7070Spatrick 694e5dd7070Spatrick void IteratorModeling::printState(raw_ostream &Out, ProgramStateRef State, 695e5dd7070Spatrick const char *NL, const char *Sep) const { 696e5dd7070Spatrick auto SymbolMap = State->get<IteratorSymbolMap>(); 697e5dd7070Spatrick auto RegionMap = State->get<IteratorRegionMap>(); 698*ec727ea7Spatrick // Use a counter to add newlines before every line except the first one. 699*ec727ea7Spatrick unsigned Count = 0; 700e5dd7070Spatrick 701e5dd7070Spatrick if (!SymbolMap.isEmpty() || !RegionMap.isEmpty()) { 702e5dd7070Spatrick Out << Sep << "Iterator Positions :" << NL; 703e5dd7070Spatrick for (const auto &Sym : SymbolMap) { 704*ec727ea7Spatrick if (Count++) 705*ec727ea7Spatrick Out << NL; 706*ec727ea7Spatrick 707e5dd7070Spatrick Sym.first->dumpToStream(Out); 708e5dd7070Spatrick Out << " : "; 709e5dd7070Spatrick const auto Pos = Sym.second; 710e5dd7070Spatrick Out << (Pos.isValid() ? "Valid" : "Invalid") << " ; Container == "; 711e5dd7070Spatrick Pos.getContainer()->dumpToStream(Out); 712e5dd7070Spatrick Out<<" ; Offset == "; 713e5dd7070Spatrick Pos.getOffset()->dumpToStream(Out); 714e5dd7070Spatrick } 715e5dd7070Spatrick 716e5dd7070Spatrick for (const auto &Reg : RegionMap) { 717*ec727ea7Spatrick if (Count++) 718*ec727ea7Spatrick Out << NL; 719*ec727ea7Spatrick 720e5dd7070Spatrick Reg.first->dumpToStream(Out); 721e5dd7070Spatrick Out << " : "; 722e5dd7070Spatrick const auto Pos = Reg.second; 723e5dd7070Spatrick Out << (Pos.isValid() ? "Valid" : "Invalid") << " ; Container == "; 724e5dd7070Spatrick Pos.getContainer()->dumpToStream(Out); 725e5dd7070Spatrick Out<<" ; Offset == "; 726e5dd7070Spatrick Pos.getOffset()->dumpToStream(Out); 727e5dd7070Spatrick } 728e5dd7070Spatrick } 729e5dd7070Spatrick } 730e5dd7070Spatrick 731e5dd7070Spatrick namespace { 732e5dd7070Spatrick 733e5dd7070Spatrick bool isSimpleComparisonOperator(OverloadedOperatorKind OK) { 734e5dd7070Spatrick return OK == OO_EqualEqual || OK == OO_ExclaimEqual; 735e5dd7070Spatrick } 736e5dd7070Spatrick 737*ec727ea7Spatrick bool isSimpleComparisonOperator(BinaryOperatorKind OK) { 738*ec727ea7Spatrick return OK == BO_EQ || OK == BO_NE; 739e5dd7070Spatrick } 740e5dd7070Spatrick 741e5dd7070Spatrick ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val) { 742e5dd7070Spatrick if (auto Reg = Val.getAsRegion()) { 743e5dd7070Spatrick Reg = Reg->getMostDerivedObjectRegion(); 744e5dd7070Spatrick return State->remove<IteratorRegionMap>(Reg); 745e5dd7070Spatrick } else if (const auto Sym = Val.getAsSymbol()) { 746e5dd7070Spatrick return State->remove<IteratorSymbolMap>(Sym); 747e5dd7070Spatrick } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) { 748e5dd7070Spatrick return State->remove<IteratorRegionMap>(LCVal->getRegion()); 749e5dd7070Spatrick } 750e5dd7070Spatrick return nullptr; 751e5dd7070Spatrick } 752e5dd7070Spatrick 753e5dd7070Spatrick ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1, 754e5dd7070Spatrick SymbolRef Sym2, bool Equal) { 755e5dd7070Spatrick auto &SVB = State->getStateManager().getSValBuilder(); 756e5dd7070Spatrick 757e5dd7070Spatrick // FIXME: This code should be reworked as follows: 758e5dd7070Spatrick // 1. Subtract the operands using evalBinOp(). 759e5dd7070Spatrick // 2. Assume that the result doesn't overflow. 760e5dd7070Spatrick // 3. Compare the result to 0. 761e5dd7070Spatrick // 4. Assume the result of the comparison. 762e5dd7070Spatrick const auto comparison = 763e5dd7070Spatrick SVB.evalBinOp(State, BO_EQ, nonloc::SymbolVal(Sym1), 764e5dd7070Spatrick nonloc::SymbolVal(Sym2), SVB.getConditionType()); 765e5dd7070Spatrick 766e5dd7070Spatrick assert(comparison.getAs<DefinedSVal>() && 767e5dd7070Spatrick "Symbol comparison must be a `DefinedSVal`"); 768e5dd7070Spatrick 769e5dd7070Spatrick auto NewState = State->assume(comparison.castAs<DefinedSVal>(), Equal); 770e5dd7070Spatrick if (!NewState) 771e5dd7070Spatrick return nullptr; 772e5dd7070Spatrick 773e5dd7070Spatrick if (const auto CompSym = comparison.getAsSymbol()) { 774e5dd7070Spatrick assert(isa<SymIntExpr>(CompSym) && 775e5dd7070Spatrick "Symbol comparison must be a `SymIntExpr`"); 776e5dd7070Spatrick assert(BinaryOperator::isComparisonOp( 777e5dd7070Spatrick cast<SymIntExpr>(CompSym)->getOpcode()) && 778e5dd7070Spatrick "Symbol comparison must be a comparison"); 779e5dd7070Spatrick return assumeNoOverflow(NewState, cast<SymIntExpr>(CompSym)->getLHS(), 2); 780e5dd7070Spatrick } 781e5dd7070Spatrick 782e5dd7070Spatrick return NewState; 783e5dd7070Spatrick } 784e5dd7070Spatrick 785e5dd7070Spatrick bool isBoundThroughLazyCompoundVal(const Environment &Env, 786e5dd7070Spatrick const MemRegion *Reg) { 787e5dd7070Spatrick for (const auto &Binding : Env) { 788e5dd7070Spatrick if (const auto LCVal = Binding.second.getAs<nonloc::LazyCompoundVal>()) { 789e5dd7070Spatrick if (LCVal->getRegion() == Reg) 790e5dd7070Spatrick return true; 791e5dd7070Spatrick } 792e5dd7070Spatrick } 793e5dd7070Spatrick 794e5dd7070Spatrick return false; 795e5dd7070Spatrick } 796e5dd7070Spatrick 797*ec727ea7Spatrick const ExplodedNode *findCallEnter(const ExplodedNode *Node, const Expr *Call) { 798*ec727ea7Spatrick while (Node) { 799*ec727ea7Spatrick ProgramPoint PP = Node->getLocation(); 800*ec727ea7Spatrick if (auto Enter = PP.getAs<CallEnter>()) { 801*ec727ea7Spatrick if (Enter->getCallExpr() == Call) 802*ec727ea7Spatrick break; 803e5dd7070Spatrick } 804e5dd7070Spatrick 805*ec727ea7Spatrick Node = Node->getFirstPred(); 806e5dd7070Spatrick } 807e5dd7070Spatrick 808*ec727ea7Spatrick return Node; 809e5dd7070Spatrick } 810e5dd7070Spatrick 811e5dd7070Spatrick } // namespace 812e5dd7070Spatrick 813e5dd7070Spatrick void ento::registerIteratorModeling(CheckerManager &mgr) { 814e5dd7070Spatrick mgr.registerChecker<IteratorModeling>(); 815e5dd7070Spatrick } 816e5dd7070Spatrick 817*ec727ea7Spatrick bool ento::shouldRegisterIteratorModeling(const CheckerManager &mgr) { 818e5dd7070Spatrick return true; 819e5dd7070Spatrick } 820