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