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