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