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