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 "Move.h" 16 #include "clang/AST/Attr.h" 17 #include "clang/AST/ExprCXX.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 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(ProgramStateRef State, const MemRegion *MR, 148 const CXXRecordDecl *RD) const; 149 150 // Classifies the object and dumps a user-friendly description string to 151 // the stream. 152 void explainObject(ProgramStateRef State, llvm::raw_ostream &OS, 153 const MemRegion *MR, const CXXRecordDecl *RD, 154 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 PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, 175 BugReporterContext &BRC, 176 PathSensitiveBugReport &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 = AK_KnownsAndLocals; 190 191 public: 192 void setAggressiveness(StringRef Str, CheckerManager &Mgr) { 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_Invalid); 199 200 if (Aggressiveness == AK_Invalid) 201 Mgr.reportInvalidCheckerOptionValue(this, "WarnOn", 202 "either \"KnownsOnly\", \"KnownsAndLocals\" or \"All\" string value"); 203 }; 204 205 private: 206 BugType BT{this, "Use-after-move", categories::CXXMoveSemantics}; 207 208 // Check if the given form of potential misuse of a given object 209 // should be reported. If so, get it reported. The callback from which 210 // this function was called should immediately return after the call 211 // because this function adds one or two transitions. 212 void modelUse(ProgramStateRef State, const MemRegion *Region, 213 const CXXRecordDecl *RD, MisuseKind MK, 214 CheckerContext &C) const; 215 216 // Returns the exploded node against which the report was emitted. 217 // The caller *must* add any further transitions against this node. 218 // Returns nullptr and does not report if such node already exists. 219 ExplodedNode *tryToReportBug(const MemRegion *Region, const CXXRecordDecl *RD, 220 CheckerContext &C, MisuseKind MK) const; 221 222 bool isInMoveSafeContext(const LocationContext *LC) const; 223 bool isStateResetMethod(const CXXMethodDecl *MethodDec) const; 224 bool isMoveSafeMethod(const CXXMethodDecl *MethodDec) const; 225 const ExplodedNode *getMoveLocation(const ExplodedNode *N, 226 const MemRegion *Region, 227 CheckerContext &C) const; 228 }; 229 } // end anonymous namespace 230 231 REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, RegionState) 232 233 // Define the inter-checker API. 234 namespace clang { 235 namespace ento { 236 namespace move { 237 bool isMovedFrom(ProgramStateRef State, const MemRegion *Region) { 238 const RegionState *RS = State->get<TrackedRegionMap>(Region); 239 return RS && (RS->isMoved() || RS->isReported()); 240 } 241 } // namespace move 242 } // namespace ento 243 } // namespace clang 244 245 // If a region is removed all of the subregions needs to be removed too. 246 static ProgramStateRef removeFromState(ProgramStateRef State, 247 const MemRegion *Region) { 248 if (!Region) 249 return State; 250 for (auto &E : State->get<TrackedRegionMap>()) { 251 if (E.first->isSubRegionOf(Region)) 252 State = State->remove<TrackedRegionMap>(E.first); 253 } 254 return State; 255 } 256 257 static bool isAnyBaseRegionReported(ProgramStateRef State, 258 const MemRegion *Region) { 259 for (auto &E : State->get<TrackedRegionMap>()) { 260 if (Region->isSubRegionOf(E.first) && E.second.isReported()) 261 return true; 262 } 263 return false; 264 } 265 266 static const MemRegion *unwrapRValueReferenceIndirection(const MemRegion *MR) { 267 if (const auto *SR = dyn_cast_or_null<SymbolicRegion>(MR)) { 268 SymbolRef Sym = SR->getSymbol(); 269 if (Sym->getType()->isRValueReferenceType()) 270 if (const MemRegion *OriginMR = Sym->getOriginRegion()) 271 return OriginMR; 272 } 273 return MR; 274 } 275 276 PathDiagnosticPieceRef 277 MoveChecker::MovedBugVisitor::VisitNode(const ExplodedNode *N, 278 BugReporterContext &BRC, 279 PathSensitiveBugReport &BR) { 280 // We need only the last move of the reported object's region. 281 // The visitor walks the ExplodedGraph backwards. 282 if (Found) 283 return nullptr; 284 ProgramStateRef State = N->getState(); 285 ProgramStateRef StatePrev = N->getFirstPred()->getState(); 286 const RegionState *TrackedObject = State->get<TrackedRegionMap>(Region); 287 const RegionState *TrackedObjectPrev = 288 StatePrev->get<TrackedRegionMap>(Region); 289 if (!TrackedObject) 290 return nullptr; 291 if (TrackedObjectPrev && TrackedObject) 292 return nullptr; 293 294 // Retrieve the associated statement. 295 const Stmt *S = N->getStmtForDiagnostics(); 296 if (!S) 297 return nullptr; 298 Found = true; 299 300 SmallString<128> Str; 301 llvm::raw_svector_ostream OS(Str); 302 303 ObjectKind OK = Chk.classifyObject(State, Region, RD); 304 switch (OK.StdKind) { 305 case SK_SmartPtr: 306 if (MK == MK_Dereference) { 307 OS << "Smart pointer"; 308 Chk.explainObject(State, OS, Region, RD, MK); 309 OS << " is reset to null when moved from"; 310 break; 311 } 312 313 // If it's not a dereference, we don't care if it was reset to null 314 // or that it is even a smart pointer. 315 [[fallthrough]]; 316 case SK_NonStd: 317 case SK_Safe: 318 OS << "Object"; 319 Chk.explainObject(State, OS, Region, RD, MK); 320 OS << " is moved"; 321 break; 322 case SK_Unsafe: 323 OS << "Object"; 324 Chk.explainObject(State, OS, Region, RD, MK); 325 OS << " is left in a valid but unspecified state after move"; 326 break; 327 } 328 329 // Generate the extra diagnostic. 330 PathDiagnosticLocation Pos(S, BRC.getSourceManager(), 331 N->getLocationContext()); 332 return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true); 333 } 334 335 const ExplodedNode *MoveChecker::getMoveLocation(const ExplodedNode *N, 336 const MemRegion *Region, 337 CheckerContext &C) const { 338 // Walk the ExplodedGraph backwards and find the first node that referred to 339 // the tracked region. 340 const ExplodedNode *MoveNode = N; 341 342 while (N) { 343 ProgramStateRef State = N->getState(); 344 if (!State->get<TrackedRegionMap>(Region)) 345 break; 346 MoveNode = N; 347 N = N->pred_empty() ? nullptr : *(N->pred_begin()); 348 } 349 return MoveNode; 350 } 351 352 void MoveChecker::modelUse(ProgramStateRef State, const MemRegion *Region, 353 const CXXRecordDecl *RD, MisuseKind MK, 354 CheckerContext &C) const { 355 assert(!C.isDifferent() && "No transitions should have been made by now"); 356 const RegionState *RS = State->get<TrackedRegionMap>(Region); 357 ObjectKind OK = classifyObject(State, Region, RD); 358 359 // Just in case: if it's not a smart pointer but it does have operator *, 360 // we shouldn't call the bug a dereference. 361 if (MK == MK_Dereference && OK.StdKind != SK_SmartPtr) 362 MK = MK_FunCall; 363 364 if (!RS || !shouldWarnAbout(OK, MK) 365 || isInMoveSafeContext(C.getLocationContext())) { 366 // Finalize changes made by the caller. 367 C.addTransition(State); 368 return; 369 } 370 371 // Don't report it in case if any base region is already reported. 372 // But still generate a sink in case of UB. 373 // And still finalize changes made by the caller. 374 if (isAnyBaseRegionReported(State, Region)) { 375 if (misuseCausesCrash(MK)) { 376 C.generateSink(State, C.getPredecessor()); 377 } else { 378 C.addTransition(State); 379 } 380 return; 381 } 382 383 ExplodedNode *N = tryToReportBug(Region, RD, C, MK); 384 385 // If the program has already crashed on this path, don't bother. 386 if (!N || N->isSink()) 387 return; 388 389 State = State->set<TrackedRegionMap>(Region, RegionState::getReported()); 390 C.addTransition(State, N); 391 } 392 393 ExplodedNode *MoveChecker::tryToReportBug(const MemRegion *Region, 394 const CXXRecordDecl *RD, 395 CheckerContext &C, 396 MisuseKind MK) const { 397 if (ExplodedNode *N = misuseCausesCrash(MK) ? C.generateErrorNode() 398 : C.generateNonFatalErrorNode()) { 399 // Uniqueing report to the same object. 400 PathDiagnosticLocation LocUsedForUniqueing; 401 const ExplodedNode *MoveNode = getMoveLocation(N, Region, C); 402 403 if (const Stmt *MoveStmt = MoveNode->getStmtForDiagnostics()) 404 LocUsedForUniqueing = PathDiagnosticLocation::createBegin( 405 MoveStmt, C.getSourceManager(), MoveNode->getLocationContext()); 406 407 // Creating the error message. 408 llvm::SmallString<128> Str; 409 llvm::raw_svector_ostream OS(Str); 410 ProgramStateRef State = N->getState(); 411 switch(MK) { 412 case MK_FunCall: 413 OS << "Method called on moved-from object"; 414 explainObject(State, OS, Region, RD, MK); 415 break; 416 case MK_Copy: 417 OS << "Moved-from object"; 418 explainObject(State, OS, Region, RD, MK); 419 OS << " is copied"; 420 break; 421 case MK_Move: 422 OS << "Moved-from object"; 423 explainObject(State, OS, Region, RD, MK); 424 OS << " is moved"; 425 break; 426 case MK_Dereference: 427 OS << "Dereference of null smart pointer"; 428 explainObject(State, OS, Region, RD, MK); 429 break; 430 } 431 432 auto R = std::make_unique<PathSensitiveBugReport>( 433 BT, OS.str(), N, LocUsedForUniqueing, 434 MoveNode->getLocationContext()->getDecl()); 435 R->addVisitor(std::make_unique<MovedBugVisitor>(*this, Region, RD, MK)); 436 C.emitReport(std::move(R)); 437 return N; 438 } 439 return nullptr; 440 } 441 442 void MoveChecker::checkPostCall(const CallEvent &Call, 443 CheckerContext &C) const { 444 const auto *AFC = dyn_cast<AnyFunctionCall>(&Call); 445 if (!AFC) 446 return; 447 448 ProgramStateRef State = C.getState(); 449 const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(AFC->getDecl()); 450 if (!MethodDecl) 451 return; 452 453 // Check if an object became moved-from. 454 // Object can become moved from after a call to move assignment operator or 455 // move constructor . 456 const auto *ConstructorDecl = dyn_cast<CXXConstructorDecl>(MethodDecl); 457 if (ConstructorDecl && !ConstructorDecl->isMoveConstructor()) 458 return; 459 460 if (!ConstructorDecl && !MethodDecl->isMoveAssignmentOperator()) 461 return; 462 463 const auto ArgRegion = AFC->getArgSVal(0).getAsRegion(); 464 if (!ArgRegion) 465 return; 466 467 // Skip moving the object to itself. 468 const auto *CC = dyn_cast_or_null<CXXConstructorCall>(&Call); 469 if (CC && CC->getCXXThisVal().getAsRegion() == ArgRegion) 470 return; 471 472 if (const auto *IC = dyn_cast<CXXInstanceCall>(AFC)) 473 if (IC->getCXXThisVal().getAsRegion() == ArgRegion) 474 return; 475 476 const MemRegion *BaseRegion = ArgRegion->getBaseRegion(); 477 // Skip temp objects because of their short lifetime. 478 if (BaseRegion->getAs<CXXTempObjectRegion>() || 479 AFC->getArgExpr(0)->isPRValue()) 480 return; 481 // If it has already been reported do not need to modify the state. 482 483 if (State->get<TrackedRegionMap>(ArgRegion)) 484 return; 485 486 const CXXRecordDecl *RD = MethodDecl->getParent(); 487 ObjectKind OK = classifyObject(State, ArgRegion, RD); 488 if (shouldBeTracked(OK)) { 489 // Mark object as moved-from. 490 State = State->set<TrackedRegionMap>(ArgRegion, RegionState::getMoved()); 491 C.addTransition(State); 492 return; 493 } 494 assert(!C.isDifferent() && "Should not have made transitions on this path!"); 495 } 496 497 bool MoveChecker::isMoveSafeMethod(const CXXMethodDecl *MethodDec) const { 498 // We abandon the cases where bool/void/void* conversion happens. 499 if (const auto *ConversionDec = 500 dyn_cast_or_null<CXXConversionDecl>(MethodDec)) { 501 const Type *Tp = ConversionDec->getConversionType().getTypePtrOrNull(); 502 if (!Tp) 503 return false; 504 if (Tp->isBooleanType() || Tp->isVoidType() || Tp->isVoidPointerType()) 505 return true; 506 } 507 // Function call `empty` can be skipped. 508 return (MethodDec && MethodDec->getDeclName().isIdentifier() && 509 (MethodDec->getName().lower() == "empty" || 510 MethodDec->getName().lower() == "isempty")); 511 } 512 513 bool MoveChecker::isStateResetMethod(const CXXMethodDecl *MethodDec) const { 514 if (!MethodDec) 515 return false; 516 if (MethodDec->hasAttr<ReinitializesAttr>()) 517 return true; 518 if (MethodDec->getDeclName().isIdentifier()) { 519 std::string MethodName = MethodDec->getName().lower(); 520 // TODO: Some of these methods (eg., resize) are not always resetting 521 // the state, so we should consider looking at the arguments. 522 if (MethodName == "assign" || MethodName == "clear" || 523 MethodName == "destroy" || MethodName == "reset" || 524 MethodName == "resize" || MethodName == "shrink") 525 return true; 526 } 527 return false; 528 } 529 530 // Don't report an error inside a move related operation. 531 // We assume that the programmer knows what she does. 532 bool MoveChecker::isInMoveSafeContext(const LocationContext *LC) const { 533 do { 534 const auto *CtxDec = LC->getDecl(); 535 auto *CtorDec = dyn_cast_or_null<CXXConstructorDecl>(CtxDec); 536 auto *DtorDec = dyn_cast_or_null<CXXDestructorDecl>(CtxDec); 537 auto *MethodDec = dyn_cast_or_null<CXXMethodDecl>(CtxDec); 538 if (DtorDec || (CtorDec && CtorDec->isCopyOrMoveConstructor()) || 539 (MethodDec && MethodDec->isOverloadedOperator() && 540 MethodDec->getOverloadedOperator() == OO_Equal) || 541 isStateResetMethod(MethodDec) || isMoveSafeMethod(MethodDec)) 542 return true; 543 } while ((LC = LC->getParent())); 544 return false; 545 } 546 547 bool MoveChecker::belongsTo(const CXXRecordDecl *RD, 548 const llvm::StringSet<> &Set) const { 549 const IdentifierInfo *II = RD->getIdentifier(); 550 return II && Set.count(II->getName()); 551 } 552 553 MoveChecker::ObjectKind 554 MoveChecker::classifyObject(ProgramStateRef State, const MemRegion *MR, 555 const CXXRecordDecl *RD) const { 556 // Local variables and local rvalue references are classified as "Local". 557 // For the purposes of this checker, we classify move-safe STL types 558 // as not-"STL" types, because that's how the checker treats them. 559 MR = unwrapRValueReferenceIndirection(MR); 560 bool IsLocal = 561 isa_and_nonnull<VarRegion, CXXLifetimeExtendedObjectRegion>(MR) && 562 MR->hasMemorySpace<StackSpaceRegion>(State); 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(ProgramStateRef State, llvm::raw_ostream &OS, 577 const MemRegion *MR, const CXXRecordDecl *RD, 578 MisuseKind MK) const { 579 // We may need a leading space every time we actually explain anything, 580 // and we never know if we are to explain anything until we try. 581 if (const auto DR = 582 dyn_cast_or_null<DeclRegion>(unwrapRValueReferenceIndirection(MR))) { 583 const auto *RegionDecl = cast<NamedDecl>(DR->getDecl()); 584 OS << " '" << RegionDecl->getDeclName() << "'"; 585 } 586 587 ObjectKind OK = classifyObject(State, MR, RD); 588 switch (OK.StdKind) { 589 case SK_NonStd: 590 case SK_Safe: 591 break; 592 case SK_SmartPtr: 593 if (MK != MK_Dereference) 594 break; 595 596 // We only care about the type if it's a dereference. 597 [[fallthrough]]; 598 case SK_Unsafe: 599 OS << " of type '" << RD->getQualifiedNameAsString() << "'"; 600 break; 601 }; 602 } 603 604 void MoveChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { 605 ProgramStateRef State = C.getState(); 606 607 // Remove the MemRegions from the map on which a ctor/dtor call or assignment 608 // happened. 609 610 // Checking constructor calls. 611 if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) { 612 State = removeFromState(State, CC->getCXXThisVal().getAsRegion()); 613 auto CtorDec = CC->getDecl(); 614 // Check for copying a moved-from object and report the bug. 615 if (CtorDec && CtorDec->isCopyOrMoveConstructor()) { 616 const MemRegion *ArgRegion = CC->getArgSVal(0).getAsRegion(); 617 const CXXRecordDecl *RD = CtorDec->getParent(); 618 MisuseKind MK = CtorDec->isMoveConstructor() ? MK_Move : MK_Copy; 619 modelUse(State, ArgRegion, RD, MK, C); 620 return; 621 } 622 } 623 624 const auto IC = dyn_cast<CXXInstanceCall>(&Call); 625 if (!IC) 626 return; 627 628 const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion(); 629 if (!ThisRegion) 630 return; 631 632 // The remaining part is check only for method call on a moved-from object. 633 const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(IC->getDecl()); 634 if (!MethodDecl) 635 return; 636 637 // Calling a destructor on a moved object is fine. 638 if (isa<CXXDestructorDecl>(MethodDecl)) 639 return; 640 641 // We want to investigate the whole object, not only sub-object of a parent 642 // class in which the encountered method defined. 643 ThisRegion = ThisRegion->getMostDerivedObjectRegion(); 644 645 if (isStateResetMethod(MethodDecl)) { 646 State = removeFromState(State, ThisRegion); 647 C.addTransition(State); 648 return; 649 } 650 651 if (isMoveSafeMethod(MethodDecl)) 652 return; 653 654 // Store class declaration as well, for bug reporting purposes. 655 const CXXRecordDecl *RD = MethodDecl->getParent(); 656 657 if (MethodDecl->isOverloadedOperator()) { 658 OverloadedOperatorKind OOK = MethodDecl->getOverloadedOperator(); 659 660 if (OOK == OO_Equal) { 661 // Remove the tracked object for every assignment operator, but report bug 662 // only for move or copy assignment's argument. 663 State = removeFromState(State, ThisRegion); 664 665 if (MethodDecl->isCopyAssignmentOperator() || 666 MethodDecl->isMoveAssignmentOperator()) { 667 const MemRegion *ArgRegion = IC->getArgSVal(0).getAsRegion(); 668 MisuseKind MK = 669 MethodDecl->isMoveAssignmentOperator() ? MK_Move : MK_Copy; 670 modelUse(State, ArgRegion, RD, MK, C); 671 return; 672 } 673 C.addTransition(State); 674 return; 675 } 676 677 if (OOK == OO_Star || OOK == OO_Arrow) { 678 modelUse(State, ThisRegion, RD, MK_Dereference, C); 679 return; 680 } 681 } 682 683 modelUse(State, ThisRegion, RD, MK_FunCall, C); 684 } 685 686 void MoveChecker::checkDeadSymbols(SymbolReaper &SymReaper, 687 CheckerContext &C) const { 688 ProgramStateRef State = C.getState(); 689 TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>(); 690 for (auto E : TrackedRegions) { 691 const MemRegion *Region = E.first; 692 bool IsRegDead = !SymReaper.isLiveRegion(Region); 693 694 // Remove the dead regions from the region map. 695 if (IsRegDead) { 696 State = State->remove<TrackedRegionMap>(Region); 697 } 698 } 699 C.addTransition(State); 700 } 701 702 ProgramStateRef MoveChecker::checkRegionChanges( 703 ProgramStateRef State, const InvalidatedSymbols *Invalidated, 704 ArrayRef<const MemRegion *> RequestedRegions, 705 ArrayRef<const MemRegion *> InvalidatedRegions, 706 const LocationContext *LCtx, const CallEvent *Call) const { 707 if (Call) { 708 // Relax invalidation upon function calls: only invalidate parameters 709 // that are passed directly via non-const pointers or non-const references 710 // or rvalue references. 711 // In case of an InstanceCall don't invalidate the this-region since 712 // it is fully handled in checkPreCall and checkPostCall. 713 const MemRegion *ThisRegion = nullptr; 714 if (const auto *IC = dyn_cast<CXXInstanceCall>(Call)) 715 ThisRegion = IC->getCXXThisVal().getAsRegion(); 716 717 // Requested ("explicit") regions are the regions passed into the call 718 // directly, but not all of them end up being invalidated. 719 // But when they do, they appear in the InvalidatedRegions array as well. 720 for (const auto *Region : RequestedRegions) { 721 if (ThisRegion != Region && 722 llvm::is_contained(InvalidatedRegions, Region)) 723 State = removeFromState(State, Region); 724 } 725 } else { 726 // For invalidations that aren't caused by calls, assume nothing. In 727 // particular, direct write into an object's field invalidates the status. 728 for (const auto *Region : InvalidatedRegions) 729 State = removeFromState(State, Region->getBaseRegion()); 730 } 731 732 return State; 733 } 734 735 void MoveChecker::printState(raw_ostream &Out, ProgramStateRef State, 736 const char *NL, const char *Sep) const { 737 738 TrackedRegionMapTy RS = State->get<TrackedRegionMap>(); 739 740 if (!RS.isEmpty()) { 741 Out << Sep << "Moved-from objects :" << NL; 742 for (auto I: RS) { 743 I.first->dumpToStream(Out); 744 if (I.second.isMoved()) 745 Out << ": moved"; 746 else 747 Out << ": moved and reported"; 748 Out << NL; 749 } 750 } 751 } 752 void ento::registerMoveChecker(CheckerManager &mgr) { 753 MoveChecker *chk = mgr.registerChecker<MoveChecker>(); 754 chk->setAggressiveness( 755 mgr.getAnalyzerOptions().getCheckerStringOption(chk, "WarnOn"), mgr); 756 } 757 758 bool ento::shouldRegisterMoveChecker(const CheckerManager &mgr) { 759 return true; 760 } 761