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