1193323Sed// MoveChecker.cpp - Check use of moved-from objects. - C++ ---------------===// 2193323Sed// 3193323Sed// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4193323Sed// See https://llvm.org/LICENSE.txt for license information. 5193323Sed// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6193323Sed// 7193323Sed//===----------------------------------------------------------------------===// 8193323Sed// 9193323Sed// This defines checker which checks for potential misuses of a moved-from 10193323Sed// object. That means method calls on the object or copying it in moved-from 11193323Sed// state. 12193323Sed// 13193323Sed//===----------------------------------------------------------------------===// 14193323Sed 15193323Sed#include "clang/AST/Attr.h" 16193323Sed#include "clang/AST/ExprCXX.h" 17193323Sed#include "clang/Driver/DriverDiagnostic.h" 18193323Sed#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 19193323Sed#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 20193323Sed#include "clang/StaticAnalyzer/Core/Checker.h" 21193323Sed#include "clang/StaticAnalyzer/Core/CheckerManager.h" 22193323Sed#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 23193323Sed#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 24193323Sed#include "llvm/ADT/StringSet.h" 25193323Sed 26193323Sedusing namespace clang; 27193323Sedusing namespace ento; 28193323Sed 29193323Sednamespace { 30198090Srdivackystruct RegionState { 31198090Srdivackyprivate: 32193323Sed enum Kind { Moved, Reported } K; 33193323Sed RegionState(Kind InK) : K(InK) {} 34199481Srdivacky 35199481Srdivackypublic: 36199481Srdivacky bool isReported() const { return K == Reported; } 37199481Srdivacky bool isMoved() const { return K == Moved; } 38199481Srdivacky 39199481Srdivacky static RegionState getReported() { return RegionState(Reported); } 40199481Srdivacky static RegionState getMoved() { return RegionState(Moved); } 41199481Srdivacky 42199481Srdivacky bool operator==(const RegionState &X) const { return K == X.K; } 43199481Srdivacky void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); } 44199481Srdivacky}; 45199481Srdivacky} // end of anonymous namespace 46199481Srdivacky 47207618Srdivackynamespace { 48207618Srdivackyclass MoveChecker 49207618Srdivacky : public Checker<check::PreCall, check::PostCall, 50199481Srdivacky check::DeadSymbols, check::RegionChanges> { 51199481Srdivackypublic: 52199481Srdivacky void checkPreCall(const CallEvent &MC, CheckerContext &C) const; 53199481Srdivacky void checkPostCall(const CallEvent &MC, CheckerContext &C) const; 54199481Srdivacky void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; 55199481Srdivacky ProgramStateRef 56199481Srdivacky checkRegionChanges(ProgramStateRef State, 57199481Srdivacky const InvalidatedSymbols *Invalidated, 58199481Srdivacky ArrayRef<const MemRegion *> RequestedRegions, 59199481Srdivacky ArrayRef<const MemRegion *> InvalidatedRegions, 60199481Srdivacky const LocationContext *LCtx, const CallEvent *Call) const; 61199481Srdivacky void printState(raw_ostream &Out, ProgramStateRef State, 62199481Srdivacky const char *NL, const char *Sep) const override; 63199481Srdivacky 64199481Srdivackyprivate: 65199481Srdivacky enum MisuseKind { MK_FunCall, MK_Copy, MK_Move, MK_Dereference }; 66199481Srdivacky enum StdObjectKind { SK_NonStd, SK_Unsafe, SK_Safe, SK_SmartPtr }; 67199481Srdivacky 68199481Srdivacky enum AggressivenessKind { // In any case, don't warn after a reset. 69199481Srdivacky AK_Invalid = -1, 70199481Srdivacky AK_KnownsOnly = 0, // Warn only about known move-unsafe classes. 71199481Srdivacky AK_KnownsAndLocals = 1, // Also warn about all local objects. 72199481Srdivacky AK_All = 2, // Warn on any use-after-move. 73199481Srdivacky AK_NumKinds = AK_All 74200581Srdivacky }; 75199481Srdivacky 76199481Srdivacky static bool misuseCausesCrash(MisuseKind MK) { 77200581Srdivacky return MK == MK_Dereference; 78199481Srdivacky } 79199481Srdivacky 80199481Srdivacky struct ObjectKind { 81199481Srdivacky // Is this a local variable or a local rvalue reference? 82199481Srdivacky bool IsLocal; 83199481Srdivacky // Is this an STL object? If so, of what kind? 84199481Srdivacky StdObjectKind StdKind; 85199481Srdivacky }; 86199481Srdivacky 87199481Srdivacky // STL smart pointers are automatically re-initialized to null when moved 88199481Srdivacky // from. So we can't warn on many methods, but we can warn when it is 89199481Srdivacky // dereferenced, which is UB even if the resulting lvalue never gets read. 90199481Srdivacky const llvm::StringSet<> StdSmartPtrClasses = { 91199481Srdivacky "shared_ptr", 92199481Srdivacky "unique_ptr", 93199481Srdivacky "weak_ptr", 94199481Srdivacky }; 95199481Srdivacky 96199481Srdivacky // Not all of these are entirely move-safe, but they do provide *some* 97193323Sed // guarantees, and it means that somebody is using them after move 98193323Sed // in a valid manner. 99193323Sed // TODO: We can still try to identify *unsafe* use after move, 100193323Sed // like we did with smart pointers. 101193323Sed const llvm::StringSet<> StdSafeClasses = { 102207618Srdivacky "basic_filebuf", 103193323Sed "basic_ios", 104193323Sed "future", 105193323Sed "optional", 106193323Sed "packaged_task", 107193323Sed "promise", 108193323Sed "shared_future", 109193323Sed "shared_lock", 110193323Sed "thread", 111193323Sed "unique_lock", 112193323Sed }; 113193323Sed 114193323Sed // Should we bother tracking the state of the object? 115199481Srdivacky bool shouldBeTracked(ObjectKind OK) const { 116199481Srdivacky // In non-aggressive mode, only warn on use-after-move of local variables 117199481Srdivacky // (or local rvalue references) and of STL objects. The former is possible 118199481Srdivacky // because local variables (or local rvalue references) are not tempting 119198090Srdivacky // their user to re-use the storage. The latter is possible because STL 120198090Srdivacky // objects are known to end up in a valid but unspecified state after the 121198090Srdivacky // move and their state-reset methods are also known, which allows us to 122198090Srdivacky // predict precisely when use-after-move is invalid. 123193323Sed // Some STL objects are known to conform to additional contracts after move, 124193323Sed // so they are not tracked. However, smart pointers specifically are tracked 125193323Sed // because we can perform extra checking over them. 126193323Sed // In aggressive mode, warn on any use-after-move because the user has 127202375Srdivacky // intentionally asked us to completely eliminate use-after-move 128202375Srdivacky // in his code. 129202375Srdivacky return (Aggressiveness == AK_All) || 130199481Srdivacky (Aggressiveness >= AK_KnownsAndLocals && OK.IsLocal) || 131199481Srdivacky OK.StdKind == SK_Unsafe || OK.StdKind == SK_SmartPtr; 132202375Srdivacky } 133193323Sed 134193323Sed // Some objects only suffer from some kinds of misuses, but we need to track 135193323Sed // them anyway because we cannot know in advance what misuse will we find. 136193323Sed bool shouldWarnAbout(ObjectKind OK, MisuseKind MK) const { 137193323Sed // Additionally, only warn on smart pointers when they are dereferenced (or 138193323Sed // local or we are aggressive). 139193323Sed return shouldBeTracked(OK) && 140193323Sed ((Aggressiveness == AK_All) || 141193323Sed (Aggressiveness >= AK_KnownsAndLocals && OK.IsLocal) || 142193323Sed OK.StdKind != SK_SmartPtr || MK == MK_Dereference); 143193323Sed } 144199481Srdivacky 145199481Srdivacky // Obtains ObjectKind of an object. Because class declaration cannot always 146199481Srdivacky // be easily obtained from the memory region, it is supplied separately. 147199481Srdivacky ObjectKind classifyObject(const MemRegion *MR, const CXXRecordDecl *RD) const; 148199481Srdivacky 149199481Srdivacky // Classifies the object and dumps a user-friendly description string to 150199481Srdivacky // the stream. 151199481Srdivacky void explainObject(llvm::raw_ostream &OS, const MemRegion *MR, 152193323Sed const CXXRecordDecl *RD, MisuseKind MK) const; 153199481Srdivacky 154199481Srdivacky bool belongsTo(const CXXRecordDecl *RD, const llvm::StringSet<> &Set) const; 155199481Srdivacky 156199481Srdivacky class MovedBugVisitor : public BugReporterVisitor { 157199481Srdivacky public: 158199481Srdivacky MovedBugVisitor(const MoveChecker &Chk, const MemRegion *R, 159199481Srdivacky const CXXRecordDecl *RD, MisuseKind MK) 160199481Srdivacky : Chk(Chk), Region(R), RD(RD), MK(MK), Found(false) {} 161199481Srdivacky 162199481Srdivacky void Profile(llvm::FoldingSetNodeID &ID) const override { 163199481Srdivacky static int X = 0; 164199481Srdivacky ID.AddPointer(&X); 165199481Srdivacky ID.AddPointer(Region); 166199481Srdivacky // Don't add RD because it's, in theory, uniquely determined by 167199481Srdivacky // the region. In practice though, it's not always possible to obtain 168199481Srdivacky // the declaration directly from the region, that's why we store it 169199481Srdivacky // in the first place. 170199481Srdivacky } 171199481Srdivacky 172199481Srdivacky PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, 173199481Srdivacky BugReporterContext &BRC, 174193323Sed PathSensitiveBugReport &BR) override; 175199481Srdivacky 176199481Srdivacky private: 177193323Sed const MoveChecker &Chk; 178199481Srdivacky // The tracked region. 179199481Srdivacky const MemRegion *Region; 180199481Srdivacky // The class of the tracked object. 181199481Srdivacky const CXXRecordDecl *RD; 182199481Srdivacky // How exactly the object was misused. 183199481Srdivacky const MisuseKind MK; 184199481Srdivacky bool Found; 185199481Srdivacky }; 186193323Sed 187199481Srdivacky AggressivenessKind Aggressiveness; 188199481Srdivacky 189199481Srdivackypublic: 190199481Srdivacky void setAggressiveness(StringRef Str, CheckerManager &Mgr) { 191199481Srdivacky Aggressiveness = 192199481Srdivacky llvm::StringSwitch<AggressivenessKind>(Str) 193199481Srdivacky .Case("KnownsOnly", AK_KnownsOnly) 194204642Srdivacky .Case("KnownsAndLocals", AK_KnownsAndLocals) 195199481Srdivacky .Case("All", AK_All) 196199481Srdivacky .Default(AK_Invalid); 197199481Srdivacky 198199481Srdivacky if (Aggressiveness == AK_Invalid) 199199481Srdivacky Mgr.reportInvalidCheckerOptionValue(this, "WarnOn", 200199481Srdivacky "either \"KnownsOnly\", \"KnownsAndLocals\" or \"All\" string value"); 201199481Srdivacky }; 202199481Srdivacky 203199481Srdivackyprivate: 204199481Srdivacky BugType BT{this, "Use-after-move", categories::CXXMoveSemantics}; 205199481Srdivacky 206199481Srdivacky // Check if the given form of potential misuse of a given object 207199481Srdivacky // should be reported. If so, get it reported. The callback from which 208199481Srdivacky // this function was called should immediately return after the call 209199481Srdivacky // because this function adds one or two transitions. 210199481Srdivacky void modelUse(ProgramStateRef State, const MemRegion *Region, 211199481Srdivacky const CXXRecordDecl *RD, MisuseKind MK, 212199481Srdivacky CheckerContext &C) const; 213199481Srdivacky 214199481Srdivacky // Returns the exploded node against which the report was emitted. 215193323Sed // The caller *must* add any further transitions against this node. 216193323Sed ExplodedNode *reportBug(const MemRegion *Region, const CXXRecordDecl *RD, 217199481Srdivacky CheckerContext &C, MisuseKind MK) const; 218199481Srdivacky 219199481Srdivacky bool isInMoveSafeContext(const LocationContext *LC) const; 220199481Srdivacky bool isStateResetMethod(const CXXMethodDecl *MethodDec) const; 221199481Srdivacky bool isMoveSafeMethod(const CXXMethodDecl *MethodDec) const; 222199481Srdivacky const ExplodedNode *getMoveLocation(const ExplodedNode *N, 223199481Srdivacky const MemRegion *Region, 224199481Srdivacky CheckerContext &C) const; 225199481Srdivacky}; 226199481Srdivacky} // end anonymous namespace 227199481Srdivacky 228199481SrdivackyREGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, RegionState) 229199481Srdivacky 230199481Srdivacky// Define the inter-checker API. 231199481Srdivackynamespace clang { 232199481Srdivackynamespace ento { 233199481Srdivackynamespace move { 234199481Srdivackybool isMovedFrom(ProgramStateRef State, const MemRegion *Region) { 235199481Srdivacky const RegionState *RS = State->get<TrackedRegionMap>(Region); 236199481Srdivacky return RS && (RS->isMoved() || RS->isReported()); 237199481Srdivacky} 238199481Srdivacky} // namespace move 239199481Srdivacky} // namespace ento 240199481Srdivacky} // namespace clang 241199481Srdivacky 242199481Srdivacky// If a region is removed all of the subregions needs to be removed too. 243199481Srdivackystatic ProgramStateRef removeFromState(ProgramStateRef State, 244199481Srdivacky const MemRegion *Region) { 245199481Srdivacky if (!Region) 246199481Srdivacky return State; 247193323Sed for (auto &E : State->get<TrackedRegionMap>()) { 248193323Sed if (E.first->isSubRegionOf(Region)) 249199481Srdivacky State = State->remove<TrackedRegionMap>(E.first); 250193323Sed } 251199481Srdivacky return State; 252199481Srdivacky} 253193323Sed 254199481Srdivackystatic bool isAnyBaseRegionReported(ProgramStateRef State, 255199481Srdivacky const MemRegion *Region) { 256199481Srdivacky for (auto &E : State->get<TrackedRegionMap>()) { 257202375Srdivacky if (Region->isSubRegionOf(E.first) && E.second.isReported()) 258199481Srdivacky return true; 259199481Srdivacky } 260199481Srdivacky return false; 261199481Srdivacky} 262199481Srdivacky 263199481Srdivackystatic const MemRegion *unwrapRValueReferenceIndirection(const MemRegion *MR) { 264199481Srdivacky if (const auto *SR = dyn_cast_or_null<SymbolicRegion>(MR)) { 265199481Srdivacky SymbolRef Sym = SR->getSymbol(); 266199481Srdivacky if (Sym->getType()->isRValueReferenceType()) 267199481Srdivacky if (const MemRegion *OriginMR = Sym->getOriginRegion()) 268199481Srdivacky return OriginMR; 269199481Srdivacky } 270199481Srdivacky return MR; 271199481Srdivacky} 272199481Srdivacky 273199481SrdivackyPathDiagnosticPieceRef 274199481SrdivackyMoveChecker::MovedBugVisitor::VisitNode(const ExplodedNode *N, 275210299Sed BugReporterContext &BRC, 276210299Sed PathSensitiveBugReport &BR) { 277199481Srdivacky // We need only the last move of the reported object's region. 278199481Srdivacky // The visitor walks the ExplodedGraph backwards. 279199481Srdivacky if (Found) 280199481Srdivacky return nullptr; 281199481Srdivacky ProgramStateRef State = N->getState(); 282199481Srdivacky ProgramStateRef StatePrev = N->getFirstPred()->getState(); 283199481Srdivacky const RegionState *TrackedObject = State->get<TrackedRegionMap>(Region); 284199481Srdivacky const RegionState *TrackedObjectPrev = 285199481Srdivacky StatePrev->get<TrackedRegionMap>(Region); 286199989Srdivacky if (!TrackedObject) 287199989Srdivacky return nullptr; 288199481Srdivacky if (TrackedObjectPrev && TrackedObject) 289199481Srdivacky return nullptr; 290199481Srdivacky 291193323Sed // Retrieve the associated statement. 292193323Sed const Stmt *S = N->getStmtForDiagnostics(); 293193323Sed if (!S) 294198090Srdivacky return nullptr; 295198090Srdivacky Found = true; 296198090Srdivacky 297198090Srdivacky SmallString<128> Str; 298198090Srdivacky llvm::raw_svector_ostream OS(Str); 299198090Srdivacky 300198090Srdivacky ObjectKind OK = Chk.classifyObject(Region, RD); 301202375Srdivacky switch (OK.StdKind) { 302198090Srdivacky case SK_SmartPtr: 303198090Srdivacky if (MK == MK_Dereference) { 304198090Srdivacky OS << "Smart pointer"; 305193323Sed Chk.explainObject(OS, Region, RD, MK); 306198090Srdivacky OS << " is reset to null when moved from"; 307198090Srdivacky break; 308198090Srdivacky } 309198090Srdivacky 310198090Srdivacky // If it's not a dereference, we don't care if it was reset to null 311199481Srdivacky // or that it is even a smart pointer. 312199481Srdivacky [[fallthrough]]; 313199481Srdivacky case SK_NonStd: 314199481Srdivacky case SK_Safe: 315199481Srdivacky OS << "Object"; 316199481Srdivacky Chk.explainObject(OS, Region, RD, MK); 317199481Srdivacky OS << " is moved"; 318199481Srdivacky break; 319199481Srdivacky case SK_Unsafe: 320199481Srdivacky OS << "Object"; 321199481Srdivacky Chk.explainObject(OS, Region, RD, MK); 322199481Srdivacky OS << " is left in a valid but unspecified state after move"; 323199481Srdivacky break; 324199481Srdivacky } 325199481Srdivacky 326199481Srdivacky // Generate the extra diagnostic. 327199481Srdivacky PathDiagnosticLocation Pos(S, BRC.getSourceManager(), 328199481Srdivacky N->getLocationContext()); 329199481Srdivacky return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true); 330199481Srdivacky} 331199481Srdivacky 332199481Srdivackyconst ExplodedNode *MoveChecker::getMoveLocation(const ExplodedNode *N, 333199481Srdivacky const MemRegion *Region, 334199481Srdivacky CheckerContext &C) const { 335199481Srdivacky // Walk the ExplodedGraph backwards and find the first node that referred to 336199481Srdivacky // the tracked region. 337199481Srdivacky const ExplodedNode *MoveNode = N; 338202375Srdivacky 339202375Srdivacky while (N) { 340199481Srdivacky ProgramStateRef State = N->getState(); 341199481Srdivacky if (!State->get<TrackedRegionMap>(Region)) 342199481Srdivacky break; 343199481Srdivacky MoveNode = N; 344199481Srdivacky N = N->pred_empty() ? nullptr : *(N->pred_begin()); 345199481Srdivacky } 346199481Srdivacky return MoveNode; 347199481Srdivacky} 348199481Srdivacky 349199481Srdivackyvoid MoveChecker::modelUse(ProgramStateRef State, const MemRegion *Region, 350199481Srdivacky const CXXRecordDecl *RD, MisuseKind MK, 351199481Srdivacky CheckerContext &C) const { 352199481Srdivacky assert(!C.isDifferent() && "No transitions should have been made by now"); 353199481Srdivacky const RegionState *RS = State->get<TrackedRegionMap>(Region); 354199481Srdivacky ObjectKind OK = classifyObject(Region, RD); 355199481Srdivacky 356199481Srdivacky // Just in case: if it's not a smart pointer but it does have operator *, 357202375Srdivacky // we shouldn't call the bug a dereference. 358199481Srdivacky if (MK == MK_Dereference && OK.StdKind != SK_SmartPtr) 359199481Srdivacky MK = MK_FunCall; 360199481Srdivacky 361199481Srdivacky if (!RS || !shouldWarnAbout(OK, MK) 362202375Srdivacky || isInMoveSafeContext(C.getLocationContext())) { 363199481Srdivacky // Finalize changes made by the caller. 364199481Srdivacky C.addTransition(State); 365199481Srdivacky return; 366199481Srdivacky } 367207618Srdivacky 368199481Srdivacky // Don't report it in case if any base region is already reported. 369199481Srdivacky // But still generate a sink in case of UB. 370199481Srdivacky // And still finalize changes made by the caller. 371199481Srdivacky if (isAnyBaseRegionReported(State, Region)) { 372199481Srdivacky if (misuseCausesCrash(MK)) { 373199481Srdivacky C.generateSink(State, C.getPredecessor()); 374199481Srdivacky } else { 375199481Srdivacky C.addTransition(State); 376199481Srdivacky } 377199481Srdivacky return; 378202375Srdivacky } 379199481Srdivacky 380199481Srdivacky ExplodedNode *N = reportBug(Region, RD, C, MK); 381199481Srdivacky 382199481Srdivacky // If the program has already crashed on this path, don't bother. 383199481Srdivacky if (N->isSink()) 384199481Srdivacky return; 385199481Srdivacky 386199481Srdivacky State = State->set<TrackedRegionMap>(Region, RegionState::getReported()); 387199481Srdivacky C.addTransition(State, N); 388199481Srdivacky} 389199481Srdivacky 390199481SrdivackyExplodedNode *MoveChecker::reportBug(const MemRegion *Region, 391199481Srdivacky const CXXRecordDecl *RD, CheckerContext &C, 392199481Srdivacky MisuseKind MK) const { 393202375Srdivacky if (ExplodedNode *N = misuseCausesCrash(MK) ? C.generateErrorNode() 394202375Srdivacky : C.generateNonFatalErrorNode()) { 395193323Sed // Uniqueing report to the same object. 396193323Sed PathDiagnosticLocation LocUsedForUniqueing; 397204642Srdivacky const ExplodedNode *MoveNode = getMoveLocation(N, Region, C); 398193323Sed 399198090Srdivacky if (const Stmt *MoveStmt = MoveNode->getStmtForDiagnostics()) 400193323Sed LocUsedForUniqueing = PathDiagnosticLocation::createBegin( 401193323Sed MoveStmt, C.getSourceManager(), MoveNode->getLocationContext()); 402193323Sed 403204642Srdivacky // Creating the error message. 404198090Srdivacky llvm::SmallString<128> Str; 405198090Srdivacky llvm::raw_svector_ostream OS(Str); 406193323Sed switch(MK) { 407193323Sed case MK_FunCall: 408193323Sed OS << "Method called on moved-from object"; 409193323Sed explainObject(OS, Region, RD, MK); 410193323Sed break; 411193323Sed case MK_Copy: 412193323Sed OS << "Moved-from object"; 413202375Srdivacky explainObject(OS, Region, RD, MK); 414193323Sed OS << " is copied"; 415193323Sed break; 416193323Sed case MK_Move: 417193323Sed OS << "Moved-from object"; 418193323Sed explainObject(OS, Region, RD, MK); 419198090Srdivacky OS << " is moved"; 420198090Srdivacky break; 421193323Sed case MK_Dereference: 422199481Srdivacky OS << "Dereference of null smart pointer"; 423202375Srdivacky explainObject(OS, Region, RD, MK); 424199481Srdivacky break; 425199481Srdivacky } 426199481Srdivacky 427199481Srdivacky auto R = std::make_unique<PathSensitiveBugReport>( 428199481Srdivacky BT, OS.str(), N, LocUsedForUniqueing, 429202375Srdivacky MoveNode->getLocationContext()->getDecl()); 430202375Srdivacky R->addVisitor(std::make_unique<MovedBugVisitor>(*this, Region, RD, MK)); 431199481Srdivacky C.emitReport(std::move(R)); 432199481Srdivacky return N; 433199481Srdivacky } 434202375Srdivacky return nullptr; 435199481Srdivacky} 436199481Srdivacky 437199481Srdivackyvoid MoveChecker::checkPostCall(const CallEvent &Call, 438199481Srdivacky CheckerContext &C) const { 439199481Srdivacky const auto *AFC = dyn_cast<AnyFunctionCall>(&Call); 440199481Srdivacky if (!AFC) 441199481Srdivacky return; 442202375Srdivacky 443202375Srdivacky ProgramStateRef State = C.getState(); 444199481Srdivacky const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(AFC->getDecl()); 445199481Srdivacky if (!MethodDecl) 446199481Srdivacky return; 447199481Srdivacky 448199481Srdivacky // Check if an object became moved-from. 449199481Srdivacky // Object can become moved from after a call to move assignment operator or 450199481Srdivacky // move constructor . 451202375Srdivacky const auto *ConstructorDecl = dyn_cast<CXXConstructorDecl>(MethodDecl); 452202375Srdivacky if (ConstructorDecl && !ConstructorDecl->isMoveConstructor()) 453199481Srdivacky return; 454199481Srdivacky 455199481Srdivacky if (!ConstructorDecl && !MethodDecl->isMoveAssignmentOperator()) 456202375Srdivacky return; 457199481Srdivacky 458199481Srdivacky const auto ArgRegion = AFC->getArgSVal(0).getAsRegion(); 459199481Srdivacky if (!ArgRegion) 460199481Srdivacky return; 461199481Srdivacky 462199481Srdivacky // Skip moving the object to itself. 463199481Srdivacky const auto *CC = dyn_cast_or_null<CXXConstructorCall>(&Call); 464202375Srdivacky if (CC && CC->getCXXThisVal().getAsRegion() == ArgRegion) 465202375Srdivacky return; 466199481Srdivacky 467199481Srdivacky if (const auto *IC = dyn_cast<CXXInstanceCall>(AFC)) 468199481Srdivacky if (IC->getCXXThisVal().getAsRegion() == ArgRegion) 469202375Srdivacky return; 470199481Srdivacky 471199481Srdivacky const MemRegion *BaseRegion = ArgRegion->getBaseRegion(); 472199481Srdivacky // Skip temp objects because of their short lifetime. 473199481Srdivacky if (BaseRegion->getAs<CXXTempObjectRegion>() || 474199481Srdivacky AFC->getArgExpr(0)->isPRValue()) 475199481Srdivacky return; 476199481Srdivacky // If it has already been reported do not need to modify the state. 477202375Srdivacky 478202375Srdivacky if (State->get<TrackedRegionMap>(ArgRegion)) 479199481Srdivacky return; 480199481Srdivacky 481199481Srdivacky const CXXRecordDecl *RD = MethodDecl->getParent(); 482202375Srdivacky ObjectKind OK = classifyObject(ArgRegion, RD); 483199481Srdivacky if (shouldBeTracked(OK)) { 484199481Srdivacky // Mark object as moved-from. 485199481Srdivacky State = State->set<TrackedRegionMap>(ArgRegion, RegionState::getMoved()); 486199481Srdivacky C.addTransition(State); 487199481Srdivacky return; 488193323Sed } 489193323Sed assert(!C.isDifferent() && "Should not have made transitions on this path!"); 490193323Sed} 491202375Srdivacky 492193323Sedbool MoveChecker::isMoveSafeMethod(const CXXMethodDecl *MethodDec) const { 493204642Srdivacky // We abandon the cases where bool/void/void* conversion happens. 494202375Srdivacky if (const auto *ConversionDec = 495202375Srdivacky dyn_cast_or_null<CXXConversionDecl>(MethodDec)) { 496193323Sed const Type *Tp = ConversionDec->getConversionType().getTypePtrOrNull(); 497193323Sed if (!Tp) 498198090Srdivacky return false; 499193323Sed if (Tp->isBooleanType() || Tp->isVoidType() || Tp->isVoidPointerType()) 500193323Sed return true; 501193323Sed } 502 // Function call `empty` can be skipped. 503 return (MethodDec && MethodDec->getDeclName().isIdentifier() && 504 (MethodDec->getName().lower() == "empty" || 505 MethodDec->getName().lower() == "isempty")); 506} 507 508bool MoveChecker::isStateResetMethod(const CXXMethodDecl *MethodDec) const { 509 if (!MethodDec) 510 return false; 511 if (MethodDec->hasAttr<ReinitializesAttr>()) 512 return true; 513 if (MethodDec->getDeclName().isIdentifier()) { 514 std::string MethodName = MethodDec->getName().lower(); 515 // TODO: Some of these methods (eg., resize) are not always resetting 516 // the state, so we should consider looking at the arguments. 517 if (MethodName == "assign" || MethodName == "clear" || 518 MethodName == "destroy" || MethodName == "reset" || 519 MethodName == "resize" || MethodName == "shrink") 520 return true; 521 } 522 return false; 523} 524 525// Don't report an error inside a move related operation. 526// We assume that the programmer knows what she does. 527bool MoveChecker::isInMoveSafeContext(const LocationContext *LC) const { 528 do { 529 const auto *CtxDec = LC->getDecl(); 530 auto *CtorDec = dyn_cast_or_null<CXXConstructorDecl>(CtxDec); 531 auto *DtorDec = dyn_cast_or_null<CXXDestructorDecl>(CtxDec); 532 auto *MethodDec = dyn_cast_or_null<CXXMethodDecl>(CtxDec); 533 if (DtorDec || (CtorDec && CtorDec->isCopyOrMoveConstructor()) || 534 (MethodDec && MethodDec->isOverloadedOperator() && 535 MethodDec->getOverloadedOperator() == OO_Equal) || 536 isStateResetMethod(MethodDec) || isMoveSafeMethod(MethodDec)) 537 return true; 538 } while ((LC = LC->getParent())); 539 return false; 540} 541 542bool MoveChecker::belongsTo(const CXXRecordDecl *RD, 543 const llvm::StringSet<> &Set) const { 544 const IdentifierInfo *II = RD->getIdentifier(); 545 return II && Set.count(II->getName()); 546} 547 548MoveChecker::ObjectKind 549MoveChecker::classifyObject(const MemRegion *MR, 550 const CXXRecordDecl *RD) const { 551 // Local variables and local rvalue references are classified as "Local". 552 // For the purposes of this checker, we classify move-safe STL types 553 // as not-"STL" types, because that's how the checker treats them. 554 MR = unwrapRValueReferenceIndirection(MR); 555 bool IsLocal = isa_and_nonnull<VarRegion>(MR) && 556 isa<StackSpaceRegion>(MR->getMemorySpace()); 557 558 if (!RD || !RD->getDeclContext()->isStdNamespace()) 559 return { IsLocal, SK_NonStd }; 560 561 if (belongsTo(RD, StdSmartPtrClasses)) 562 return { IsLocal, SK_SmartPtr }; 563 564 if (belongsTo(RD, StdSafeClasses)) 565 return { IsLocal, SK_Safe }; 566 567 return { IsLocal, SK_Unsafe }; 568} 569 570void MoveChecker::explainObject(llvm::raw_ostream &OS, const MemRegion *MR, 571 const CXXRecordDecl *RD, MisuseKind MK) const { 572 // We may need a leading space every time we actually explain anything, 573 // and we never know if we are to explain anything until we try. 574 if (const auto DR = 575 dyn_cast_or_null<DeclRegion>(unwrapRValueReferenceIndirection(MR))) { 576 const auto *RegionDecl = cast<NamedDecl>(DR->getDecl()); 577 OS << " '" << RegionDecl->getDeclName() << "'"; 578 } 579 580 ObjectKind OK = classifyObject(MR, RD); 581 switch (OK.StdKind) { 582 case SK_NonStd: 583 case SK_Safe: 584 break; 585 case SK_SmartPtr: 586 if (MK != MK_Dereference) 587 break; 588 589 // We only care about the type if it's a dereference. 590 [[fallthrough]]; 591 case SK_Unsafe: 592 OS << " of type '" << RD->getQualifiedNameAsString() << "'"; 593 break; 594 }; 595} 596 597void MoveChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { 598 ProgramStateRef State = C.getState(); 599 600 // Remove the MemRegions from the map on which a ctor/dtor call or assignment 601 // happened. 602 603 // Checking constructor calls. 604 if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) { 605 State = removeFromState(State, CC->getCXXThisVal().getAsRegion()); 606 auto CtorDec = CC->getDecl(); 607 // Check for copying a moved-from object and report the bug. 608 if (CtorDec && CtorDec->isCopyOrMoveConstructor()) { 609 const MemRegion *ArgRegion = CC->getArgSVal(0).getAsRegion(); 610 const CXXRecordDecl *RD = CtorDec->getParent(); 611 MisuseKind MK = CtorDec->isMoveConstructor() ? MK_Move : MK_Copy; 612 modelUse(State, ArgRegion, RD, MK, C); 613 return; 614 } 615 } 616 617 const auto IC = dyn_cast<CXXInstanceCall>(&Call); 618 if (!IC) 619 return; 620 621 const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion(); 622 if (!ThisRegion) 623 return; 624 625 // The remaining part is check only for method call on a moved-from object. 626 const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(IC->getDecl()); 627 if (!MethodDecl) 628 return; 629 630 // Calling a destructor on a moved object is fine. 631 if (isa<CXXDestructorDecl>(MethodDecl)) 632 return; 633 634 // We want to investigate the whole object, not only sub-object of a parent 635 // class in which the encountered method defined. 636 ThisRegion = ThisRegion->getMostDerivedObjectRegion(); 637 638 if (isStateResetMethod(MethodDecl)) { 639 State = removeFromState(State, ThisRegion); 640 C.addTransition(State); 641 return; 642 } 643 644 if (isMoveSafeMethod(MethodDecl)) 645 return; 646 647 // Store class declaration as well, for bug reporting purposes. 648 const CXXRecordDecl *RD = MethodDecl->getParent(); 649 650 if (MethodDecl->isOverloadedOperator()) { 651 OverloadedOperatorKind OOK = MethodDecl->getOverloadedOperator(); 652 653 if (OOK == OO_Equal) { 654 // Remove the tracked object for every assignment operator, but report bug 655 // only for move or copy assignment's argument. 656 State = removeFromState(State, ThisRegion); 657 658 if (MethodDecl->isCopyAssignmentOperator() || 659 MethodDecl->isMoveAssignmentOperator()) { 660 const MemRegion *ArgRegion = IC->getArgSVal(0).getAsRegion(); 661 MisuseKind MK = 662 MethodDecl->isMoveAssignmentOperator() ? MK_Move : MK_Copy; 663 modelUse(State, ArgRegion, RD, MK, C); 664 return; 665 } 666 C.addTransition(State); 667 return; 668 } 669 670 if (OOK == OO_Star || OOK == OO_Arrow) { 671 modelUse(State, ThisRegion, RD, MK_Dereference, C); 672 return; 673 } 674 } 675 676 modelUse(State, ThisRegion, RD, MK_FunCall, C); 677} 678 679void MoveChecker::checkDeadSymbols(SymbolReaper &SymReaper, 680 CheckerContext &C) const { 681 ProgramStateRef State = C.getState(); 682 TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>(); 683 for (auto E : TrackedRegions) { 684 const MemRegion *Region = E.first; 685 bool IsRegDead = !SymReaper.isLiveRegion(Region); 686 687 // Remove the dead regions from the region map. 688 if (IsRegDead) { 689 State = State->remove<TrackedRegionMap>(Region); 690 } 691 } 692 C.addTransition(State); 693} 694 695ProgramStateRef MoveChecker::checkRegionChanges( 696 ProgramStateRef State, const InvalidatedSymbols *Invalidated, 697 ArrayRef<const MemRegion *> RequestedRegions, 698 ArrayRef<const MemRegion *> InvalidatedRegions, 699 const LocationContext *LCtx, const CallEvent *Call) const { 700 if (Call) { 701 // Relax invalidation upon function calls: only invalidate parameters 702 // that are passed directly via non-const pointers or non-const references 703 // or rvalue references. 704 // In case of an InstanceCall don't invalidate the this-region since 705 // it is fully handled in checkPreCall and checkPostCall. 706 const MemRegion *ThisRegion = nullptr; 707 if (const auto *IC = dyn_cast<CXXInstanceCall>(Call)) 708 ThisRegion = IC->getCXXThisVal().getAsRegion(); 709 710 // Requested ("explicit") regions are the regions passed into the call 711 // directly, but not all of them end up being invalidated. 712 // But when they do, they appear in the InvalidatedRegions array as well. 713 for (const auto *Region : RequestedRegions) { 714 if (ThisRegion != Region && 715 llvm::is_contained(InvalidatedRegions, Region)) 716 State = removeFromState(State, Region); 717 } 718 } else { 719 // For invalidations that aren't caused by calls, assume nothing. In 720 // particular, direct write into an object's field invalidates the status. 721 for (const auto *Region : InvalidatedRegions) 722 State = removeFromState(State, Region->getBaseRegion()); 723 } 724 725 return State; 726} 727 728void MoveChecker::printState(raw_ostream &Out, ProgramStateRef State, 729 const char *NL, const char *Sep) const { 730 731 TrackedRegionMapTy RS = State->get<TrackedRegionMap>(); 732 733 if (!RS.isEmpty()) { 734 Out << Sep << "Moved-from objects :" << NL; 735 for (auto I: RS) { 736 I.first->dumpToStream(Out); 737 if (I.second.isMoved()) 738 Out << ": moved"; 739 else 740 Out << ": moved and reported"; 741 Out << NL; 742 } 743 } 744} 745void ento::registerMoveChecker(CheckerManager &mgr) { 746 MoveChecker *chk = mgr.registerChecker<MoveChecker>(); 747 chk->setAggressiveness( 748 mgr.getAnalyzerOptions().getCheckerStringOption(chk, "WarnOn"), mgr); 749} 750 751bool ento::shouldRegisterMoveChecker(const CheckerManager &mgr) { 752 return true; 753} 754