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 checker for using iterators outside their range (past end). Usage 10 // means here dereferencing, incrementing etc. 11 // 12 //===----------------------------------------------------------------------===// 13 // 14 // In the code, iterator can be represented as a: 15 // * type-I: typedef-ed pointer. Operations over such iterator, such as 16 // comparisons or increments, are modeled straightforwardly by the 17 // analyzer. 18 // * type-II: structure with its method bodies available. Operations over such 19 // iterator are inlined by the analyzer, and results of modeling 20 // these operations are exposing implementation details of the 21 // iterators, which is not necessarily helping. 22 // * type-III: completely opaque structure. Operations over such iterator are 23 // modeled conservatively, producing conjured symbols everywhere. 24 // 25 // To handle all these types in a common way we introduce a structure called 26 // IteratorPosition which is an abstraction of the position the iterator 27 // represents using symbolic expressions. The checker handles all the 28 // operations on this structure. 29 // 30 // Additionally, depending on the circumstances, operators of types II and III 31 // can be represented as: 32 // * type-IIa, type-IIIa: conjured structure symbols - when returned by value 33 // from conservatively evaluated methods such as 34 // `.begin()`. 35 // * type-IIb, type-IIIb: memory regions of iterator-typed objects, such as 36 // variables or temporaries, when the iterator object is 37 // currently treated as an lvalue. 38 // * type-IIc, type-IIIc: compound values of iterator-typed objects, when the 39 // iterator object is treated as an rvalue taken of a 40 // particular lvalue, eg. a copy of "type-a" iterator 41 // object, or an iterator that existed before the 42 // analysis has started. 43 // 44 // To handle any of these three different representations stored in an SVal we 45 // use setter and getters functions which separate the three cases. To store 46 // them we use a pointer union of symbol and memory region. 47 // 48 // The checker works the following way: We record the begin and the 49 // past-end iterator for all containers whenever their `.begin()` and `.end()` 50 // are called. Since the Constraint Manager cannot handle such SVals we need 51 // to take over its role. We post-check equality and non-equality comparisons 52 // and record that the two sides are equal if we are in the 'equal' branch 53 // (true-branch for `==` and false-branch for `!=`). 54 // 55 // In case of type-I or type-II iterators we get a concrete integer as a result 56 // of the comparison (1 or 0) but in case of type-III we only get a Symbol. In 57 // this latter case we record the symbol and reload it in evalAssume() and do 58 // the propagation there. We also handle (maybe double) negated comparisons 59 // which are represented in the form of (x == 0 or x != 0) where x is the 60 // comparison itself. 61 // 62 // Since `SimpleConstraintManager` cannot handle complex symbolic expressions 63 // we only use expressions of the format S, S+n or S-n for iterator positions 64 // where S is a conjured symbol and n is an unsigned concrete integer. When 65 // making an assumption e.g. `S1 + n == S2 + m` we store `S1 - S2 == m - n` as 66 // a constraint which we later retrieve when doing an actual comparison. 67 68 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 69 #include "clang/AST/DeclTemplate.h" 70 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 71 #include "clang/StaticAnalyzer/Core/Checker.h" 72 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 73 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 74 #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" 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<MaterializeTemporaryExpr>, 88 check::Bind, check::LiveSymbols, check::DeadSymbols> { 89 90 void handleComparison(CheckerContext &C, const Expr *CE, const SVal &RetVal, 91 const SVal &LVal, const SVal &RVal, 92 OverloadedOperatorKind Op) const; 93 void processComparison(CheckerContext &C, ProgramStateRef State, 94 SymbolRef Sym1, SymbolRef Sym2, const SVal &RetVal, 95 OverloadedOperatorKind Op) const; 96 void handleIncrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter, 97 bool Postfix) const; 98 void handleDecrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter, 99 bool Postfix) const; 100 void handleRandomIncrOrDecr(CheckerContext &C, const Expr *CE, 101 OverloadedOperatorKind Op, const SVal &RetVal, 102 const SVal &LHS, const SVal &RHS) const; 103 void handleBegin(CheckerContext &C, const Expr *CE, const SVal &RetVal, 104 const SVal &Cont) const; 105 void handleEnd(CheckerContext &C, const Expr *CE, const SVal &RetVal, 106 const SVal &Cont) const; 107 void assignToContainer(CheckerContext &C, const Expr *CE, const SVal &RetVal, 108 const MemRegion *Cont) const; 109 void handleAssign(CheckerContext &C, const SVal &Cont, 110 const Expr *CE = nullptr, 111 const SVal &OldCont = UndefinedVal()) const; 112 void handleClear(CheckerContext &C, const SVal &Cont) const; 113 void handlePushBack(CheckerContext &C, const SVal &Cont) const; 114 void handlePopBack(CheckerContext &C, const SVal &Cont) const; 115 void handlePushFront(CheckerContext &C, const SVal &Cont) const; 116 void handlePopFront(CheckerContext &C, const SVal &Cont) const; 117 void handleInsert(CheckerContext &C, const SVal &Iter) const; 118 void handleErase(CheckerContext &C, const SVal &Iter) const; 119 void handleErase(CheckerContext &C, const SVal &Iter1, 120 const SVal &Iter2) const; 121 void handleEraseAfter(CheckerContext &C, const SVal &Iter) const; 122 void handleEraseAfter(CheckerContext &C, const SVal &Iter1, 123 const SVal &Iter2) const; 124 public: 125 IteratorModeling() {} 126 127 void checkPostCall(const CallEvent &Call, CheckerContext &C) const; 128 void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const; 129 void checkPostStmt(const CXXConstructExpr *CCE, CheckerContext &C) const; 130 void checkPostStmt(const DeclStmt *DS, CheckerContext &C) const; 131 void checkPostStmt(const MaterializeTemporaryExpr *MTE, 132 CheckerContext &C) const; 133 void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const; 134 void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; 135 }; 136 137 bool isBeginCall(const FunctionDecl *Func); 138 bool isEndCall(const FunctionDecl *Func); 139 bool isAssignCall(const FunctionDecl *Func); 140 bool isClearCall(const FunctionDecl *Func); 141 bool isPushBackCall(const FunctionDecl *Func); 142 bool isEmplaceBackCall(const FunctionDecl *Func); 143 bool isPopBackCall(const FunctionDecl *Func); 144 bool isPushFrontCall(const FunctionDecl *Func); 145 bool isEmplaceFrontCall(const FunctionDecl *Func); 146 bool isPopFrontCall(const FunctionDecl *Func); 147 bool isAssignmentOperator(OverloadedOperatorKind OK); 148 bool isSimpleComparisonOperator(OverloadedOperatorKind OK); 149 bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg); 150 bool frontModifiable(ProgramStateRef State, const MemRegion *Reg); 151 bool backModifiable(ProgramStateRef State, const MemRegion *Reg); 152 SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont); 153 SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont); 154 ProgramStateRef createContainerBegin(ProgramStateRef State, 155 const MemRegion *Cont, const Expr *E, 156 QualType T, const LocationContext *LCtx, 157 unsigned BlockCount); 158 ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont, 159 const Expr *E, QualType T, 160 const LocationContext *LCtx, 161 unsigned BlockCount); 162 ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont, 163 const ContainerData &CData); 164 ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val); 165 ProgramStateRef assumeNoOverflow(ProgramStateRef State, SymbolRef Sym, 166 long Scale); 167 ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State, 168 const MemRegion *Cont); 169 ProgramStateRef 170 invalidateAllIteratorPositionsExcept(ProgramStateRef State, 171 const MemRegion *Cont, SymbolRef Offset, 172 BinaryOperator::Opcode Opc); 173 ProgramStateRef invalidateIteratorPositions(ProgramStateRef State, 174 SymbolRef Offset, 175 BinaryOperator::Opcode Opc); 176 ProgramStateRef invalidateIteratorPositions(ProgramStateRef State, 177 SymbolRef Offset1, 178 BinaryOperator::Opcode Opc1, 179 SymbolRef Offset2, 180 BinaryOperator::Opcode Opc2); 181 ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State, 182 const MemRegion *Cont, 183 const MemRegion *NewCont); 184 ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State, 185 const MemRegion *Cont, 186 const MemRegion *NewCont, 187 SymbolRef Offset, 188 BinaryOperator::Opcode Opc); 189 ProgramStateRef rebaseSymbolInIteratorPositionsIf( 190 ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym, 191 SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc); 192 ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1, 193 SymbolRef Sym2, bool Equal); 194 SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB, SymbolRef Expr, 195 SymbolRef OldSym, SymbolRef NewSym); 196 bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont); 197 bool isBoundThroughLazyCompoundVal(const Environment &Env, 198 const MemRegion *Reg); 199 200 } // namespace 201 202 void IteratorModeling::checkPostCall(const CallEvent &Call, 203 CheckerContext &C) const { 204 // Record new iterator positions and iterator position changes 205 const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); 206 if (!Func) 207 return; 208 209 if (Func->isOverloadedOperator()) { 210 const auto Op = Func->getOverloadedOperator(); 211 if (isAssignmentOperator(Op)) { 212 // Overloaded 'operator=' must be a non-static member function. 213 const auto *InstCall = cast<CXXInstanceCall>(&Call); 214 if (cast<CXXMethodDecl>(Func)->isMoveAssignmentOperator()) { 215 handleAssign(C, InstCall->getCXXThisVal(), Call.getOriginExpr(), 216 Call.getArgSVal(0)); 217 return; 218 } 219 220 handleAssign(C, InstCall->getCXXThisVal()); 221 return; 222 } else if (isSimpleComparisonOperator(Op)) { 223 const auto *OrigExpr = Call.getOriginExpr(); 224 if (!OrigExpr) 225 return; 226 227 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { 228 handleComparison(C, OrigExpr, Call.getReturnValue(), 229 InstCall->getCXXThisVal(), Call.getArgSVal(0), Op); 230 return; 231 } 232 233 handleComparison(C, OrigExpr, Call.getReturnValue(), Call.getArgSVal(0), 234 Call.getArgSVal(1), Op); 235 return; 236 } else if (isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) { 237 const auto *OrigExpr = Call.getOriginExpr(); 238 if (!OrigExpr) 239 return; 240 241 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { 242 if (Call.getNumArgs() >= 1 && 243 Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) { 244 handleRandomIncrOrDecr(C, OrigExpr, Func->getOverloadedOperator(), 245 Call.getReturnValue(), 246 InstCall->getCXXThisVal(), Call.getArgSVal(0)); 247 return; 248 } 249 } else { 250 if (Call.getNumArgs() >= 2 && 251 Call.getArgExpr(1)->getType()->isIntegralOrEnumerationType()) { 252 handleRandomIncrOrDecr(C, OrigExpr, Func->getOverloadedOperator(), 253 Call.getReturnValue(), Call.getArgSVal(0), 254 Call.getArgSVal(1)); 255 return; 256 } 257 } 258 } else if (isIncrementOperator(Func->getOverloadedOperator())) { 259 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { 260 handleIncrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(), 261 Call.getNumArgs()); 262 return; 263 } 264 265 handleIncrement(C, Call.getReturnValue(), Call.getArgSVal(0), 266 Call.getNumArgs()); 267 return; 268 } else if (isDecrementOperator(Func->getOverloadedOperator())) { 269 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { 270 handleDecrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(), 271 Call.getNumArgs()); 272 return; 273 } 274 275 handleDecrement(C, Call.getReturnValue(), Call.getArgSVal(0), 276 Call.getNumArgs()); 277 return; 278 } 279 } else { 280 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { 281 if (isAssignCall(Func)) { 282 handleAssign(C, InstCall->getCXXThisVal()); 283 return; 284 } 285 286 if (isClearCall(Func)) { 287 handleClear(C, InstCall->getCXXThisVal()); 288 return; 289 } 290 291 if (isPushBackCall(Func) || isEmplaceBackCall(Func)) { 292 handlePushBack(C, InstCall->getCXXThisVal()); 293 return; 294 } 295 296 if (isPopBackCall(Func)) { 297 handlePopBack(C, InstCall->getCXXThisVal()); 298 return; 299 } 300 301 if (isPushFrontCall(Func) || isEmplaceFrontCall(Func)) { 302 handlePushFront(C, InstCall->getCXXThisVal()); 303 return; 304 } 305 306 if (isPopFrontCall(Func)) { 307 handlePopFront(C, InstCall->getCXXThisVal()); 308 return; 309 } 310 311 if (isInsertCall(Func) || isEmplaceCall(Func)) { 312 handleInsert(C, Call.getArgSVal(0)); 313 return; 314 } 315 316 if (isEraseCall(Func)) { 317 if (Call.getNumArgs() == 1) { 318 handleErase(C, Call.getArgSVal(0)); 319 return; 320 } 321 322 if (Call.getNumArgs() == 2) { 323 handleErase(C, Call.getArgSVal(0), Call.getArgSVal(1)); 324 return; 325 } 326 } 327 328 if (isEraseAfterCall(Func)) { 329 if (Call.getNumArgs() == 1) { 330 handleEraseAfter(C, Call.getArgSVal(0)); 331 return; 332 } 333 334 if (Call.getNumArgs() == 2) { 335 handleEraseAfter(C, Call.getArgSVal(0), Call.getArgSVal(1)); 336 return; 337 } 338 } 339 } 340 341 const auto *OrigExpr = Call.getOriginExpr(); 342 if (!OrigExpr) 343 return; 344 345 if (!isIteratorType(Call.getResultType())) 346 return; 347 348 auto State = C.getState(); 349 350 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { 351 if (isBeginCall(Func)) { 352 handleBegin(C, OrigExpr, Call.getReturnValue(), 353 InstCall->getCXXThisVal()); 354 return; 355 } 356 357 if (isEndCall(Func)) { 358 handleEnd(C, OrigExpr, Call.getReturnValue(), 359 InstCall->getCXXThisVal()); 360 return; 361 } 362 } 363 364 // Already bound to container? 365 if (getIteratorPosition(State, Call.getReturnValue())) 366 return; 367 368 // Copy-like and move constructors 369 if (isa<CXXConstructorCall>(&Call) && Call.getNumArgs() == 1) { 370 if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(0))) { 371 State = setIteratorPosition(State, Call.getReturnValue(), *Pos); 372 if (cast<CXXConstructorDecl>(Func)->isMoveConstructor()) { 373 State = removeIteratorPosition(State, Call.getArgSVal(0)); 374 } 375 C.addTransition(State); 376 return; 377 } 378 } 379 380 // Assumption: if return value is an iterator which is not yet bound to a 381 // container, then look for the first iterator argument, and 382 // bind the return value to the same container. This approach 383 // works for STL algorithms. 384 // FIXME: Add a more conservative mode 385 for (unsigned i = 0; i < Call.getNumArgs(); ++i) { 386 if (isIteratorType(Call.getArgExpr(i)->getType())) { 387 if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(i))) { 388 assignToContainer(C, OrigExpr, Call.getReturnValue(), 389 Pos->getContainer()); 390 return; 391 } 392 } 393 } 394 } 395 } 396 397 void IteratorModeling::checkBind(SVal Loc, SVal Val, const Stmt *S, 398 CheckerContext &C) const { 399 auto State = C.getState(); 400 const auto *Pos = getIteratorPosition(State, Val); 401 if (Pos) { 402 State = setIteratorPosition(State, Loc, *Pos); 403 C.addTransition(State); 404 } else { 405 const auto *OldPos = getIteratorPosition(State, Loc); 406 if (OldPos) { 407 State = removeIteratorPosition(State, Loc); 408 C.addTransition(State); 409 } 410 } 411 } 412 413 void IteratorModeling::checkPostStmt(const MaterializeTemporaryExpr *MTE, 414 CheckerContext &C) const { 415 /* Transfer iterator state to temporary objects */ 416 auto State = C.getState(); 417 const auto *Pos = getIteratorPosition(State, C.getSVal(MTE->getSubExpr())); 418 if (!Pos) 419 return; 420 State = setIteratorPosition(State, C.getSVal(MTE), *Pos); 421 C.addTransition(State); 422 } 423 424 void IteratorModeling::checkLiveSymbols(ProgramStateRef State, 425 SymbolReaper &SR) const { 426 // Keep symbolic expressions of iterator positions, container begins and ends 427 // alive 428 auto RegionMap = State->get<IteratorRegionMap>(); 429 for (const auto Reg : RegionMap) { 430 const auto Offset = Reg.second.getOffset(); 431 for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i) 432 if (isa<SymbolData>(*i)) 433 SR.markLive(*i); 434 } 435 436 auto SymbolMap = State->get<IteratorSymbolMap>(); 437 for (const auto Sym : SymbolMap) { 438 const auto Offset = Sym.second.getOffset(); 439 for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i) 440 if (isa<SymbolData>(*i)) 441 SR.markLive(*i); 442 } 443 444 auto ContMap = State->get<ContainerMap>(); 445 for (const auto Cont : ContMap) { 446 const auto CData = Cont.second; 447 if (CData.getBegin()) { 448 SR.markLive(CData.getBegin()); 449 if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getBegin())) 450 SR.markLive(SIE->getLHS()); 451 } 452 if (CData.getEnd()) { 453 SR.markLive(CData.getEnd()); 454 if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getEnd())) 455 SR.markLive(SIE->getLHS()); 456 } 457 } 458 } 459 460 void IteratorModeling::checkDeadSymbols(SymbolReaper &SR, 461 CheckerContext &C) const { 462 // Cleanup 463 auto State = C.getState(); 464 465 auto RegionMap = State->get<IteratorRegionMap>(); 466 for (const auto Reg : RegionMap) { 467 if (!SR.isLiveRegion(Reg.first)) { 468 // The region behind the `LazyCompoundVal` is often cleaned up before 469 // the `LazyCompoundVal` itself. If there are iterator positions keyed 470 // by these regions their cleanup must be deferred. 471 if (!isBoundThroughLazyCompoundVal(State->getEnvironment(), Reg.first)) { 472 State = State->remove<IteratorRegionMap>(Reg.first); 473 } 474 } 475 } 476 477 auto SymbolMap = State->get<IteratorSymbolMap>(); 478 for (const auto Sym : SymbolMap) { 479 if (!SR.isLive(Sym.first)) { 480 State = State->remove<IteratorSymbolMap>(Sym.first); 481 } 482 } 483 484 auto ContMap = State->get<ContainerMap>(); 485 for (const auto Cont : ContMap) { 486 if (!SR.isLiveRegion(Cont.first)) { 487 // We must keep the container data while it has live iterators to be able 488 // to compare them to the begin and the end of the container. 489 if (!hasLiveIterators(State, Cont.first)) { 490 State = State->remove<ContainerMap>(Cont.first); 491 } 492 } 493 } 494 495 C.addTransition(State); 496 } 497 498 void IteratorModeling::handleComparison(CheckerContext &C, const Expr *CE, 499 const SVal &RetVal, const SVal &LVal, 500 const SVal &RVal, 501 OverloadedOperatorKind Op) const { 502 // Record the operands and the operator of the comparison for the next 503 // evalAssume, if the result is a symbolic expression. If it is a concrete 504 // value (only one branch is possible), then transfer the state between 505 // the operands according to the operator and the result 506 auto State = C.getState(); 507 const auto *LPos = getIteratorPosition(State, LVal); 508 const auto *RPos = getIteratorPosition(State, RVal); 509 const MemRegion *Cont = nullptr; 510 if (LPos) { 511 Cont = LPos->getContainer(); 512 } else if (RPos) { 513 Cont = RPos->getContainer(); 514 } 515 if (!Cont) 516 return; 517 518 // At least one of the iterators have recorded positions. If one of them has 519 // not then create a new symbol for the offset. 520 SymbolRef Sym; 521 if (!LPos || !RPos) { 522 auto &SymMgr = C.getSymbolManager(); 523 Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(), 524 C.getASTContext().LongTy, C.blockCount()); 525 State = assumeNoOverflow(State, Sym, 4); 526 } 527 528 if (!LPos) { 529 State = setIteratorPosition(State, LVal, 530 IteratorPosition::getPosition(Cont, Sym)); 531 LPos = getIteratorPosition(State, LVal); 532 } else if (!RPos) { 533 State = setIteratorPosition(State, RVal, 534 IteratorPosition::getPosition(Cont, Sym)); 535 RPos = getIteratorPosition(State, RVal); 536 } 537 538 processComparison(C, State, LPos->getOffset(), RPos->getOffset(), RetVal, Op); 539 } 540 541 void IteratorModeling::processComparison(CheckerContext &C, 542 ProgramStateRef State, SymbolRef Sym1, 543 SymbolRef Sym2, const SVal &RetVal, 544 OverloadedOperatorKind Op) const { 545 if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) { 546 if ((State = relateSymbols(State, Sym1, Sym2, 547 (Op == OO_EqualEqual) == 548 (TruthVal->getValue() != 0)))) { 549 C.addTransition(State); 550 } else { 551 C.generateSink(State, C.getPredecessor()); 552 } 553 return; 554 } 555 556 const auto ConditionVal = RetVal.getAs<DefinedSVal>(); 557 if (!ConditionVal) 558 return; 559 560 if (auto StateTrue = relateSymbols(State, Sym1, Sym2, Op == OO_EqualEqual)) { 561 StateTrue = StateTrue->assume(*ConditionVal, true); 562 C.addTransition(StateTrue); 563 } 564 565 if (auto StateFalse = relateSymbols(State, Sym1, Sym2, Op != OO_EqualEqual)) { 566 StateFalse = StateFalse->assume(*ConditionVal, false); 567 C.addTransition(StateFalse); 568 } 569 } 570 571 void IteratorModeling::handleIncrement(CheckerContext &C, const SVal &RetVal, 572 const SVal &Iter, bool Postfix) const { 573 // Increment the symbolic expressions which represents the position of the 574 // iterator 575 auto State = C.getState(); 576 auto &BVF = C.getSymbolManager().getBasicVals(); 577 578 const auto *Pos = getIteratorPosition(State, Iter); 579 if (!Pos) 580 return; 581 582 auto NewState = 583 advancePosition(State, Iter, OO_Plus, 584 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1)))); 585 assert(NewState && 586 "Advancing position by concrete int should always be successful"); 587 588 const auto *NewPos = getIteratorPosition(NewState, Iter); 589 assert(NewPos && 590 "Iterator should have position after successful advancement"); 591 592 State = setIteratorPosition(State, Iter, *NewPos); 593 State = setIteratorPosition(State, RetVal, Postfix ? *Pos : *NewPos); 594 C.addTransition(State); 595 } 596 597 void IteratorModeling::handleDecrement(CheckerContext &C, const SVal &RetVal, 598 const SVal &Iter, bool Postfix) const { 599 // Decrement the symbolic expressions which represents the position of the 600 // iterator 601 auto State = C.getState(); 602 auto &BVF = C.getSymbolManager().getBasicVals(); 603 604 const auto *Pos = getIteratorPosition(State, Iter); 605 if (!Pos) 606 return; 607 608 auto NewState = 609 advancePosition(State, Iter, OO_Minus, 610 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1)))); 611 assert(NewState && 612 "Advancing position by concrete int should always be successful"); 613 614 const auto *NewPos = getIteratorPosition(NewState, Iter); 615 assert(NewPos && 616 "Iterator should have position after successful advancement"); 617 618 State = setIteratorPosition(State, Iter, *NewPos); 619 State = setIteratorPosition(State, RetVal, Postfix ? *Pos : *NewPos); 620 C.addTransition(State); 621 } 622 623 void IteratorModeling::handleRandomIncrOrDecr(CheckerContext &C, 624 const Expr *CE, 625 OverloadedOperatorKind Op, 626 const SVal &RetVal, 627 const SVal &LHS, 628 const SVal &RHS) const { 629 // Increment or decrement the symbolic expressions which represents the 630 // position of the iterator 631 auto State = C.getState(); 632 633 const auto *Pos = getIteratorPosition(State, LHS); 634 if (!Pos) 635 return; 636 637 const auto *value = &RHS; 638 if (auto loc = RHS.getAs<Loc>()) { 639 const auto val = State->getRawSVal(*loc); 640 value = &val; 641 } 642 643 auto &TgtVal = (Op == OO_PlusEqual || Op == OO_MinusEqual) ? LHS : RetVal; 644 645 auto NewState = 646 advancePosition(State, LHS, Op, *value); 647 if (NewState) { 648 const auto *NewPos = getIteratorPosition(NewState, LHS); 649 assert(NewPos && 650 "Iterator should have position after successful advancement"); 651 652 State = setIteratorPosition(NewState, TgtVal, *NewPos); 653 C.addTransition(State); 654 } else { 655 assignToContainer(C, CE, TgtVal, Pos->getContainer()); 656 } 657 } 658 659 void IteratorModeling::handleBegin(CheckerContext &C, const Expr *CE, 660 const SVal &RetVal, const SVal &Cont) const { 661 const auto *ContReg = Cont.getAsRegion(); 662 if (!ContReg) 663 return; 664 665 ContReg = ContReg->getMostDerivedObjectRegion(); 666 667 // If the container already has a begin symbol then use it. Otherwise first 668 // create a new one. 669 auto State = C.getState(); 670 auto BeginSym = getContainerBegin(State, ContReg); 671 if (!BeginSym) { 672 State = createContainerBegin(State, ContReg, CE, C.getASTContext().LongTy, 673 C.getLocationContext(), C.blockCount()); 674 BeginSym = getContainerBegin(State, ContReg); 675 } 676 State = setIteratorPosition(State, RetVal, 677 IteratorPosition::getPosition(ContReg, BeginSym)); 678 C.addTransition(State); 679 } 680 681 void IteratorModeling::handleEnd(CheckerContext &C, const Expr *CE, 682 const SVal &RetVal, const SVal &Cont) const { 683 const auto *ContReg = Cont.getAsRegion(); 684 if (!ContReg) 685 return; 686 687 ContReg = ContReg->getMostDerivedObjectRegion(); 688 689 // If the container already has an end symbol then use it. Otherwise first 690 // create a new one. 691 auto State = C.getState(); 692 auto EndSym = getContainerEnd(State, ContReg); 693 if (!EndSym) { 694 State = createContainerEnd(State, ContReg, CE, C.getASTContext().LongTy, 695 C.getLocationContext(), C.blockCount()); 696 EndSym = getContainerEnd(State, ContReg); 697 } 698 State = setIteratorPosition(State, RetVal, 699 IteratorPosition::getPosition(ContReg, EndSym)); 700 C.addTransition(State); 701 } 702 703 void IteratorModeling::assignToContainer(CheckerContext &C, const Expr *CE, 704 const SVal &RetVal, 705 const MemRegion *Cont) const { 706 Cont = Cont->getMostDerivedObjectRegion(); 707 708 auto State = C.getState(); 709 auto &SymMgr = C.getSymbolManager(); 710 auto Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(), 711 C.getASTContext().LongTy, C.blockCount()); 712 State = assumeNoOverflow(State, Sym, 4); 713 State = setIteratorPosition(State, RetVal, 714 IteratorPosition::getPosition(Cont, Sym)); 715 C.addTransition(State); 716 } 717 718 void IteratorModeling::handleAssign(CheckerContext &C, const SVal &Cont, 719 const Expr *CE, const SVal &OldCont) const { 720 const auto *ContReg = Cont.getAsRegion(); 721 if (!ContReg) 722 return; 723 724 ContReg = ContReg->getMostDerivedObjectRegion(); 725 726 // Assignment of a new value to a container always invalidates all its 727 // iterators 728 auto State = C.getState(); 729 const auto CData = getContainerData(State, ContReg); 730 if (CData) { 731 State = invalidateAllIteratorPositions(State, ContReg); 732 } 733 734 // In case of move, iterators of the old container (except the past-end 735 // iterators) remain valid but refer to the new container 736 if (!OldCont.isUndef()) { 737 const auto *OldContReg = OldCont.getAsRegion(); 738 if (OldContReg) { 739 OldContReg = OldContReg->getMostDerivedObjectRegion(); 740 const auto OldCData = getContainerData(State, OldContReg); 741 if (OldCData) { 742 if (const auto OldEndSym = OldCData->getEnd()) { 743 // If we already assigned an "end" symbol to the old container, then 744 // first reassign all iterator positions to the new container which 745 // are not past the container (thus not greater or equal to the 746 // current "end" symbol). 747 State = reassignAllIteratorPositionsUnless(State, OldContReg, ContReg, 748 OldEndSym, BO_GE); 749 auto &SymMgr = C.getSymbolManager(); 750 auto &SVB = C.getSValBuilder(); 751 // Then generate and assign a new "end" symbol for the new container. 752 auto NewEndSym = 753 SymMgr.conjureSymbol(CE, C.getLocationContext(), 754 C.getASTContext().LongTy, C.blockCount()); 755 State = assumeNoOverflow(State, NewEndSym, 4); 756 if (CData) { 757 State = setContainerData(State, ContReg, CData->newEnd(NewEndSym)); 758 } else { 759 State = setContainerData(State, ContReg, 760 ContainerData::fromEnd(NewEndSym)); 761 } 762 // Finally, replace the old "end" symbol in the already reassigned 763 // iterator positions with the new "end" symbol. 764 State = rebaseSymbolInIteratorPositionsIf( 765 State, SVB, OldEndSym, NewEndSym, OldEndSym, BO_LT); 766 } else { 767 // There was no "end" symbol assigned yet to the old container, 768 // so reassign all iterator positions to the new container. 769 State = reassignAllIteratorPositions(State, OldContReg, ContReg); 770 } 771 if (const auto OldBeginSym = OldCData->getBegin()) { 772 // If we already assigned a "begin" symbol to the old container, then 773 // assign it to the new container and remove it from the old one. 774 if (CData) { 775 State = 776 setContainerData(State, ContReg, CData->newBegin(OldBeginSym)); 777 } else { 778 State = setContainerData(State, ContReg, 779 ContainerData::fromBegin(OldBeginSym)); 780 } 781 State = 782 setContainerData(State, OldContReg, OldCData->newEnd(nullptr)); 783 } 784 } else { 785 // There was neither "begin" nor "end" symbol assigned yet to the old 786 // container, so reassign all iterator positions to the new container. 787 State = reassignAllIteratorPositions(State, OldContReg, ContReg); 788 } 789 } 790 } 791 C.addTransition(State); 792 } 793 794 void IteratorModeling::handleClear(CheckerContext &C, const SVal &Cont) const { 795 const auto *ContReg = Cont.getAsRegion(); 796 if (!ContReg) 797 return; 798 799 ContReg = ContReg->getMostDerivedObjectRegion(); 800 801 // The clear() operation invalidates all the iterators, except the past-end 802 // iterators of list-like containers 803 auto State = C.getState(); 804 if (!hasSubscriptOperator(State, ContReg) || 805 !backModifiable(State, ContReg)) { 806 const auto CData = getContainerData(State, ContReg); 807 if (CData) { 808 if (const auto EndSym = CData->getEnd()) { 809 State = 810 invalidateAllIteratorPositionsExcept(State, ContReg, EndSym, BO_GE); 811 C.addTransition(State); 812 return; 813 } 814 } 815 } 816 State = invalidateAllIteratorPositions(State, ContReg); 817 C.addTransition(State); 818 } 819 820 void IteratorModeling::handlePushBack(CheckerContext &C, 821 const SVal &Cont) const { 822 const auto *ContReg = Cont.getAsRegion(); 823 if (!ContReg) 824 return; 825 826 ContReg = ContReg->getMostDerivedObjectRegion(); 827 828 // For deque-like containers invalidate all iterator positions 829 auto State = C.getState(); 830 if (hasSubscriptOperator(State, ContReg) && frontModifiable(State, ContReg)) { 831 State = invalidateAllIteratorPositions(State, ContReg); 832 C.addTransition(State); 833 return; 834 } 835 836 const auto CData = getContainerData(State, ContReg); 837 if (!CData) 838 return; 839 840 // For vector-like containers invalidate the past-end iterator positions 841 if (const auto EndSym = CData->getEnd()) { 842 if (hasSubscriptOperator(State, ContReg)) { 843 State = invalidateIteratorPositions(State, EndSym, BO_GE); 844 } 845 auto &SymMgr = C.getSymbolManager(); 846 auto &BVF = SymMgr.getBasicVals(); 847 auto &SVB = C.getSValBuilder(); 848 const auto newEndSym = 849 SVB.evalBinOp(State, BO_Add, 850 nonloc::SymbolVal(EndSym), 851 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))), 852 SymMgr.getType(EndSym)).getAsSymbol(); 853 State = setContainerData(State, ContReg, CData->newEnd(newEndSym)); 854 } 855 C.addTransition(State); 856 } 857 858 void IteratorModeling::handlePopBack(CheckerContext &C, 859 const SVal &Cont) const { 860 const auto *ContReg = Cont.getAsRegion(); 861 if (!ContReg) 862 return; 863 864 ContReg = ContReg->getMostDerivedObjectRegion(); 865 866 auto State = C.getState(); 867 const auto CData = getContainerData(State, ContReg); 868 if (!CData) 869 return; 870 871 if (const auto EndSym = CData->getEnd()) { 872 auto &SymMgr = C.getSymbolManager(); 873 auto &BVF = SymMgr.getBasicVals(); 874 auto &SVB = C.getSValBuilder(); 875 const auto BackSym = 876 SVB.evalBinOp(State, BO_Sub, 877 nonloc::SymbolVal(EndSym), 878 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))), 879 SymMgr.getType(EndSym)).getAsSymbol(); 880 // For vector-like and deque-like containers invalidate the last and the 881 // past-end iterator positions. For list-like containers only invalidate 882 // the last position 883 if (hasSubscriptOperator(State, ContReg) && 884 backModifiable(State, ContReg)) { 885 State = invalidateIteratorPositions(State, BackSym, BO_GE); 886 State = setContainerData(State, ContReg, CData->newEnd(nullptr)); 887 } else { 888 State = invalidateIteratorPositions(State, BackSym, BO_EQ); 889 } 890 auto newEndSym = BackSym; 891 State = setContainerData(State, ContReg, CData->newEnd(newEndSym)); 892 C.addTransition(State); 893 } 894 } 895 896 void IteratorModeling::handlePushFront(CheckerContext &C, 897 const SVal &Cont) const { 898 const auto *ContReg = Cont.getAsRegion(); 899 if (!ContReg) 900 return; 901 902 ContReg = ContReg->getMostDerivedObjectRegion(); 903 904 // For deque-like containers invalidate all iterator positions 905 auto State = C.getState(); 906 if (hasSubscriptOperator(State, ContReg)) { 907 State = invalidateAllIteratorPositions(State, ContReg); 908 C.addTransition(State); 909 } else { 910 const auto CData = getContainerData(State, ContReg); 911 if (!CData) 912 return; 913 914 if (const auto BeginSym = CData->getBegin()) { 915 auto &SymMgr = C.getSymbolManager(); 916 auto &BVF = SymMgr.getBasicVals(); 917 auto &SVB = C.getSValBuilder(); 918 const auto newBeginSym = 919 SVB.evalBinOp(State, BO_Sub, 920 nonloc::SymbolVal(BeginSym), 921 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))), 922 SymMgr.getType(BeginSym)).getAsSymbol(); 923 State = setContainerData(State, ContReg, CData->newBegin(newBeginSym)); 924 C.addTransition(State); 925 } 926 } 927 } 928 929 void IteratorModeling::handlePopFront(CheckerContext &C, 930 const SVal &Cont) const { 931 const auto *ContReg = Cont.getAsRegion(); 932 if (!ContReg) 933 return; 934 935 ContReg = ContReg->getMostDerivedObjectRegion(); 936 937 auto State = C.getState(); 938 const auto CData = getContainerData(State, ContReg); 939 if (!CData) 940 return; 941 942 // For deque-like containers invalidate all iterator positions. For list-like 943 // iterators only invalidate the first position 944 if (const auto BeginSym = CData->getBegin()) { 945 if (hasSubscriptOperator(State, ContReg)) { 946 State = invalidateIteratorPositions(State, BeginSym, BO_LE); 947 } else { 948 State = invalidateIteratorPositions(State, BeginSym, BO_EQ); 949 } 950 auto &SymMgr = C.getSymbolManager(); 951 auto &BVF = SymMgr.getBasicVals(); 952 auto &SVB = C.getSValBuilder(); 953 const auto newBeginSym = 954 SVB.evalBinOp(State, BO_Add, 955 nonloc::SymbolVal(BeginSym), 956 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))), 957 SymMgr.getType(BeginSym)).getAsSymbol(); 958 State = setContainerData(State, ContReg, CData->newBegin(newBeginSym)); 959 C.addTransition(State); 960 } 961 } 962 963 void IteratorModeling::handleInsert(CheckerContext &C, const SVal &Iter) const { 964 auto State = C.getState(); 965 const auto *Pos = getIteratorPosition(State, Iter); 966 if (!Pos) 967 return; 968 969 // For deque-like containers invalidate all iterator positions. For 970 // vector-like containers invalidate iterator positions after the insertion. 971 const auto *Cont = Pos->getContainer(); 972 if (hasSubscriptOperator(State, Cont) && backModifiable(State, Cont)) { 973 if (frontModifiable(State, Cont)) { 974 State = invalidateAllIteratorPositions(State, Cont); 975 } else { 976 State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE); 977 } 978 if (const auto *CData = getContainerData(State, Cont)) { 979 if (const auto EndSym = CData->getEnd()) { 980 State = invalidateIteratorPositions(State, EndSym, BO_GE); 981 State = setContainerData(State, Cont, CData->newEnd(nullptr)); 982 } 983 } 984 C.addTransition(State); 985 } 986 } 987 988 void IteratorModeling::handleErase(CheckerContext &C, const SVal &Iter) const { 989 auto State = C.getState(); 990 const auto *Pos = getIteratorPosition(State, Iter); 991 if (!Pos) 992 return; 993 994 // For deque-like containers invalidate all iterator positions. For 995 // vector-like containers invalidate iterator positions at and after the 996 // deletion. For list-like containers only invalidate the deleted position. 997 const auto *Cont = Pos->getContainer(); 998 if (hasSubscriptOperator(State, Cont) && backModifiable(State, Cont)) { 999 if (frontModifiable(State, Cont)) { 1000 State = invalidateAllIteratorPositions(State, Cont); 1001 } else { 1002 State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE); 1003 } 1004 if (const auto *CData = getContainerData(State, Cont)) { 1005 if (const auto EndSym = CData->getEnd()) { 1006 State = invalidateIteratorPositions(State, EndSym, BO_GE); 1007 State = setContainerData(State, Cont, CData->newEnd(nullptr)); 1008 } 1009 } 1010 } else { 1011 State = invalidateIteratorPositions(State, Pos->getOffset(), BO_EQ); 1012 } 1013 C.addTransition(State); 1014 } 1015 1016 void IteratorModeling::handleErase(CheckerContext &C, const SVal &Iter1, 1017 const SVal &Iter2) const { 1018 auto State = C.getState(); 1019 const auto *Pos1 = getIteratorPosition(State, Iter1); 1020 const auto *Pos2 = getIteratorPosition(State, Iter2); 1021 if (!Pos1 || !Pos2) 1022 return; 1023 1024 // For deque-like containers invalidate all iterator positions. For 1025 // vector-like containers invalidate iterator positions at and after the 1026 // deletion range. For list-like containers only invalidate the deleted 1027 // position range [first..last]. 1028 const auto *Cont = Pos1->getContainer(); 1029 if (hasSubscriptOperator(State, Cont) && backModifiable(State, Cont)) { 1030 if (frontModifiable(State, Cont)) { 1031 State = invalidateAllIteratorPositions(State, Cont); 1032 } else { 1033 State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE); 1034 } 1035 if (const auto *CData = getContainerData(State, Cont)) { 1036 if (const auto EndSym = CData->getEnd()) { 1037 State = invalidateIteratorPositions(State, EndSym, BO_GE); 1038 State = setContainerData(State, Cont, CData->newEnd(nullptr)); 1039 } 1040 } 1041 } else { 1042 State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE, 1043 Pos2->getOffset(), BO_LT); 1044 } 1045 C.addTransition(State); 1046 } 1047 1048 void IteratorModeling::handleEraseAfter(CheckerContext &C, 1049 const SVal &Iter) const { 1050 auto State = C.getState(); 1051 const auto *Pos = getIteratorPosition(State, Iter); 1052 if (!Pos) 1053 return; 1054 1055 // Invalidate the deleted iterator position, which is the position of the 1056 // parameter plus one. 1057 auto &SymMgr = C.getSymbolManager(); 1058 auto &BVF = SymMgr.getBasicVals(); 1059 auto &SVB = C.getSValBuilder(); 1060 const auto NextSym = 1061 SVB.evalBinOp(State, BO_Add, 1062 nonloc::SymbolVal(Pos->getOffset()), 1063 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))), 1064 SymMgr.getType(Pos->getOffset())).getAsSymbol(); 1065 State = invalidateIteratorPositions(State, NextSym, BO_EQ); 1066 C.addTransition(State); 1067 } 1068 1069 void IteratorModeling::handleEraseAfter(CheckerContext &C, const SVal &Iter1, 1070 const SVal &Iter2) const { 1071 auto State = C.getState(); 1072 const auto *Pos1 = getIteratorPosition(State, Iter1); 1073 const auto *Pos2 = getIteratorPosition(State, Iter2); 1074 if (!Pos1 || !Pos2) 1075 return; 1076 1077 // Invalidate the deleted iterator position range (first..last) 1078 State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GT, 1079 Pos2->getOffset(), BO_LT); 1080 C.addTransition(State); 1081 } 1082 1083 namespace { 1084 1085 const CXXRecordDecl *getCXXRecordDecl(ProgramStateRef State, 1086 const MemRegion *Reg); 1087 1088 bool isBeginCall(const FunctionDecl *Func) { 1089 const auto *IdInfo = Func->getIdentifier(); 1090 if (!IdInfo) 1091 return false; 1092 return IdInfo->getName().endswith_lower("begin"); 1093 } 1094 1095 bool isEndCall(const FunctionDecl *Func) { 1096 const auto *IdInfo = Func->getIdentifier(); 1097 if (!IdInfo) 1098 return false; 1099 return IdInfo->getName().endswith_lower("end"); 1100 } 1101 1102 bool isAssignCall(const FunctionDecl *Func) { 1103 const auto *IdInfo = Func->getIdentifier(); 1104 if (!IdInfo) 1105 return false; 1106 if (Func->getNumParams() > 2) 1107 return false; 1108 return IdInfo->getName() == "assign"; 1109 } 1110 1111 bool isClearCall(const FunctionDecl *Func) { 1112 const auto *IdInfo = Func->getIdentifier(); 1113 if (!IdInfo) 1114 return false; 1115 if (Func->getNumParams() > 0) 1116 return false; 1117 return IdInfo->getName() == "clear"; 1118 } 1119 1120 bool isPushBackCall(const FunctionDecl *Func) { 1121 const auto *IdInfo = Func->getIdentifier(); 1122 if (!IdInfo) 1123 return false; 1124 if (Func->getNumParams() != 1) 1125 return false; 1126 return IdInfo->getName() == "push_back"; 1127 } 1128 1129 bool isEmplaceBackCall(const FunctionDecl *Func) { 1130 const auto *IdInfo = Func->getIdentifier(); 1131 if (!IdInfo) 1132 return false; 1133 if (Func->getNumParams() < 1) 1134 return false; 1135 return IdInfo->getName() == "emplace_back"; 1136 } 1137 1138 bool isPopBackCall(const FunctionDecl *Func) { 1139 const auto *IdInfo = Func->getIdentifier(); 1140 if (!IdInfo) 1141 return false; 1142 if (Func->getNumParams() > 0) 1143 return false; 1144 return IdInfo->getName() == "pop_back"; 1145 } 1146 1147 bool isPushFrontCall(const FunctionDecl *Func) { 1148 const auto *IdInfo = Func->getIdentifier(); 1149 if (!IdInfo) 1150 return false; 1151 if (Func->getNumParams() != 1) 1152 return false; 1153 return IdInfo->getName() == "push_front"; 1154 } 1155 1156 bool isEmplaceFrontCall(const FunctionDecl *Func) { 1157 const auto *IdInfo = Func->getIdentifier(); 1158 if (!IdInfo) 1159 return false; 1160 if (Func->getNumParams() < 1) 1161 return false; 1162 return IdInfo->getName() == "emplace_front"; 1163 } 1164 1165 bool isPopFrontCall(const FunctionDecl *Func) { 1166 const auto *IdInfo = Func->getIdentifier(); 1167 if (!IdInfo) 1168 return false; 1169 if (Func->getNumParams() > 0) 1170 return false; 1171 return IdInfo->getName() == "pop_front"; 1172 } 1173 1174 bool isAssignmentOperator(OverloadedOperatorKind OK) { return OK == OO_Equal; } 1175 1176 bool isSimpleComparisonOperator(OverloadedOperatorKind OK) { 1177 return OK == OO_EqualEqual || OK == OO_ExclaimEqual; 1178 } 1179 1180 bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg) { 1181 const auto *CRD = getCXXRecordDecl(State, Reg); 1182 if (!CRD) 1183 return false; 1184 1185 for (const auto *Method : CRD->methods()) { 1186 if (!Method->isOverloadedOperator()) 1187 continue; 1188 const auto OPK = Method->getOverloadedOperator(); 1189 if (OPK == OO_Subscript) { 1190 return true; 1191 } 1192 } 1193 return false; 1194 } 1195 1196 bool frontModifiable(ProgramStateRef State, const MemRegion *Reg) { 1197 const auto *CRD = getCXXRecordDecl(State, Reg); 1198 if (!CRD) 1199 return false; 1200 1201 for (const auto *Method : CRD->methods()) { 1202 if (!Method->getDeclName().isIdentifier()) 1203 continue; 1204 if (Method->getName() == "push_front" || Method->getName() == "pop_front") { 1205 return true; 1206 } 1207 } 1208 return false; 1209 } 1210 1211 bool backModifiable(ProgramStateRef State, const MemRegion *Reg) { 1212 const auto *CRD = getCXXRecordDecl(State, Reg); 1213 if (!CRD) 1214 return false; 1215 1216 for (const auto *Method : CRD->methods()) { 1217 if (!Method->getDeclName().isIdentifier()) 1218 continue; 1219 if (Method->getName() == "push_back" || Method->getName() == "pop_back") { 1220 return true; 1221 } 1222 } 1223 return false; 1224 } 1225 1226 const CXXRecordDecl *getCXXRecordDecl(ProgramStateRef State, 1227 const MemRegion *Reg) { 1228 auto TI = getDynamicTypeInfo(State, Reg); 1229 if (!TI.isValid()) 1230 return nullptr; 1231 1232 auto Type = TI.getType(); 1233 if (const auto *RefT = Type->getAs<ReferenceType>()) { 1234 Type = RefT->getPointeeType(); 1235 } 1236 1237 return Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl(); 1238 } 1239 1240 SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont) { 1241 const auto *CDataPtr = getContainerData(State, Cont); 1242 if (!CDataPtr) 1243 return nullptr; 1244 1245 return CDataPtr->getBegin(); 1246 } 1247 1248 SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont) { 1249 const auto *CDataPtr = getContainerData(State, Cont); 1250 if (!CDataPtr) 1251 return nullptr; 1252 1253 return CDataPtr->getEnd(); 1254 } 1255 1256 ProgramStateRef createContainerBegin(ProgramStateRef State, 1257 const MemRegion *Cont, const Expr *E, 1258 QualType T, const LocationContext *LCtx, 1259 unsigned BlockCount) { 1260 // Only create if it does not exist 1261 const auto *CDataPtr = getContainerData(State, Cont); 1262 if (CDataPtr && CDataPtr->getBegin()) 1263 return State; 1264 1265 auto &SymMgr = State->getSymbolManager(); 1266 const SymbolConjured *Sym = SymMgr.conjureSymbol(E, LCtx, T, BlockCount, 1267 "begin"); 1268 State = assumeNoOverflow(State, Sym, 4); 1269 1270 if (CDataPtr) { 1271 const auto CData = CDataPtr->newBegin(Sym); 1272 return setContainerData(State, Cont, CData); 1273 } 1274 1275 const auto CData = ContainerData::fromBegin(Sym); 1276 return setContainerData(State, Cont, CData); 1277 } 1278 1279 ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont, 1280 const Expr *E, QualType T, 1281 const LocationContext *LCtx, 1282 unsigned BlockCount) { 1283 // Only create if it does not exist 1284 const auto *CDataPtr = getContainerData(State, Cont); 1285 if (CDataPtr && CDataPtr->getEnd()) 1286 return State; 1287 1288 auto &SymMgr = State->getSymbolManager(); 1289 const SymbolConjured *Sym = SymMgr.conjureSymbol(E, LCtx, T, BlockCount, 1290 "end"); 1291 State = assumeNoOverflow(State, Sym, 4); 1292 1293 if (CDataPtr) { 1294 const auto CData = CDataPtr->newEnd(Sym); 1295 return setContainerData(State, Cont, CData); 1296 } 1297 1298 const auto CData = ContainerData::fromEnd(Sym); 1299 return setContainerData(State, Cont, CData); 1300 } 1301 1302 ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont, 1303 const ContainerData &CData) { 1304 return State->set<ContainerMap>(Cont, CData); 1305 } 1306 1307 ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val) { 1308 if (auto Reg = Val.getAsRegion()) { 1309 Reg = Reg->getMostDerivedObjectRegion(); 1310 return State->remove<IteratorRegionMap>(Reg); 1311 } else if (const auto Sym = Val.getAsSymbol()) { 1312 return State->remove<IteratorSymbolMap>(Sym); 1313 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) { 1314 return State->remove<IteratorRegionMap>(LCVal->getRegion()); 1315 } 1316 return nullptr; 1317 } 1318 1319 // This function tells the analyzer's engine that symbols produced by our 1320 // checker, most notably iterator positions, are relatively small. 1321 // A distance between items in the container should not be very large. 1322 // By assuming that it is within around 1/8 of the address space, 1323 // we can help the analyzer perform operations on these symbols 1324 // without being afraid of integer overflows. 1325 // FIXME: Should we provide it as an API, so that all checkers could use it? 1326 ProgramStateRef assumeNoOverflow(ProgramStateRef State, SymbolRef Sym, 1327 long Scale) { 1328 SValBuilder &SVB = State->getStateManager().getSValBuilder(); 1329 BasicValueFactory &BV = SVB.getBasicValueFactory(); 1330 1331 QualType T = Sym->getType(); 1332 assert(T->isSignedIntegerOrEnumerationType()); 1333 APSIntType AT = BV.getAPSIntType(T); 1334 1335 ProgramStateRef NewState = State; 1336 1337 llvm::APSInt Max = AT.getMaxValue() / AT.getValue(Scale); 1338 SVal IsCappedFromAbove = 1339 SVB.evalBinOpNN(State, BO_LE, nonloc::SymbolVal(Sym), 1340 nonloc::ConcreteInt(Max), SVB.getConditionType()); 1341 if (auto DV = IsCappedFromAbove.getAs<DefinedSVal>()) { 1342 NewState = NewState->assume(*DV, true); 1343 if (!NewState) 1344 return State; 1345 } 1346 1347 llvm::APSInt Min = -Max; 1348 SVal IsCappedFromBelow = 1349 SVB.evalBinOpNN(State, BO_GE, nonloc::SymbolVal(Sym), 1350 nonloc::ConcreteInt(Min), SVB.getConditionType()); 1351 if (auto DV = IsCappedFromBelow.getAs<DefinedSVal>()) { 1352 NewState = NewState->assume(*DV, true); 1353 if (!NewState) 1354 return State; 1355 } 1356 1357 return NewState; 1358 } 1359 1360 ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1, 1361 SymbolRef Sym2, bool Equal) { 1362 auto &SVB = State->getStateManager().getSValBuilder(); 1363 1364 // FIXME: This code should be reworked as follows: 1365 // 1. Subtract the operands using evalBinOp(). 1366 // 2. Assume that the result doesn't overflow. 1367 // 3. Compare the result to 0. 1368 // 4. Assume the result of the comparison. 1369 const auto comparison = 1370 SVB.evalBinOp(State, BO_EQ, nonloc::SymbolVal(Sym1), 1371 nonloc::SymbolVal(Sym2), SVB.getConditionType()); 1372 1373 assert(comparison.getAs<DefinedSVal>() && 1374 "Symbol comparison must be a `DefinedSVal`"); 1375 1376 auto NewState = State->assume(comparison.castAs<DefinedSVal>(), Equal); 1377 if (!NewState) 1378 return nullptr; 1379 1380 if (const auto CompSym = comparison.getAsSymbol()) { 1381 assert(isa<SymIntExpr>(CompSym) && 1382 "Symbol comparison must be a `SymIntExpr`"); 1383 assert(BinaryOperator::isComparisonOp( 1384 cast<SymIntExpr>(CompSym)->getOpcode()) && 1385 "Symbol comparison must be a comparison"); 1386 return assumeNoOverflow(NewState, cast<SymIntExpr>(CompSym)->getLHS(), 2); 1387 } 1388 1389 return NewState; 1390 } 1391 1392 bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont) { 1393 auto RegionMap = State->get<IteratorRegionMap>(); 1394 for (const auto Reg : RegionMap) { 1395 if (Reg.second.getContainer() == Cont) 1396 return true; 1397 } 1398 1399 auto SymbolMap = State->get<IteratorSymbolMap>(); 1400 for (const auto Sym : SymbolMap) { 1401 if (Sym.second.getContainer() == Cont) 1402 return true; 1403 } 1404 1405 return false; 1406 } 1407 1408 bool isBoundThroughLazyCompoundVal(const Environment &Env, 1409 const MemRegion *Reg) { 1410 for (const auto Binding: Env) { 1411 if (const auto LCVal = Binding.second.getAs<nonloc::LazyCompoundVal>()) { 1412 if (LCVal->getRegion() == Reg) 1413 return true; 1414 } 1415 } 1416 1417 return false; 1418 } 1419 1420 template <typename Condition, typename Process> 1421 ProgramStateRef processIteratorPositions(ProgramStateRef State, Condition Cond, 1422 Process Proc) { 1423 auto &RegionMapFactory = State->get_context<IteratorRegionMap>(); 1424 auto RegionMap = State->get<IteratorRegionMap>(); 1425 bool Changed = false; 1426 for (const auto Reg : RegionMap) { 1427 if (Cond(Reg.second)) { 1428 RegionMap = RegionMapFactory.add(RegionMap, Reg.first, Proc(Reg.second)); 1429 Changed = true; 1430 } 1431 } 1432 1433 if (Changed) 1434 State = State->set<IteratorRegionMap>(RegionMap); 1435 1436 auto &SymbolMapFactory = State->get_context<IteratorSymbolMap>(); 1437 auto SymbolMap = State->get<IteratorSymbolMap>(); 1438 Changed = false; 1439 for (const auto Sym : SymbolMap) { 1440 if (Cond(Sym.second)) { 1441 SymbolMap = SymbolMapFactory.add(SymbolMap, Sym.first, Proc(Sym.second)); 1442 Changed = true; 1443 } 1444 } 1445 1446 if (Changed) 1447 State = State->set<IteratorSymbolMap>(SymbolMap); 1448 1449 return State; 1450 } 1451 1452 ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State, 1453 const MemRegion *Cont) { 1454 auto MatchCont = [&](const IteratorPosition &Pos) { 1455 return Pos.getContainer() == Cont; 1456 }; 1457 auto Invalidate = [&](const IteratorPosition &Pos) { 1458 return Pos.invalidate(); 1459 }; 1460 return processIteratorPositions(State, MatchCont, Invalidate); 1461 } 1462 1463 ProgramStateRef 1464 invalidateAllIteratorPositionsExcept(ProgramStateRef State, 1465 const MemRegion *Cont, SymbolRef Offset, 1466 BinaryOperator::Opcode Opc) { 1467 auto MatchContAndCompare = [&](const IteratorPosition &Pos) { 1468 return Pos.getContainer() == Cont && 1469 !compare(State, Pos.getOffset(), Offset, Opc); 1470 }; 1471 auto Invalidate = [&](const IteratorPosition &Pos) { 1472 return Pos.invalidate(); 1473 }; 1474 return processIteratorPositions(State, MatchContAndCompare, Invalidate); 1475 } 1476 1477 ProgramStateRef invalidateIteratorPositions(ProgramStateRef State, 1478 SymbolRef Offset, 1479 BinaryOperator::Opcode Opc) { 1480 auto Compare = [&](const IteratorPosition &Pos) { 1481 return compare(State, Pos.getOffset(), Offset, Opc); 1482 }; 1483 auto Invalidate = [&](const IteratorPosition &Pos) { 1484 return Pos.invalidate(); 1485 }; 1486 return processIteratorPositions(State, Compare, Invalidate); 1487 } 1488 1489 ProgramStateRef invalidateIteratorPositions(ProgramStateRef State, 1490 SymbolRef Offset1, 1491 BinaryOperator::Opcode Opc1, 1492 SymbolRef Offset2, 1493 BinaryOperator::Opcode Opc2) { 1494 auto Compare = [&](const IteratorPosition &Pos) { 1495 return compare(State, Pos.getOffset(), Offset1, Opc1) && 1496 compare(State, Pos.getOffset(), Offset2, Opc2); 1497 }; 1498 auto Invalidate = [&](const IteratorPosition &Pos) { 1499 return Pos.invalidate(); 1500 }; 1501 return processIteratorPositions(State, Compare, Invalidate); 1502 } 1503 1504 ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State, 1505 const MemRegion *Cont, 1506 const MemRegion *NewCont) { 1507 auto MatchCont = [&](const IteratorPosition &Pos) { 1508 return Pos.getContainer() == Cont; 1509 }; 1510 auto ReAssign = [&](const IteratorPosition &Pos) { 1511 return Pos.reAssign(NewCont); 1512 }; 1513 return processIteratorPositions(State, MatchCont, ReAssign); 1514 } 1515 1516 ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State, 1517 const MemRegion *Cont, 1518 const MemRegion *NewCont, 1519 SymbolRef Offset, 1520 BinaryOperator::Opcode Opc) { 1521 auto MatchContAndCompare = [&](const IteratorPosition &Pos) { 1522 return Pos.getContainer() == Cont && 1523 !compare(State, Pos.getOffset(), Offset, Opc); 1524 }; 1525 auto ReAssign = [&](const IteratorPosition &Pos) { 1526 return Pos.reAssign(NewCont); 1527 }; 1528 return processIteratorPositions(State, MatchContAndCompare, ReAssign); 1529 } 1530 1531 // This function rebases symbolic expression `OldSym + Int` to `NewSym + Int`, 1532 // `OldSym - Int` to `NewSym - Int` and `OldSym` to `NewSym` in any iterator 1533 // position offsets where `CondSym` is true. 1534 ProgramStateRef rebaseSymbolInIteratorPositionsIf( 1535 ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym, 1536 SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc) { 1537 auto LessThanEnd = [&](const IteratorPosition &Pos) { 1538 return compare(State, Pos.getOffset(), CondSym, Opc); 1539 }; 1540 auto RebaseSymbol = [&](const IteratorPosition &Pos) { 1541 return Pos.setTo(rebaseSymbol(State, SVB, Pos.getOffset(), OldSym, 1542 NewSym)); 1543 }; 1544 return processIteratorPositions(State, LessThanEnd, RebaseSymbol); 1545 } 1546 1547 // This function rebases symbolic expression `OldExpr + Int` to `NewExpr + Int`, 1548 // `OldExpr - Int` to `NewExpr - Int` and `OldExpr` to `NewExpr` in expression 1549 // `OrigExpr`. 1550 SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB, 1551 SymbolRef OrigExpr, SymbolRef OldExpr, 1552 SymbolRef NewSym) { 1553 auto &SymMgr = SVB.getSymbolManager(); 1554 auto Diff = SVB.evalBinOpNN(State, BO_Sub, nonloc::SymbolVal(OrigExpr), 1555 nonloc::SymbolVal(OldExpr), 1556 SymMgr.getType(OrigExpr)); 1557 1558 const auto DiffInt = Diff.getAs<nonloc::ConcreteInt>(); 1559 if (!DiffInt) 1560 return OrigExpr; 1561 1562 return SVB.evalBinOpNN(State, BO_Add, *DiffInt, nonloc::SymbolVal(NewSym), 1563 SymMgr.getType(OrigExpr)).getAsSymbol(); 1564 } 1565 1566 } // namespace 1567 1568 void ento::registerIteratorModeling(CheckerManager &mgr) { 1569 mgr.registerChecker<IteratorModeling>(); 1570 } 1571 1572 bool ento::shouldRegisterIteratorModeling(const LangOptions &LO) { 1573 return true; 1574 } 1575