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