1 // MoveChecker.cpp - Check use of moved-from objects. - C++ ---------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // This defines checker which checks for potential misuses of a moved-from 11 // object. That means method calls on the object or copying it in moved-from 12 // state. 13 // 14 //===----------------------------------------------------------------------===// 15 16 #include "ClangSACheckers.h" 17 #include "clang/AST/ExprCXX.h" 18 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 19 #include "clang/StaticAnalyzer/Core/Checker.h" 20 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 21 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 22 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 23 #include "llvm/ADT/StringSet.h" 24 25 using namespace clang; 26 using namespace ento; 27 28 namespace { 29 30 struct RegionState { 31 private: 32 enum Kind { Moved, Reported } K; 33 RegionState(Kind InK) : K(InK) {} 34 35 public: 36 bool isReported() const { return K == Reported; } 37 bool isMoved() const { return K == Moved; } 38 39 static RegionState getReported() { return RegionState(Reported); } 40 static RegionState getMoved() { return RegionState(Moved); } 41 42 bool operator==(const RegionState &X) const { return K == X.K; } 43 void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); } 44 }; 45 46 class MoveChecker 47 : public Checker<check::PreCall, check::PostCall, 48 check::DeadSymbols, check::RegionChanges> { 49 public: 50 void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const; 51 void checkPreCall(const CallEvent &MC, CheckerContext &C) const; 52 void checkPostCall(const CallEvent &MC, CheckerContext &C) const; 53 void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; 54 ProgramStateRef 55 checkRegionChanges(ProgramStateRef State, 56 const InvalidatedSymbols *Invalidated, 57 ArrayRef<const MemRegion *> RequestedRegions, 58 ArrayRef<const MemRegion *> InvalidatedRegions, 59 const LocationContext *LCtx, const CallEvent *Call) const; 60 void printState(raw_ostream &Out, ProgramStateRef State, 61 const char *NL, const char *Sep) const override; 62 63 private: 64 enum MisuseKind { MK_FunCall, MK_Copy, MK_Move, MK_Dereference }; 65 enum StdObjectKind { SK_NonStd, SK_Unsafe, SK_Safe, SK_SmartPtr }; 66 67 static bool misuseCausesCrash(MisuseKind MK) { 68 return MK == MK_Dereference; 69 } 70 71 struct ObjectKind { 72 // Is this a local variable or a local rvalue reference? 73 bool IsLocal : 1; 74 // Is this an STL object? If so, of what kind? 75 StdObjectKind StdKind : 2; 76 }; 77 78 // STL smart pointers are automatically re-initialized to null when moved 79 // from. So we can't warn on many methods, but we can warn when it is 80 // dereferenced, which is UB even if the resulting lvalue never gets read. 81 const llvm::StringSet<> StdSmartPtrClasses = { 82 "shared_ptr", 83 "unique_ptr", 84 "weak_ptr", 85 }; 86 87 // Not all of these are entirely move-safe, but they do provide *some* 88 // guarantees, and it means that somebody is using them after move 89 // in a valid manner. 90 // TODO: We can still try to identify *unsafe* use after move, 91 // like we did with smart pointers. 92 const llvm::StringSet<> StdSafeClasses = { 93 "basic_filebuf", 94 "basic_ios", 95 "future", 96 "optional", 97 "packaged_task" 98 "promise", 99 "shared_future", 100 "shared_lock", 101 "thread", 102 "unique_lock", 103 }; 104 105 // Should we bother tracking the state of the object? 106 bool shouldBeTracked(ObjectKind OK) const { 107 // In non-aggressive mode, only warn on use-after-move of local variables 108 // (or local rvalue references) and of STL objects. The former is possible 109 // because local variables (or local rvalue references) are not tempting 110 // their user to re-use the storage. The latter is possible because STL 111 // objects are known to end up in a valid but unspecified state after the 112 // move and their state-reset methods are also known, which allows us to 113 // predict precisely when use-after-move is invalid. 114 // Some STL objects are known to conform to additional contracts after move, 115 // so they are not tracked. However, smart pointers specifically are tracked 116 // because we can perform extra checking over them. 117 // In aggressive mode, warn on any use-after-move because the user has 118 // intentionally asked us to completely eliminate use-after-move 119 // in his code. 120 return IsAggressive || OK.IsLocal 121 || OK.StdKind == SK_Unsafe || OK.StdKind == SK_SmartPtr; 122 } 123 124 // Some objects only suffer from some kinds of misuses, but we need to track 125 // them anyway because we cannot know in advance what misuse will we find. 126 bool shouldWarnAbout(ObjectKind OK, MisuseKind MK) const { 127 // Additionally, only warn on smart pointers when they are dereferenced (or 128 // local or we are aggressive). 129 return shouldBeTracked(OK) && 130 (IsAggressive || OK.IsLocal 131 || OK.StdKind != SK_SmartPtr || MK == MK_Dereference); 132 } 133 134 // Obtains ObjectKind of an object. Because class declaration cannot always 135 // be easily obtained from the memory region, it is supplied separately. 136 ObjectKind classifyObject(const MemRegion *MR, const CXXRecordDecl *RD) const; 137 138 // Classifies the object and dumps a user-friendly description string to 139 // the stream. 140 void explainObject(llvm::raw_ostream &OS, const MemRegion *MR, 141 const CXXRecordDecl *RD, MisuseKind MK) const; 142 143 bool belongsTo(const CXXRecordDecl *RD, const llvm::StringSet<> &Set) const; 144 145 class MovedBugVisitor : public BugReporterVisitor { 146 public: 147 MovedBugVisitor(const MoveChecker &Chk, const MemRegion *R, 148 const CXXRecordDecl *RD, MisuseKind MK) 149 : Chk(Chk), Region(R), RD(RD), MK(MK), Found(false) {} 150 151 void Profile(llvm::FoldingSetNodeID &ID) const override { 152 static int X = 0; 153 ID.AddPointer(&X); 154 ID.AddPointer(Region); 155 // Don't add RD because it's, in theory, uniquely determined by 156 // the region. In practice though, it's not always possible to obtain 157 // the declaration directly from the region, that's why we store it 158 // in the first place. 159 } 160 161 std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, 162 BugReporterContext &BRC, 163 BugReport &BR) override; 164 165 private: 166 const MoveChecker &Chk; 167 // The tracked region. 168 const MemRegion *Region; 169 // The class of the tracked object. 170 const CXXRecordDecl *RD; 171 // How exactly the object was misused. 172 const MisuseKind MK; 173 bool Found; 174 }; 175 176 bool IsAggressive = false; 177 178 public: 179 void setAggressiveness(bool Aggressive) { IsAggressive = Aggressive; } 180 181 private: 182 mutable std::unique_ptr<BugType> BT; 183 184 // Check if the given form of potential misuse of a given object 185 // should be reported. If so, get it reported. The callback from which 186 // this function was called should immediately return after the call 187 // because this function adds one or two transitions. 188 void modelUse(ProgramStateRef State, const MemRegion *Region, 189 const CXXRecordDecl *RD, MisuseKind MK, 190 CheckerContext &C) const; 191 192 // Returns the exploded node against which the report was emitted. 193 // The caller *must* add any further transitions against this node. 194 ExplodedNode *reportBug(const MemRegion *Region, const CXXRecordDecl *RD, 195 CheckerContext &C, MisuseKind MK) const; 196 197 bool isInMoveSafeContext(const LocationContext *LC) const; 198 bool isStateResetMethod(const CXXMethodDecl *MethodDec) const; 199 bool isMoveSafeMethod(const CXXMethodDecl *MethodDec) const; 200 const ExplodedNode *getMoveLocation(const ExplodedNode *N, 201 const MemRegion *Region, 202 CheckerContext &C) const; 203 }; 204 } // end anonymous namespace 205 206 REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, RegionState) 207 208 // If a region is removed all of the subregions needs to be removed too. 209 static ProgramStateRef removeFromState(ProgramStateRef State, 210 const MemRegion *Region) { 211 if (!Region) 212 return State; 213 for (auto &E : State->get<TrackedRegionMap>()) { 214 if (E.first->isSubRegionOf(Region)) 215 State = State->remove<TrackedRegionMap>(E.first); 216 } 217 return State; 218 } 219 220 static bool isAnyBaseRegionReported(ProgramStateRef State, 221 const MemRegion *Region) { 222 for (auto &E : State->get<TrackedRegionMap>()) { 223 if (Region->isSubRegionOf(E.first) && E.second.isReported()) 224 return true; 225 } 226 return false; 227 } 228 229 static const MemRegion *unwrapRValueReferenceIndirection(const MemRegion *MR) { 230 if (const auto *SR = dyn_cast_or_null<SymbolicRegion>(MR)) { 231 SymbolRef Sym = SR->getSymbol(); 232 if (Sym->getType()->isRValueReferenceType()) 233 if (const MemRegion *OriginMR = Sym->getOriginRegion()) 234 return OriginMR; 235 } 236 return MR; 237 } 238 239 std::shared_ptr<PathDiagnosticPiece> 240 MoveChecker::MovedBugVisitor::VisitNode(const ExplodedNode *N, 241 BugReporterContext &BRC, BugReport &BR) { 242 // We need only the last move of the reported object's region. 243 // The visitor walks the ExplodedGraph backwards. 244 if (Found) 245 return nullptr; 246 ProgramStateRef State = N->getState(); 247 ProgramStateRef StatePrev = N->getFirstPred()->getState(); 248 const RegionState *TrackedObject = State->get<TrackedRegionMap>(Region); 249 const RegionState *TrackedObjectPrev = 250 StatePrev->get<TrackedRegionMap>(Region); 251 if (!TrackedObject) 252 return nullptr; 253 if (TrackedObjectPrev && TrackedObject) 254 return nullptr; 255 256 // Retrieve the associated statement. 257 const Stmt *S = PathDiagnosticLocation::getStmt(N); 258 if (!S) 259 return nullptr; 260 Found = true; 261 262 SmallString<128> Str; 263 llvm::raw_svector_ostream OS(Str); 264 265 ObjectKind OK = Chk.classifyObject(Region, RD); 266 switch (OK.StdKind) { 267 case SK_SmartPtr: 268 if (MK == MK_Dereference) { 269 OS << "Smart pointer"; 270 Chk.explainObject(OS, Region, RD, MK); 271 OS << " is reset to null when moved from"; 272 break; 273 } 274 275 // If it's not a dereference, we don't care if it was reset to null 276 // or that it is even a smart pointer. 277 LLVM_FALLTHROUGH; 278 case SK_NonStd: 279 case SK_Safe: 280 OS << "Object"; 281 Chk.explainObject(OS, Region, RD, MK); 282 OS << " is moved"; 283 break; 284 case SK_Unsafe: 285 OS << "Object"; 286 Chk.explainObject(OS, Region, RD, MK); 287 OS << " is left in a valid but unspecified state after move"; 288 break; 289 } 290 291 // Generate the extra diagnostic. 292 PathDiagnosticLocation Pos(S, BRC.getSourceManager(), 293 N->getLocationContext()); 294 return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true); 295 } 296 297 const ExplodedNode *MoveChecker::getMoveLocation(const ExplodedNode *N, 298 const MemRegion *Region, 299 CheckerContext &C) const { 300 // Walk the ExplodedGraph backwards and find the first node that referred to 301 // the tracked region. 302 const ExplodedNode *MoveNode = N; 303 304 while (N) { 305 ProgramStateRef State = N->getState(); 306 if (!State->get<TrackedRegionMap>(Region)) 307 break; 308 MoveNode = N; 309 N = N->pred_empty() ? nullptr : *(N->pred_begin()); 310 } 311 return MoveNode; 312 } 313 314 void MoveChecker::modelUse(ProgramStateRef State, const MemRegion *Region, 315 const CXXRecordDecl *RD, MisuseKind MK, 316 CheckerContext &C) const { 317 assert(!C.isDifferent() && "No transitions should have been made by now"); 318 const RegionState *RS = State->get<TrackedRegionMap>(Region); 319 ObjectKind OK = classifyObject(Region, RD); 320 321 // Just in case: if it's not a smart pointer but it does have operator *, 322 // we shouldn't call the bug a dereference. 323 if (MK == MK_Dereference && OK.StdKind != SK_SmartPtr) 324 MK = MK_FunCall; 325 326 if (!RS || !shouldWarnAbout(OK, MK) 327 || isInMoveSafeContext(C.getLocationContext())) { 328 // Finalize changes made by the caller. 329 C.addTransition(State); 330 return; 331 } 332 333 // Don't report it in case if any base region is already reported. 334 // But still generate a sink in case of UB. 335 // And still finalize changes made by the caller. 336 if (isAnyBaseRegionReported(State, Region)) { 337 if (misuseCausesCrash(MK)) { 338 C.generateSink(State, C.getPredecessor()); 339 } else { 340 C.addTransition(State); 341 } 342 return; 343 } 344 345 ExplodedNode *N = reportBug(Region, RD, C, MK); 346 347 // If the program has already crashed on this path, don't bother. 348 if (N->isSink()) 349 return; 350 351 State = State->set<TrackedRegionMap>(Region, RegionState::getReported()); 352 C.addTransition(State, N); 353 } 354 355 ExplodedNode *MoveChecker::reportBug(const MemRegion *Region, 356 const CXXRecordDecl *RD, CheckerContext &C, 357 MisuseKind MK) const { 358 if (ExplodedNode *N = misuseCausesCrash(MK) ? C.generateErrorNode() 359 : C.generateNonFatalErrorNode()) { 360 361 if (!BT) 362 BT.reset(new BugType(this, "Use-after-move", 363 "C++ move semantics")); 364 365 // Uniqueing report to the same object. 366 PathDiagnosticLocation LocUsedForUniqueing; 367 const ExplodedNode *MoveNode = getMoveLocation(N, Region, C); 368 369 if (const Stmt *MoveStmt = PathDiagnosticLocation::getStmt(MoveNode)) 370 LocUsedForUniqueing = PathDiagnosticLocation::createBegin( 371 MoveStmt, C.getSourceManager(), MoveNode->getLocationContext()); 372 373 // Creating the error message. 374 llvm::SmallString<128> Str; 375 llvm::raw_svector_ostream OS(Str); 376 switch(MK) { 377 case MK_FunCall: 378 OS << "Method called on moved-from object"; 379 explainObject(OS, Region, RD, MK); 380 break; 381 case MK_Copy: 382 OS << "Moved-from object"; 383 explainObject(OS, Region, RD, MK); 384 OS << " is copied"; 385 break; 386 case MK_Move: 387 OS << "Moved-from object"; 388 explainObject(OS, Region, RD, MK); 389 OS << " is moved"; 390 break; 391 case MK_Dereference: 392 OS << "Dereference of null smart pointer"; 393 explainObject(OS, Region, RD, MK); 394 break; 395 } 396 397 auto R = 398 llvm::make_unique<BugReport>(*BT, OS.str(), N, LocUsedForUniqueing, 399 MoveNode->getLocationContext()->getDecl()); 400 R->addVisitor(llvm::make_unique<MovedBugVisitor>(*this, Region, RD, MK)); 401 C.emitReport(std::move(R)); 402 return N; 403 } 404 return nullptr; 405 } 406 407 void MoveChecker::checkPostCall(const CallEvent &Call, 408 CheckerContext &C) const { 409 const auto *AFC = dyn_cast<AnyFunctionCall>(&Call); 410 if (!AFC) 411 return; 412 413 ProgramStateRef State = C.getState(); 414 const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(AFC->getDecl()); 415 if (!MethodDecl) 416 return; 417 418 // Check if an object became moved-from. 419 // Object can become moved from after a call to move assignment operator or 420 // move constructor . 421 const auto *ConstructorDecl = dyn_cast<CXXConstructorDecl>(MethodDecl); 422 if (ConstructorDecl && !ConstructorDecl->isMoveConstructor()) 423 return; 424 425 if (!ConstructorDecl && !MethodDecl->isMoveAssignmentOperator()) 426 return; 427 428 const auto ArgRegion = AFC->getArgSVal(0).getAsRegion(); 429 if (!ArgRegion) 430 return; 431 432 // Skip moving the object to itself. 433 const auto *CC = dyn_cast_or_null<CXXConstructorCall>(&Call); 434 if (CC && CC->getCXXThisVal().getAsRegion() == ArgRegion) 435 return; 436 437 if (const auto *IC = dyn_cast<CXXInstanceCall>(AFC)) 438 if (IC->getCXXThisVal().getAsRegion() == ArgRegion) 439 return; 440 441 const MemRegion *BaseRegion = ArgRegion->getBaseRegion(); 442 // Skip temp objects because of their short lifetime. 443 if (BaseRegion->getAs<CXXTempObjectRegion>() || 444 AFC->getArgExpr(0)->isRValue()) 445 return; 446 // If it has already been reported do not need to modify the state. 447 448 if (State->get<TrackedRegionMap>(ArgRegion)) 449 return; 450 451 const CXXRecordDecl *RD = MethodDecl->getParent(); 452 ObjectKind OK = classifyObject(ArgRegion, RD); 453 if (shouldBeTracked(OK)) { 454 // Mark object as moved-from. 455 State = State->set<TrackedRegionMap>(ArgRegion, RegionState::getMoved()); 456 C.addTransition(State); 457 return; 458 } 459 assert(!C.isDifferent() && "Should not have made transitions on this path!"); 460 } 461 462 bool MoveChecker::isMoveSafeMethod(const CXXMethodDecl *MethodDec) const { 463 // We abandon the cases where bool/void/void* conversion happens. 464 if (const auto *ConversionDec = 465 dyn_cast_or_null<CXXConversionDecl>(MethodDec)) { 466 const Type *Tp = ConversionDec->getConversionType().getTypePtrOrNull(); 467 if (!Tp) 468 return false; 469 if (Tp->isBooleanType() || Tp->isVoidType() || Tp->isVoidPointerType()) 470 return true; 471 } 472 // Function call `empty` can be skipped. 473 return (MethodDec && MethodDec->getDeclName().isIdentifier() && 474 (MethodDec->getName().lower() == "empty" || 475 MethodDec->getName().lower() == "isempty")); 476 } 477 478 bool MoveChecker::isStateResetMethod(const CXXMethodDecl *MethodDec) const { 479 if (!MethodDec) 480 return false; 481 if (MethodDec->hasAttr<ReinitializesAttr>()) 482 return true; 483 if (MethodDec->getDeclName().isIdentifier()) { 484 std::string MethodName = MethodDec->getName().lower(); 485 // TODO: Some of these methods (eg., resize) are not always resetting 486 // the state, so we should consider looking at the arguments. 487 if (MethodName == "reset" || MethodName == "clear" || 488 MethodName == "destroy" || MethodName == "resize" || 489 MethodName == "shrink") 490 return true; 491 } 492 return false; 493 } 494 495 // Don't report an error inside a move related operation. 496 // We assume that the programmer knows what she does. 497 bool MoveChecker::isInMoveSafeContext(const LocationContext *LC) const { 498 do { 499 const auto *CtxDec = LC->getDecl(); 500 auto *CtorDec = dyn_cast_or_null<CXXConstructorDecl>(CtxDec); 501 auto *DtorDec = dyn_cast_or_null<CXXDestructorDecl>(CtxDec); 502 auto *MethodDec = dyn_cast_or_null<CXXMethodDecl>(CtxDec); 503 if (DtorDec || (CtorDec && CtorDec->isCopyOrMoveConstructor()) || 504 (MethodDec && MethodDec->isOverloadedOperator() && 505 MethodDec->getOverloadedOperator() == OO_Equal) || 506 isStateResetMethod(MethodDec) || isMoveSafeMethod(MethodDec)) 507 return true; 508 } while ((LC = LC->getParent())); 509 return false; 510 } 511 512 bool MoveChecker::belongsTo(const CXXRecordDecl *RD, 513 const llvm::StringSet<> &Set) const { 514 const IdentifierInfo *II = RD->getIdentifier(); 515 return II && Set.count(II->getName()); 516 } 517 518 MoveChecker::ObjectKind 519 MoveChecker::classifyObject(const MemRegion *MR, 520 const CXXRecordDecl *RD) const { 521 // Local variables and local rvalue references are classified as "Local". 522 // For the purposes of this checker, we classify move-safe STL types 523 // as not-"STL" types, because that's how the checker treats them. 524 MR = unwrapRValueReferenceIndirection(MR); 525 bool IsLocal = 526 MR && isa<VarRegion>(MR) && isa<StackSpaceRegion>(MR->getMemorySpace()); 527 528 if (!RD || !RD->getDeclContext()->isStdNamespace()) 529 return { IsLocal, SK_NonStd }; 530 531 if (belongsTo(RD, StdSmartPtrClasses)) 532 return { IsLocal, SK_SmartPtr }; 533 534 if (belongsTo(RD, StdSafeClasses)) 535 return { IsLocal, SK_Safe }; 536 537 return { IsLocal, SK_Unsafe }; 538 } 539 540 void MoveChecker::explainObject(llvm::raw_ostream &OS, const MemRegion *MR, 541 const CXXRecordDecl *RD, MisuseKind MK) const { 542 // We may need a leading space every time we actually explain anything, 543 // and we never know if we are to explain anything until we try. 544 if (const auto DR = 545 dyn_cast_or_null<DeclRegion>(unwrapRValueReferenceIndirection(MR))) { 546 const auto *RegionDecl = cast<NamedDecl>(DR->getDecl()); 547 OS << " '" << RegionDecl->getNameAsString() << "'"; 548 } 549 550 ObjectKind OK = classifyObject(MR, RD); 551 switch (OK.StdKind) { 552 case SK_NonStd: 553 case SK_Safe: 554 break; 555 case SK_SmartPtr: 556 if (MK != MK_Dereference) 557 break; 558 559 // We only care about the type if it's a dereference. 560 LLVM_FALLTHROUGH; 561 case SK_Unsafe: 562 OS << " of type '" << RD->getQualifiedNameAsString() << "'"; 563 break; 564 }; 565 } 566 567 void MoveChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { 568 ProgramStateRef State = C.getState(); 569 570 // Remove the MemRegions from the map on which a ctor/dtor call or assignment 571 // happened. 572 573 // Checking constructor calls. 574 if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) { 575 State = removeFromState(State, CC->getCXXThisVal().getAsRegion()); 576 auto CtorDec = CC->getDecl(); 577 // Check for copying a moved-from object and report the bug. 578 if (CtorDec && CtorDec->isCopyOrMoveConstructor()) { 579 const MemRegion *ArgRegion = CC->getArgSVal(0).getAsRegion(); 580 const CXXRecordDecl *RD = CtorDec->getParent(); 581 MisuseKind MK = CtorDec->isMoveConstructor() ? MK_Move : MK_Copy; 582 modelUse(State, ArgRegion, RD, MK, C); 583 return; 584 } 585 } 586 587 const auto IC = dyn_cast<CXXInstanceCall>(&Call); 588 if (!IC) 589 return; 590 591 // Calling a destructor on a moved object is fine. 592 if (isa<CXXDestructorCall>(IC)) 593 return; 594 595 const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion(); 596 if (!ThisRegion) 597 return; 598 599 // The remaining part is check only for method call on a moved-from object. 600 const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(IC->getDecl()); 601 if (!MethodDecl) 602 return; 603 604 // We want to investigate the whole object, not only sub-object of a parent 605 // class in which the encountered method defined. 606 ThisRegion = ThisRegion->getMostDerivedObjectRegion(); 607 608 if (isStateResetMethod(MethodDecl)) { 609 State = removeFromState(State, ThisRegion); 610 C.addTransition(State); 611 return; 612 } 613 614 if (isMoveSafeMethod(MethodDecl)) 615 return; 616 617 // Store class declaration as well, for bug reporting purposes. 618 const CXXRecordDecl *RD = MethodDecl->getParent(); 619 620 if (MethodDecl->isOverloadedOperator()) { 621 OverloadedOperatorKind OOK = MethodDecl->getOverloadedOperator(); 622 623 if (OOK == OO_Equal) { 624 // Remove the tracked object for every assignment operator, but report bug 625 // only for move or copy assignment's argument. 626 State = removeFromState(State, ThisRegion); 627 628 if (MethodDecl->isCopyAssignmentOperator() || 629 MethodDecl->isMoveAssignmentOperator()) { 630 const MemRegion *ArgRegion = IC->getArgSVal(0).getAsRegion(); 631 MisuseKind MK = 632 MethodDecl->isMoveAssignmentOperator() ? MK_Move : MK_Copy; 633 modelUse(State, ArgRegion, RD, MK, C); 634 return; 635 } 636 C.addTransition(State); 637 return; 638 } 639 640 if (OOK == OO_Star || OOK == OO_Arrow) { 641 modelUse(State, ThisRegion, RD, MK_Dereference, C); 642 return; 643 } 644 } 645 646 modelUse(State, ThisRegion, RD, MK_FunCall, C); 647 } 648 649 void MoveChecker::checkDeadSymbols(SymbolReaper &SymReaper, 650 CheckerContext &C) const { 651 ProgramStateRef State = C.getState(); 652 TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>(); 653 for (TrackedRegionMapTy::value_type E : TrackedRegions) { 654 const MemRegion *Region = E.first; 655 bool IsRegDead = !SymReaper.isLiveRegion(Region); 656 657 // Remove the dead regions from the region map. 658 if (IsRegDead) { 659 State = State->remove<TrackedRegionMap>(Region); 660 } 661 } 662 C.addTransition(State); 663 } 664 665 ProgramStateRef MoveChecker::checkRegionChanges( 666 ProgramStateRef State, const InvalidatedSymbols *Invalidated, 667 ArrayRef<const MemRegion *> RequestedRegions, 668 ArrayRef<const MemRegion *> InvalidatedRegions, 669 const LocationContext *LCtx, const CallEvent *Call) const { 670 if (Call) { 671 // Relax invalidation upon function calls: only invalidate parameters 672 // that are passed directly via non-const pointers or non-const references 673 // or rvalue references. 674 // In case of an InstanceCall don't invalidate the this-region since 675 // it is fully handled in checkPreCall and checkPostCall. 676 const MemRegion *ThisRegion = nullptr; 677 if (const auto *IC = dyn_cast<CXXInstanceCall>(Call)) 678 ThisRegion = IC->getCXXThisVal().getAsRegion(); 679 680 // Requested ("explicit") regions are the regions passed into the call 681 // directly, but not all of them end up being invalidated. 682 // But when they do, they appear in the InvalidatedRegions array as well. 683 for (const auto *Region : RequestedRegions) { 684 if (ThisRegion != Region) { 685 if (llvm::find(InvalidatedRegions, Region) != 686 std::end(InvalidatedRegions)) { 687 State = removeFromState(State, Region); 688 } 689 } 690 } 691 } else { 692 // For invalidations that aren't caused by calls, assume nothing. In 693 // particular, direct write into an object's field invalidates the status. 694 for (const auto *Region : InvalidatedRegions) 695 State = removeFromState(State, Region->getBaseRegion()); 696 } 697 698 return State; 699 } 700 701 void MoveChecker::printState(raw_ostream &Out, ProgramStateRef State, 702 const char *NL, const char *Sep) const { 703 704 TrackedRegionMapTy RS = State->get<TrackedRegionMap>(); 705 706 if (!RS.isEmpty()) { 707 Out << Sep << "Moved-from objects :" << NL; 708 for (auto I: RS) { 709 I.first->dumpToStream(Out); 710 if (I.second.isMoved()) 711 Out << ": moved"; 712 else 713 Out << ": moved and reported"; 714 Out << NL; 715 } 716 } 717 } 718 void ento::registerMoveChecker(CheckerManager &mgr) { 719 MoveChecker *chk = mgr.registerChecker<MoveChecker>(); 720 chk->setAggressiveness(mgr.getAnalyzerOptions().getCheckerBooleanOption( 721 "Aggressive", false, chk)); 722 } 723