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