1 //===-- ContainerModeling.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 container-like containers. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "clang/AST/DeclTemplate.h" 14 #include "clang/Driver/DriverDiagnostic.h" 15 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 16 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 17 #include "clang/StaticAnalyzer/Core/Checker.h" 18 #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" 19 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 20 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 21 #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" 22 23 #include "Iterator.h" 24 25 #include <utility> 26 27 using namespace clang; 28 using namespace ento; 29 using namespace iterator; 30 31 namespace { 32 33 class ContainerModeling 34 : public Checker<check::PostCall, check::LiveSymbols, check::DeadSymbols> { 35 36 void handleBegin(CheckerContext &C, const Expr *CE, SVal RetVal, 37 SVal Cont) const; 38 void handleEnd(CheckerContext &C, const Expr *CE, SVal RetVal, 39 SVal Cont) const; 40 void handleAssignment(CheckerContext &C, SVal Cont, const Expr *CE = nullptr, 41 SVal OldCont = UndefinedVal()) const; 42 void handleAssign(CheckerContext &C, SVal Cont, const Expr *ContE) const; 43 void handleClear(CheckerContext &C, SVal Cont, const Expr *ContE) const; 44 void handlePushBack(CheckerContext &C, SVal Cont, const Expr *ContE) const; 45 void handlePopBack(CheckerContext &C, SVal Cont, const Expr *ContE) const; 46 void handlePushFront(CheckerContext &C, SVal Cont, const Expr *ContE) const; 47 void handlePopFront(CheckerContext &C, SVal Cont, const Expr *ContE) const; 48 void handleInsert(CheckerContext &C, SVal Cont, SVal Iter) const; 49 void handleErase(CheckerContext &C, SVal Cont, SVal Iter) const; 50 void handleErase(CheckerContext &C, SVal Cont, SVal Iter1, SVal Iter2) const; 51 void handleEraseAfter(CheckerContext &C, SVal Cont, SVal Iter) const; 52 void handleEraseAfter(CheckerContext &C, SVal Cont, SVal Iter1, 53 SVal Iter2) const; 54 const NoteTag *getChangeTag(CheckerContext &C, StringRef Text, 55 const MemRegion *ContReg, 56 const Expr *ContE) const; 57 void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, 58 const char *Sep) const override; 59 60 public: 61 ContainerModeling() = default; 62 63 void checkPostCall(const CallEvent &Call, CheckerContext &C) const; 64 void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const; 65 void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; 66 67 using NoItParamFn = void (ContainerModeling::*)(CheckerContext &, SVal, 68 const Expr *) const; 69 using OneItParamFn = void (ContainerModeling::*)(CheckerContext &, SVal, 70 SVal) const; 71 using TwoItParamFn = void (ContainerModeling::*)(CheckerContext &, SVal, SVal, 72 SVal) const; 73 74 CallDescriptionMap<NoItParamFn> NoIterParamFunctions = { 75 {{0, "clear", 0}, 76 &ContainerModeling::handleClear}, 77 {{0, "assign", 2}, 78 &ContainerModeling::handleAssign}, 79 {{0, "push_back", 1}, 80 &ContainerModeling::handlePushBack}, 81 {{0, "emplace_back", 1}, 82 &ContainerModeling::handlePushBack}, 83 {{0, "pop_back", 0}, 84 &ContainerModeling::handlePopBack}, 85 {{0, "push_front", 1}, 86 &ContainerModeling::handlePushFront}, 87 {{0, "emplace_front", 1}, 88 &ContainerModeling::handlePushFront}, 89 {{0, "pop_front", 0}, 90 &ContainerModeling::handlePopFront}, 91 }; 92 93 CallDescriptionMap<OneItParamFn> OneIterParamFunctions = { 94 {{0, "insert", 2}, 95 &ContainerModeling::handleInsert}, 96 {{0, "emplace", 2}, 97 &ContainerModeling::handleInsert}, 98 {{0, "erase", 1}, 99 &ContainerModeling::handleErase}, 100 {{0, "erase_after", 1}, 101 &ContainerModeling::handleEraseAfter}, 102 }; 103 104 CallDescriptionMap<TwoItParamFn> TwoIterParamFunctions = { 105 {{0, "erase", 2}, 106 &ContainerModeling::handleErase}, 107 {{0, "erase_after", 2}, 108 &ContainerModeling::handleEraseAfter}, 109 }; 110 111 }; 112 113 bool isBeginCall(const FunctionDecl *Func); 114 bool isEndCall(const FunctionDecl *Func); 115 bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg); 116 bool frontModifiable(ProgramStateRef State, const MemRegion *Reg); 117 bool backModifiable(ProgramStateRef State, const MemRegion *Reg); 118 SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont); 119 SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont); 120 ProgramStateRef createContainerBegin(ProgramStateRef State, 121 const MemRegion *Cont, const Expr *E, 122 QualType T, const LocationContext *LCtx, 123 unsigned BlockCount); 124 ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont, 125 const Expr *E, QualType T, 126 const LocationContext *LCtx, 127 unsigned BlockCount); 128 ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont, 129 const ContainerData &CData); 130 ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State, 131 const MemRegion *Cont); 132 ProgramStateRef 133 invalidateAllIteratorPositionsExcept(ProgramStateRef State, 134 const MemRegion *Cont, SymbolRef Offset, 135 BinaryOperator::Opcode Opc); 136 ProgramStateRef invalidateIteratorPositions(ProgramStateRef State, 137 SymbolRef Offset, 138 BinaryOperator::Opcode Opc); 139 ProgramStateRef invalidateIteratorPositions(ProgramStateRef State, 140 SymbolRef Offset1, 141 BinaryOperator::Opcode Opc1, 142 SymbolRef Offset2, 143 BinaryOperator::Opcode Opc2); 144 ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State, 145 const MemRegion *Cont, 146 const MemRegion *NewCont); 147 ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State, 148 const MemRegion *Cont, 149 const MemRegion *NewCont, 150 SymbolRef Offset, 151 BinaryOperator::Opcode Opc); 152 ProgramStateRef rebaseSymbolInIteratorPositionsIf( 153 ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym, 154 SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc); 155 SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB, SymbolRef Expr, 156 SymbolRef OldSym, SymbolRef NewSym); 157 bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont); 158 159 } // namespace 160 161 void ContainerModeling::checkPostCall(const CallEvent &Call, 162 CheckerContext &C) const { 163 const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); 164 if (!Func) 165 return; 166 167 if (Func->isOverloadedOperator()) { 168 const auto Op = Func->getOverloadedOperator(); 169 if (Op == OO_Equal) { 170 // Overloaded 'operator=' must be a non-static member function. 171 const auto *InstCall = cast<CXXInstanceCall>(&Call); 172 if (cast<CXXMethodDecl>(Func)->isMoveAssignmentOperator()) { 173 handleAssignment(C, InstCall->getCXXThisVal(), Call.getOriginExpr(), 174 Call.getArgSVal(0)); 175 return; 176 } 177 178 handleAssignment(C, InstCall->getCXXThisVal()); 179 return; 180 } 181 } else { 182 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { 183 const NoItParamFn *Handler0 = NoIterParamFunctions.lookup(Call); 184 if (Handler0) { 185 (this->**Handler0)(C, InstCall->getCXXThisVal(), 186 InstCall->getCXXThisExpr()); 187 return; 188 } 189 190 const OneItParamFn *Handler1 = OneIterParamFunctions.lookup(Call); 191 if (Handler1) { 192 (this->**Handler1)(C, InstCall->getCXXThisVal(), Call.getArgSVal(0)); 193 return; 194 } 195 196 const TwoItParamFn *Handler2 = TwoIterParamFunctions.lookup(Call); 197 if (Handler2) { 198 (this->**Handler2)(C, InstCall->getCXXThisVal(), Call.getArgSVal(0), 199 Call.getArgSVal(1)); 200 return; 201 } 202 203 const auto *OrigExpr = Call.getOriginExpr(); 204 if (!OrigExpr) 205 return; 206 207 if (isBeginCall(Func)) { 208 handleBegin(C, OrigExpr, Call.getReturnValue(), 209 InstCall->getCXXThisVal()); 210 return; 211 } 212 213 if (isEndCall(Func)) { 214 handleEnd(C, OrigExpr, Call.getReturnValue(), 215 InstCall->getCXXThisVal()); 216 return; 217 } 218 } 219 } 220 } 221 222 void ContainerModeling::checkLiveSymbols(ProgramStateRef State, 223 SymbolReaper &SR) const { 224 // Keep symbolic expressions of container begins and ends alive 225 auto ContMap = State->get<ContainerMap>(); 226 for (const auto &Cont : ContMap) { 227 const auto CData = Cont.second; 228 if (CData.getBegin()) { 229 SR.markLive(CData.getBegin()); 230 if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getBegin())) 231 SR.markLive(SIE->getLHS()); 232 } 233 if (CData.getEnd()) { 234 SR.markLive(CData.getEnd()); 235 if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getEnd())) 236 SR.markLive(SIE->getLHS()); 237 } 238 } 239 } 240 241 void ContainerModeling::checkDeadSymbols(SymbolReaper &SR, 242 CheckerContext &C) const { 243 // Cleanup 244 auto State = C.getState(); 245 246 auto ContMap = State->get<ContainerMap>(); 247 for (const auto &Cont : ContMap) { 248 if (!SR.isLiveRegion(Cont.first)) { 249 // We must keep the container data while it has live iterators to be able 250 // to compare them to the begin and the end of the container. 251 if (!hasLiveIterators(State, Cont.first)) { 252 State = State->remove<ContainerMap>(Cont.first); 253 } 254 } 255 } 256 257 C.addTransition(State); 258 } 259 260 void ContainerModeling::handleBegin(CheckerContext &C, const Expr *CE, 261 SVal RetVal, SVal Cont) const { 262 const auto *ContReg = Cont.getAsRegion(); 263 if (!ContReg) 264 return; 265 266 ContReg = ContReg->getMostDerivedObjectRegion(); 267 268 // If the container already has a begin symbol then use it. Otherwise first 269 // create a new one. 270 auto State = C.getState(); 271 auto BeginSym = getContainerBegin(State, ContReg); 272 if (!BeginSym) { 273 State = createContainerBegin(State, ContReg, CE, C.getASTContext().LongTy, 274 C.getLocationContext(), C.blockCount()); 275 BeginSym = getContainerBegin(State, ContReg); 276 } 277 State = setIteratorPosition(State, RetVal, 278 IteratorPosition::getPosition(ContReg, BeginSym)); 279 C.addTransition(State); 280 } 281 282 void ContainerModeling::handleEnd(CheckerContext &C, const Expr *CE, 283 SVal RetVal, SVal Cont) const { 284 const auto *ContReg = Cont.getAsRegion(); 285 if (!ContReg) 286 return; 287 288 ContReg = ContReg->getMostDerivedObjectRegion(); 289 290 // If the container already has an end symbol then use it. Otherwise first 291 // create a new one. 292 auto State = C.getState(); 293 auto EndSym = getContainerEnd(State, ContReg); 294 if (!EndSym) { 295 State = createContainerEnd(State, ContReg, CE, C.getASTContext().LongTy, 296 C.getLocationContext(), C.blockCount()); 297 EndSym = getContainerEnd(State, ContReg); 298 } 299 State = setIteratorPosition(State, RetVal, 300 IteratorPosition::getPosition(ContReg, EndSym)); 301 C.addTransition(State); 302 } 303 304 void ContainerModeling::handleAssignment(CheckerContext &C, SVal Cont, 305 const Expr *CE, SVal OldCont) const { 306 const auto *ContReg = Cont.getAsRegion(); 307 if (!ContReg) 308 return; 309 310 ContReg = ContReg->getMostDerivedObjectRegion(); 311 312 // Assignment of a new value to a container always invalidates all its 313 // iterators 314 auto State = C.getState(); 315 const auto CData = getContainerData(State, ContReg); 316 if (CData) { 317 State = invalidateAllIteratorPositions(State, ContReg); 318 } 319 320 // In case of move, iterators of the old container (except the past-end 321 // iterators) remain valid but refer to the new container 322 if (!OldCont.isUndef()) { 323 const auto *OldContReg = OldCont.getAsRegion(); 324 if (OldContReg) { 325 OldContReg = OldContReg->getMostDerivedObjectRegion(); 326 const auto OldCData = getContainerData(State, OldContReg); 327 if (OldCData) { 328 if (const auto OldEndSym = OldCData->getEnd()) { 329 // If we already assigned an "end" symbol to the old container, then 330 // first reassign all iterator positions to the new container which 331 // are not past the container (thus not greater or equal to the 332 // current "end" symbol). 333 State = reassignAllIteratorPositionsUnless(State, OldContReg, ContReg, 334 OldEndSym, BO_GE); 335 auto &SymMgr = C.getSymbolManager(); 336 auto &SVB = C.getSValBuilder(); 337 // Then generate and assign a new "end" symbol for the new container. 338 auto NewEndSym = 339 SymMgr.conjureSymbol(CE, C.getLocationContext(), 340 C.getASTContext().LongTy, C.blockCount()); 341 State = assumeNoOverflow(State, NewEndSym, 4); 342 if (CData) { 343 State = setContainerData(State, ContReg, CData->newEnd(NewEndSym)); 344 } else { 345 State = setContainerData(State, ContReg, 346 ContainerData::fromEnd(NewEndSym)); 347 } 348 // Finally, replace the old "end" symbol in the already reassigned 349 // iterator positions with the new "end" symbol. 350 State = rebaseSymbolInIteratorPositionsIf( 351 State, SVB, OldEndSym, NewEndSym, OldEndSym, BO_LT); 352 } else { 353 // There was no "end" symbol assigned yet to the old container, 354 // so reassign all iterator positions to the new container. 355 State = reassignAllIteratorPositions(State, OldContReg, ContReg); 356 } 357 if (const auto OldBeginSym = OldCData->getBegin()) { 358 // If we already assigned a "begin" symbol to the old container, then 359 // assign it to the new container and remove it from the old one. 360 if (CData) { 361 State = 362 setContainerData(State, ContReg, CData->newBegin(OldBeginSym)); 363 } else { 364 State = setContainerData(State, ContReg, 365 ContainerData::fromBegin(OldBeginSym)); 366 } 367 State = 368 setContainerData(State, OldContReg, OldCData->newBegin(nullptr)); 369 } 370 } else { 371 // There was neither "begin" nor "end" symbol assigned yet to the old 372 // container, so reassign all iterator positions to the new container. 373 State = reassignAllIteratorPositions(State, OldContReg, ContReg); 374 } 375 } 376 } 377 C.addTransition(State); 378 } 379 380 void ContainerModeling::handleAssign(CheckerContext &C, SVal Cont, 381 const Expr *ContE) const { 382 const auto *ContReg = Cont.getAsRegion(); 383 if (!ContReg) 384 return; 385 386 ContReg = ContReg->getMostDerivedObjectRegion(); 387 388 // The assign() operation invalidates all the iterators 389 auto State = C.getState(); 390 State = invalidateAllIteratorPositions(State, ContReg); 391 C.addTransition(State); 392 } 393 394 void ContainerModeling::handleClear(CheckerContext &C, SVal Cont, 395 const Expr *ContE) const { 396 const auto *ContReg = Cont.getAsRegion(); 397 if (!ContReg) 398 return; 399 400 ContReg = ContReg->getMostDerivedObjectRegion(); 401 402 // The clear() operation invalidates all the iterators, except the past-end 403 // iterators of list-like containers 404 auto State = C.getState(); 405 if (!hasSubscriptOperator(State, ContReg) || 406 !backModifiable(State, ContReg)) { 407 const auto CData = getContainerData(State, ContReg); 408 if (CData) { 409 if (const auto EndSym = CData->getEnd()) { 410 State = 411 invalidateAllIteratorPositionsExcept(State, ContReg, EndSym, BO_GE); 412 C.addTransition(State); 413 return; 414 } 415 } 416 } 417 const NoteTag *ChangeTag = 418 getChangeTag(C, "became empty", ContReg, ContE); 419 State = invalidateAllIteratorPositions(State, ContReg); 420 C.addTransition(State, ChangeTag); 421 } 422 423 void ContainerModeling::handlePushBack(CheckerContext &C, SVal Cont, 424 const Expr *ContE) const { 425 const auto *ContReg = Cont.getAsRegion(); 426 if (!ContReg) 427 return; 428 429 ContReg = ContReg->getMostDerivedObjectRegion(); 430 431 // For deque-like containers invalidate all iterator positions 432 auto State = C.getState(); 433 if (hasSubscriptOperator(State, ContReg) && frontModifiable(State, ContReg)) { 434 State = invalidateAllIteratorPositions(State, ContReg); 435 C.addTransition(State); 436 return; 437 } 438 439 const auto CData = getContainerData(State, ContReg); 440 if (!CData) 441 return; 442 443 // For vector-like containers invalidate the past-end iterator positions 444 if (const auto EndSym = CData->getEnd()) { 445 if (hasSubscriptOperator(State, ContReg)) { 446 State = invalidateIteratorPositions(State, EndSym, BO_GE); 447 } 448 auto &SymMgr = C.getSymbolManager(); 449 auto &BVF = SymMgr.getBasicVals(); 450 auto &SVB = C.getSValBuilder(); 451 const auto newEndSym = 452 SVB.evalBinOp(State, BO_Add, 453 nonloc::SymbolVal(EndSym), 454 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))), 455 SymMgr.getType(EndSym)).getAsSymbol(); 456 const NoteTag *ChangeTag = 457 getChangeTag(C, "extended to the back by 1 position", ContReg, ContE); 458 State = setContainerData(State, ContReg, CData->newEnd(newEndSym)); 459 C.addTransition(State, ChangeTag); 460 } 461 } 462 463 void ContainerModeling::handlePopBack(CheckerContext &C, SVal Cont, 464 const Expr *ContE) const { 465 const auto *ContReg = Cont.getAsRegion(); 466 if (!ContReg) 467 return; 468 469 ContReg = ContReg->getMostDerivedObjectRegion(); 470 471 auto State = C.getState(); 472 const auto CData = getContainerData(State, ContReg); 473 if (!CData) 474 return; 475 476 if (const auto EndSym = CData->getEnd()) { 477 auto &SymMgr = C.getSymbolManager(); 478 auto &BVF = SymMgr.getBasicVals(); 479 auto &SVB = C.getSValBuilder(); 480 const auto BackSym = 481 SVB.evalBinOp(State, BO_Sub, 482 nonloc::SymbolVal(EndSym), 483 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))), 484 SymMgr.getType(EndSym)).getAsSymbol(); 485 const NoteTag *ChangeTag = 486 getChangeTag(C, "shrank from the back by 1 position", ContReg, ContE); 487 // For vector-like and deque-like containers invalidate the last and the 488 // past-end iterator positions. For list-like containers only invalidate 489 // the last position 490 if (hasSubscriptOperator(State, ContReg) && 491 backModifiable(State, ContReg)) { 492 State = invalidateIteratorPositions(State, BackSym, BO_GE); 493 State = setContainerData(State, ContReg, CData->newEnd(nullptr)); 494 } else { 495 State = invalidateIteratorPositions(State, BackSym, BO_EQ); 496 } 497 auto newEndSym = BackSym; 498 State = setContainerData(State, ContReg, CData->newEnd(newEndSym)); 499 C.addTransition(State, ChangeTag); 500 } 501 } 502 503 void ContainerModeling::handlePushFront(CheckerContext &C, SVal Cont, 504 const Expr *ContE) const { 505 const auto *ContReg = Cont.getAsRegion(); 506 if (!ContReg) 507 return; 508 509 ContReg = ContReg->getMostDerivedObjectRegion(); 510 511 // For deque-like containers invalidate all iterator positions 512 auto State = C.getState(); 513 if (hasSubscriptOperator(State, ContReg)) { 514 State = invalidateAllIteratorPositions(State, ContReg); 515 C.addTransition(State); 516 } else { 517 const auto CData = getContainerData(State, ContReg); 518 if (!CData) 519 return; 520 521 if (const auto BeginSym = CData->getBegin()) { 522 auto &SymMgr = C.getSymbolManager(); 523 auto &BVF = SymMgr.getBasicVals(); 524 auto &SVB = C.getSValBuilder(); 525 const auto newBeginSym = 526 SVB.evalBinOp(State, BO_Sub, 527 nonloc::SymbolVal(BeginSym), 528 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))), 529 SymMgr.getType(BeginSym)).getAsSymbol(); 530 const NoteTag *ChangeTag = 531 getChangeTag(C, "extended to the front by 1 position", ContReg, ContE); 532 State = setContainerData(State, ContReg, CData->newBegin(newBeginSym)); 533 C.addTransition(State, ChangeTag); 534 } 535 } 536 } 537 538 void ContainerModeling::handlePopFront(CheckerContext &C, SVal Cont, 539 const Expr *ContE) const { 540 const auto *ContReg = Cont.getAsRegion(); 541 if (!ContReg) 542 return; 543 544 ContReg = ContReg->getMostDerivedObjectRegion(); 545 546 auto State = C.getState(); 547 const auto CData = getContainerData(State, ContReg); 548 if (!CData) 549 return; 550 551 // For deque-like containers invalidate all iterator positions. For list-like 552 // iterators only invalidate the first position 553 if (const auto BeginSym = CData->getBegin()) { 554 if (hasSubscriptOperator(State, ContReg)) { 555 State = invalidateIteratorPositions(State, BeginSym, BO_LE); 556 } else { 557 State = invalidateIteratorPositions(State, BeginSym, BO_EQ); 558 } 559 auto &SymMgr = C.getSymbolManager(); 560 auto &BVF = SymMgr.getBasicVals(); 561 auto &SVB = C.getSValBuilder(); 562 const auto newBeginSym = 563 SVB.evalBinOp(State, BO_Add, 564 nonloc::SymbolVal(BeginSym), 565 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))), 566 SymMgr.getType(BeginSym)).getAsSymbol(); 567 const NoteTag *ChangeTag = 568 getChangeTag(C, "shrank from the front by 1 position", ContReg, ContE); 569 State = setContainerData(State, ContReg, CData->newBegin(newBeginSym)); 570 C.addTransition(State, ChangeTag); 571 } 572 } 573 574 void ContainerModeling::handleInsert(CheckerContext &C, SVal Cont, 575 SVal Iter) const { 576 const auto *ContReg = Cont.getAsRegion(); 577 if (!ContReg) 578 return; 579 580 ContReg = ContReg->getMostDerivedObjectRegion(); 581 582 auto State = C.getState(); 583 const auto *Pos = getIteratorPosition(State, Iter); 584 if (!Pos) 585 return; 586 587 // For deque-like containers invalidate all iterator positions. For 588 // vector-like containers invalidate iterator positions after the insertion. 589 if (hasSubscriptOperator(State, ContReg) && backModifiable(State, ContReg)) { 590 if (frontModifiable(State, ContReg)) { 591 State = invalidateAllIteratorPositions(State, ContReg); 592 } else { 593 State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE); 594 } 595 if (const auto *CData = getContainerData(State, ContReg)) { 596 if (const auto EndSym = CData->getEnd()) { 597 State = invalidateIteratorPositions(State, EndSym, BO_GE); 598 State = setContainerData(State, ContReg, CData->newEnd(nullptr)); 599 } 600 } 601 C.addTransition(State); 602 } 603 } 604 605 void ContainerModeling::handleErase(CheckerContext &C, SVal Cont, 606 SVal Iter) const { 607 const auto *ContReg = Cont.getAsRegion(); 608 if (!ContReg) 609 return; 610 611 ContReg = ContReg->getMostDerivedObjectRegion(); 612 613 auto State = C.getState(); 614 const auto *Pos = getIteratorPosition(State, Iter); 615 if (!Pos) 616 return; 617 618 // For deque-like containers invalidate all iterator positions. For 619 // vector-like containers invalidate iterator positions at and after the 620 // deletion. For list-like containers only invalidate the deleted position. 621 if (hasSubscriptOperator(State, ContReg) && backModifiable(State, ContReg)) { 622 if (frontModifiable(State, ContReg)) { 623 State = invalidateAllIteratorPositions(State, ContReg); 624 } else { 625 State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE); 626 } 627 if (const auto *CData = getContainerData(State, ContReg)) { 628 if (const auto EndSym = CData->getEnd()) { 629 State = invalidateIteratorPositions(State, EndSym, BO_GE); 630 State = setContainerData(State, ContReg, CData->newEnd(nullptr)); 631 } 632 } 633 } else { 634 State = invalidateIteratorPositions(State, Pos->getOffset(), BO_EQ); 635 } 636 C.addTransition(State); 637 } 638 639 void ContainerModeling::handleErase(CheckerContext &C, SVal Cont, SVal Iter1, 640 SVal Iter2) const { 641 const auto *ContReg = Cont.getAsRegion(); 642 if (!ContReg) 643 return; 644 645 ContReg = ContReg->getMostDerivedObjectRegion(); 646 auto State = C.getState(); 647 const auto *Pos1 = getIteratorPosition(State, Iter1); 648 const auto *Pos2 = getIteratorPosition(State, Iter2); 649 if (!Pos1 || !Pos2) 650 return; 651 652 // For deque-like containers invalidate all iterator positions. For 653 // vector-like containers invalidate iterator positions at and after the 654 // deletion range. For list-like containers only invalidate the deleted 655 // position range [first..last]. 656 if (hasSubscriptOperator(State, ContReg) && backModifiable(State, ContReg)) { 657 if (frontModifiable(State, ContReg)) { 658 State = invalidateAllIteratorPositions(State, ContReg); 659 } else { 660 State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE); 661 } 662 if (const auto *CData = getContainerData(State, ContReg)) { 663 if (const auto EndSym = CData->getEnd()) { 664 State = invalidateIteratorPositions(State, EndSym, BO_GE); 665 State = setContainerData(State, ContReg, CData->newEnd(nullptr)); 666 } 667 } 668 } else { 669 State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE, 670 Pos2->getOffset(), BO_LT); 671 } 672 C.addTransition(State); 673 } 674 675 void ContainerModeling::handleEraseAfter(CheckerContext &C, SVal Cont, 676 SVal Iter) const { 677 auto State = C.getState(); 678 const auto *Pos = getIteratorPosition(State, Iter); 679 if (!Pos) 680 return; 681 682 // Invalidate the deleted iterator position, which is the position of the 683 // parameter plus one. 684 auto &SymMgr = C.getSymbolManager(); 685 auto &BVF = SymMgr.getBasicVals(); 686 auto &SVB = C.getSValBuilder(); 687 const auto NextSym = 688 SVB.evalBinOp(State, BO_Add, 689 nonloc::SymbolVal(Pos->getOffset()), 690 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))), 691 SymMgr.getType(Pos->getOffset())).getAsSymbol(); 692 State = invalidateIteratorPositions(State, NextSym, BO_EQ); 693 C.addTransition(State); 694 } 695 696 void ContainerModeling::handleEraseAfter(CheckerContext &C, SVal Cont, 697 SVal Iter1, SVal Iter2) const { 698 auto State = C.getState(); 699 const auto *Pos1 = getIteratorPosition(State, Iter1); 700 const auto *Pos2 = getIteratorPosition(State, Iter2); 701 if (!Pos1 || !Pos2) 702 return; 703 704 // Invalidate the deleted iterator position range (first..last) 705 State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GT, 706 Pos2->getOffset(), BO_LT); 707 C.addTransition(State); 708 } 709 710 const NoteTag *ContainerModeling::getChangeTag(CheckerContext &C, 711 StringRef Text, 712 const MemRegion *ContReg, 713 const Expr *ContE) const { 714 StringRef Name; 715 // First try to get the name of the variable from the region 716 if (const auto *DR = dyn_cast<DeclRegion>(ContReg)) { 717 Name = DR->getDecl()->getName(); 718 // If the region is not a `DeclRegion` then use the expression instead 719 } else if (const auto *DRE = 720 dyn_cast<DeclRefExpr>(ContE->IgnoreParenCasts())) { 721 Name = DRE->getDecl()->getName(); 722 } 723 724 return C.getNoteTag( 725 [Text, Name, ContReg](PathSensitiveBugReport &BR) -> std::string { 726 if (!BR.isInteresting(ContReg)) 727 return ""; 728 729 SmallString<256> Msg; 730 llvm::raw_svector_ostream Out(Msg); 731 Out << "Container " << (!Name.empty() ? ("'" + Name.str() + "' ") : "" ) 732 << Text; 733 return std::string(Out.str()); 734 }); 735 } 736 737 void ContainerModeling::printState(raw_ostream &Out, ProgramStateRef State, 738 const char *NL, const char *Sep) const { 739 auto ContMap = State->get<ContainerMap>(); 740 741 if (!ContMap.isEmpty()) { 742 Out << Sep << "Container Data :" << NL; 743 for (const auto &Cont : ContMap) { 744 Cont.first->dumpToStream(Out); 745 Out << " : [ "; 746 const auto CData = Cont.second; 747 if (CData.getBegin()) 748 CData.getBegin()->dumpToStream(Out); 749 else 750 Out << "<Unknown>"; 751 Out << " .. "; 752 if (CData.getEnd()) 753 CData.getEnd()->dumpToStream(Out); 754 else 755 Out << "<Unknown>"; 756 Out << " ]"; 757 } 758 } 759 } 760 761 namespace { 762 763 bool isBeginCall(const FunctionDecl *Func) { 764 const auto *IdInfo = Func->getIdentifier(); 765 if (!IdInfo) 766 return false; 767 return IdInfo->getName().endswith_insensitive("begin"); 768 } 769 770 bool isEndCall(const FunctionDecl *Func) { 771 const auto *IdInfo = Func->getIdentifier(); 772 if (!IdInfo) 773 return false; 774 return IdInfo->getName().endswith_insensitive("end"); 775 } 776 777 const CXXRecordDecl *getCXXRecordDecl(ProgramStateRef State, 778 const MemRegion *Reg) { 779 auto TI = getDynamicTypeInfo(State, Reg); 780 if (!TI.isValid()) 781 return nullptr; 782 783 auto Type = TI.getType(); 784 if (const auto *RefT = Type->getAs<ReferenceType>()) { 785 Type = RefT->getPointeeType(); 786 } 787 788 return Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl(); 789 } 790 791 bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg) { 792 const auto *CRD = getCXXRecordDecl(State, Reg); 793 if (!CRD) 794 return false; 795 796 for (const auto *Method : CRD->methods()) { 797 if (!Method->isOverloadedOperator()) 798 continue; 799 const auto OPK = Method->getOverloadedOperator(); 800 if (OPK == OO_Subscript) { 801 return true; 802 } 803 } 804 return false; 805 } 806 807 bool frontModifiable(ProgramStateRef State, const MemRegion *Reg) { 808 const auto *CRD = getCXXRecordDecl(State, Reg); 809 if (!CRD) 810 return false; 811 812 for (const auto *Method : CRD->methods()) { 813 if (!Method->getDeclName().isIdentifier()) 814 continue; 815 if (Method->getName() == "push_front" || Method->getName() == "pop_front") { 816 return true; 817 } 818 } 819 return false; 820 } 821 822 bool backModifiable(ProgramStateRef State, const MemRegion *Reg) { 823 const auto *CRD = getCXXRecordDecl(State, Reg); 824 if (!CRD) 825 return false; 826 827 for (const auto *Method : CRD->methods()) { 828 if (!Method->getDeclName().isIdentifier()) 829 continue; 830 if (Method->getName() == "push_back" || Method->getName() == "pop_back") { 831 return true; 832 } 833 } 834 return false; 835 } 836 837 SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont) { 838 const auto *CDataPtr = getContainerData(State, Cont); 839 if (!CDataPtr) 840 return nullptr; 841 842 return CDataPtr->getBegin(); 843 } 844 845 SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont) { 846 const auto *CDataPtr = getContainerData(State, Cont); 847 if (!CDataPtr) 848 return nullptr; 849 850 return CDataPtr->getEnd(); 851 } 852 853 ProgramStateRef createContainerBegin(ProgramStateRef State, 854 const MemRegion *Cont, const Expr *E, 855 QualType T, const LocationContext *LCtx, 856 unsigned BlockCount) { 857 // Only create if it does not exist 858 const auto *CDataPtr = getContainerData(State, Cont); 859 if (CDataPtr && CDataPtr->getBegin()) 860 return State; 861 862 auto &SymMgr = State->getSymbolManager(); 863 const SymbolConjured *Sym = SymMgr.conjureSymbol(E, LCtx, T, BlockCount, 864 "begin"); 865 State = assumeNoOverflow(State, Sym, 4); 866 867 if (CDataPtr) { 868 const auto CData = CDataPtr->newBegin(Sym); 869 return setContainerData(State, Cont, CData); 870 } 871 872 const auto CData = ContainerData::fromBegin(Sym); 873 return setContainerData(State, Cont, CData); 874 } 875 876 ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont, 877 const Expr *E, QualType T, 878 const LocationContext *LCtx, 879 unsigned BlockCount) { 880 // Only create if it does not exist 881 const auto *CDataPtr = getContainerData(State, Cont); 882 if (CDataPtr && CDataPtr->getEnd()) 883 return State; 884 885 auto &SymMgr = State->getSymbolManager(); 886 const SymbolConjured *Sym = SymMgr.conjureSymbol(E, LCtx, T, BlockCount, 887 "end"); 888 State = assumeNoOverflow(State, Sym, 4); 889 890 if (CDataPtr) { 891 const auto CData = CDataPtr->newEnd(Sym); 892 return setContainerData(State, Cont, CData); 893 } 894 895 const auto CData = ContainerData::fromEnd(Sym); 896 return setContainerData(State, Cont, CData); 897 } 898 899 ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont, 900 const ContainerData &CData) { 901 return State->set<ContainerMap>(Cont, CData); 902 } 903 904 template <typename Condition, typename Process> 905 ProgramStateRef processIteratorPositions(ProgramStateRef State, Condition Cond, 906 Process Proc) { 907 auto &RegionMapFactory = State->get_context<IteratorRegionMap>(); 908 auto RegionMap = State->get<IteratorRegionMap>(); 909 bool Changed = false; 910 for (const auto &Reg : RegionMap) { 911 if (Cond(Reg.second)) { 912 RegionMap = RegionMapFactory.add(RegionMap, Reg.first, Proc(Reg.second)); 913 Changed = true; 914 } 915 } 916 917 if (Changed) 918 State = State->set<IteratorRegionMap>(RegionMap); 919 920 auto &SymbolMapFactory = State->get_context<IteratorSymbolMap>(); 921 auto SymbolMap = State->get<IteratorSymbolMap>(); 922 Changed = false; 923 for (const auto &Sym : SymbolMap) { 924 if (Cond(Sym.second)) { 925 SymbolMap = SymbolMapFactory.add(SymbolMap, Sym.first, Proc(Sym.second)); 926 Changed = true; 927 } 928 } 929 930 if (Changed) 931 State = State->set<IteratorSymbolMap>(SymbolMap); 932 933 return State; 934 } 935 936 ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State, 937 const MemRegion *Cont) { 938 auto MatchCont = [&](const IteratorPosition &Pos) { 939 return Pos.getContainer() == Cont; 940 }; 941 auto Invalidate = [&](const IteratorPosition &Pos) { 942 return Pos.invalidate(); 943 }; 944 return processIteratorPositions(State, MatchCont, Invalidate); 945 } 946 947 ProgramStateRef 948 invalidateAllIteratorPositionsExcept(ProgramStateRef State, 949 const MemRegion *Cont, SymbolRef Offset, 950 BinaryOperator::Opcode Opc) { 951 auto MatchContAndCompare = [&](const IteratorPosition &Pos) { 952 return Pos.getContainer() == Cont && 953 !compare(State, Pos.getOffset(), Offset, Opc); 954 }; 955 auto Invalidate = [&](const IteratorPosition &Pos) { 956 return Pos.invalidate(); 957 }; 958 return processIteratorPositions(State, MatchContAndCompare, Invalidate); 959 } 960 961 ProgramStateRef invalidateIteratorPositions(ProgramStateRef State, 962 SymbolRef Offset, 963 BinaryOperator::Opcode Opc) { 964 auto Compare = [&](const IteratorPosition &Pos) { 965 return compare(State, Pos.getOffset(), Offset, Opc); 966 }; 967 auto Invalidate = [&](const IteratorPosition &Pos) { 968 return Pos.invalidate(); 969 }; 970 return processIteratorPositions(State, Compare, Invalidate); 971 } 972 973 ProgramStateRef invalidateIteratorPositions(ProgramStateRef State, 974 SymbolRef Offset1, 975 BinaryOperator::Opcode Opc1, 976 SymbolRef Offset2, 977 BinaryOperator::Opcode Opc2) { 978 auto Compare = [&](const IteratorPosition &Pos) { 979 return compare(State, Pos.getOffset(), Offset1, Opc1) && 980 compare(State, Pos.getOffset(), Offset2, Opc2); 981 }; 982 auto Invalidate = [&](const IteratorPosition &Pos) { 983 return Pos.invalidate(); 984 }; 985 return processIteratorPositions(State, Compare, Invalidate); 986 } 987 988 ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State, 989 const MemRegion *Cont, 990 const MemRegion *NewCont) { 991 auto MatchCont = [&](const IteratorPosition &Pos) { 992 return Pos.getContainer() == Cont; 993 }; 994 auto ReAssign = [&](const IteratorPosition &Pos) { 995 return Pos.reAssign(NewCont); 996 }; 997 return processIteratorPositions(State, MatchCont, ReAssign); 998 } 999 1000 ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State, 1001 const MemRegion *Cont, 1002 const MemRegion *NewCont, 1003 SymbolRef Offset, 1004 BinaryOperator::Opcode Opc) { 1005 auto MatchContAndCompare = [&](const IteratorPosition &Pos) { 1006 return Pos.getContainer() == Cont && 1007 !compare(State, Pos.getOffset(), Offset, Opc); 1008 }; 1009 auto ReAssign = [&](const IteratorPosition &Pos) { 1010 return Pos.reAssign(NewCont); 1011 }; 1012 return processIteratorPositions(State, MatchContAndCompare, ReAssign); 1013 } 1014 1015 // This function rebases symbolic expression `OldSym + Int` to `NewSym + Int`, 1016 // `OldSym - Int` to `NewSym - Int` and `OldSym` to `NewSym` in any iterator 1017 // position offsets where `CondSym` is true. 1018 ProgramStateRef rebaseSymbolInIteratorPositionsIf( 1019 ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym, 1020 SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc) { 1021 auto LessThanEnd = [&](const IteratorPosition &Pos) { 1022 return compare(State, Pos.getOffset(), CondSym, Opc); 1023 }; 1024 auto RebaseSymbol = [&](const IteratorPosition &Pos) { 1025 return Pos.setTo(rebaseSymbol(State, SVB, Pos.getOffset(), OldSym, 1026 NewSym)); 1027 }; 1028 return processIteratorPositions(State, LessThanEnd, RebaseSymbol); 1029 } 1030 1031 // This function rebases symbolic expression `OldExpr + Int` to `NewExpr + Int`, 1032 // `OldExpr - Int` to `NewExpr - Int` and `OldExpr` to `NewExpr` in expression 1033 // `OrigExpr`. 1034 SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB, 1035 SymbolRef OrigExpr, SymbolRef OldExpr, 1036 SymbolRef NewSym) { 1037 auto &SymMgr = SVB.getSymbolManager(); 1038 auto Diff = SVB.evalBinOpNN(State, BO_Sub, nonloc::SymbolVal(OrigExpr), 1039 nonloc::SymbolVal(OldExpr), 1040 SymMgr.getType(OrigExpr)); 1041 1042 const auto DiffInt = Diff.getAs<nonloc::ConcreteInt>(); 1043 if (!DiffInt) 1044 return OrigExpr; 1045 1046 return SVB.evalBinOpNN(State, BO_Add, *DiffInt, nonloc::SymbolVal(NewSym), 1047 SymMgr.getType(OrigExpr)).getAsSymbol(); 1048 } 1049 1050 bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont) { 1051 auto RegionMap = State->get<IteratorRegionMap>(); 1052 for (const auto &Reg : RegionMap) { 1053 if (Reg.second.getContainer() == Cont) 1054 return true; 1055 } 1056 1057 auto SymbolMap = State->get<IteratorSymbolMap>(); 1058 for (const auto &Sym : SymbolMap) { 1059 if (Sym.second.getContainer() == Cont) 1060 return true; 1061 } 1062 1063 return false; 1064 } 1065 1066 } // namespace 1067 1068 void ento::registerContainerModeling(CheckerManager &mgr) { 1069 mgr.registerChecker<ContainerModeling>(); 1070 } 1071 1072 bool ento::shouldRegisterContainerModeling(const CheckerManager &mgr) { 1073 if (!mgr.getLangOpts().CPlusPlus) 1074 return false; 1075 1076 if (!mgr.getAnalyzerOptions().ShouldAggressivelySimplifyBinaryOperation) { 1077 mgr.getASTContext().getDiagnostics().Report( 1078 diag::err_analyzer_checker_incompatible_analyzer_option) 1079 << "aggressive-binary-operation-simplification" << "false"; 1080 return false; 1081 } 1082 1083 return true; 1084 } 1085