]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MisusedMovedObjectChecker.cpp
Merge clang trunk r300422 and resolve conflicts.
[FreeBSD/FreeBSD.git] / contrib / llvm / tools / clang / lib / StaticAnalyzer / Checkers / MisusedMovedObjectChecker.cpp
1 // MisusedMovedObjectChecker.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 "ClangSACheckers.h"
17 #include "clang/AST/ExprCXX.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
24 using namespace clang;
25 using namespace ento;
26
27 namespace {
28
29 struct RegionState {
30 private:
31   enum Kind { Moved, Reported } K;
32   RegionState(Kind InK) : K(InK) {}
33
34 public:
35   bool isReported() const { return K == Reported; }
36   bool isMoved() const { return K == Moved; }
37
38   static RegionState getReported() { return RegionState(Reported); }
39   static RegionState getMoved() { return RegionState(Moved); }
40
41   bool operator==(const RegionState &X) const { return K == X.K; }
42   void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); }
43 };
44
45 class MisusedMovedObjectChecker
46     : public Checker<check::PreCall, check::PostCall, check::EndFunction,
47                      check::DeadSymbols, check::RegionChanges> {
48 public:
49   void checkEndFunction(CheckerContext &C) const;
50   void checkPreCall(const CallEvent &MC, CheckerContext &C) const;
51   void checkPostCall(const CallEvent &MC, CheckerContext &C) const;
52   void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
53   ProgramStateRef
54   checkRegionChanges(ProgramStateRef State,
55                      const InvalidatedSymbols *Invalidated,
56                      ArrayRef<const MemRegion *> ExplicitRegions,
57                      ArrayRef<const MemRegion *> Regions,
58                      const LocationContext *LCtx, const CallEvent *Call) const;
59
60 private:
61   class MovedBugVisitor : public BugReporterVisitorImpl<MovedBugVisitor> {
62   public:
63     MovedBugVisitor(const MemRegion *R) : Region(R), Found(false) {}
64
65     void Profile(llvm::FoldingSetNodeID &ID) const override {
66       static int X = 0;
67       ID.AddPointer(&X);
68       ID.AddPointer(Region);
69     }
70
71     std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
72                                                    const ExplodedNode *PrevN,
73                                                    BugReporterContext &BRC,
74                                                    BugReport &BR) override;
75
76   private:
77     // The tracked region.
78     const MemRegion *Region;
79     bool Found;
80   };
81
82   mutable std::unique_ptr<BugType> BT;
83   ExplodedNode *reportBug(const MemRegion *Region, const CallEvent &Call,
84                           CheckerContext &C, bool isCopy) const;
85   bool isInMoveSafeContext(const LocationContext *LC) const;
86   bool isStateResetMethod(const CXXMethodDecl *MethodDec) const;
87   bool isMoveSafeMethod(const CXXMethodDecl *MethodDec) const;
88   const ExplodedNode *getMoveLocation(const ExplodedNode *N,
89                                       const MemRegion *Region,
90                                       CheckerContext &C) const;
91 };
92 } // end anonymous namespace
93
94 REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, RegionState)
95
96 // If a region is removed all of the subregions needs to be removed too.
97 static ProgramStateRef removeFromState(ProgramStateRef State,
98                                        const MemRegion *Region) {
99   if (!Region)
100     return State;
101   // Note: The isSubRegionOf function is not reflexive.
102   State = State->remove<TrackedRegionMap>(Region);
103   for (auto &E : State->get<TrackedRegionMap>()) {
104     if (E.first->isSubRegionOf(Region))
105       State = State->remove<TrackedRegionMap>(E.first);
106   }
107   return State;
108 }
109
110 static bool isAnyBaseRegionReported(ProgramStateRef State,
111                                     const MemRegion *Region) {
112   for (auto &E : State->get<TrackedRegionMap>()) {
113     if (Region->isSubRegionOf(E.first) && E.second.isReported())
114       return true;
115   }
116   return false;
117 }
118
119 std::shared_ptr<PathDiagnosticPiece>
120 MisusedMovedObjectChecker::MovedBugVisitor::VisitNode(const ExplodedNode *N,
121                                                       const ExplodedNode *PrevN,
122                                                       BugReporterContext &BRC,
123                                                       BugReport &BR) {
124   // We need only the last move of the reported object's region.
125   // The visitor walks the ExplodedGraph backwards.
126   if (Found)
127     return nullptr;
128   ProgramStateRef State = N->getState();
129   ProgramStateRef StatePrev = PrevN->getState();
130   const RegionState *TrackedObject = State->get<TrackedRegionMap>(Region);
131   const RegionState *TrackedObjectPrev =
132       StatePrev->get<TrackedRegionMap>(Region);
133   if (!TrackedObject)
134     return nullptr;
135   if (TrackedObjectPrev && TrackedObject)
136     return nullptr;
137
138   // Retrieve the associated statement.
139   const Stmt *S = PathDiagnosticLocation::getStmt(N);
140   if (!S)
141     return nullptr;
142   Found = true;
143
144   std::string ObjectName;
145   if (const auto DecReg = Region->getAs<DeclRegion>()) {
146     const auto *RegionDecl = dyn_cast<NamedDecl>(DecReg->getDecl());
147     ObjectName = RegionDecl->getNameAsString();
148   }
149   std::string InfoText;
150   if (ObjectName != "")
151     InfoText = "'" + ObjectName + "' became 'moved-from' here";
152   else
153     InfoText = "Became 'moved-from' here";
154
155   // Generate the extra diagnostic.
156   PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
157                              N->getLocationContext());
158   return std::make_shared<PathDiagnosticEventPiece>(Pos, InfoText, true);
159 }
160
161 const ExplodedNode *MisusedMovedObjectChecker::getMoveLocation(
162     const ExplodedNode *N, const MemRegion *Region, CheckerContext &C) const {
163   // Walk the ExplodedGraph backwards and find the first node that referred to
164   // the tracked region.
165   const ExplodedNode *MoveNode = N;
166
167   while (N) {
168     ProgramStateRef State = N->getState();
169     if (!State->get<TrackedRegionMap>(Region))
170       break;
171     MoveNode = N;
172     N = N->pred_empty() ? nullptr : *(N->pred_begin());
173   }
174   return MoveNode;
175 }
176
177 ExplodedNode *MisusedMovedObjectChecker::reportBug(const MemRegion *Region,
178                                                    const CallEvent &Call,
179                                                    CheckerContext &C,
180                                                    bool isCopy = false) const {
181   if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
182     if (!BT)
183       BT.reset(new BugType(this, "Usage of a 'moved-from' object",
184                            "C++ move semantics"));
185
186     // Uniqueing report to the same object.
187     PathDiagnosticLocation LocUsedForUniqueing;
188     const ExplodedNode *MoveNode = getMoveLocation(N, Region, C);
189
190     if (const Stmt *MoveStmt = PathDiagnosticLocation::getStmt(MoveNode))
191       LocUsedForUniqueing = PathDiagnosticLocation::createBegin(
192           MoveStmt, C.getSourceManager(), MoveNode->getLocationContext());
193
194     // Creating the error message.
195     std::string ErrorMessage;
196     if (isCopy)
197       ErrorMessage = "Copying a 'moved-from' object";
198     else
199       ErrorMessage = "Method call on a 'moved-from' object";
200     if (const auto DecReg = Region->getAs<DeclRegion>()) {
201       const auto *RegionDecl = dyn_cast<NamedDecl>(DecReg->getDecl());
202       ErrorMessage += " '" + RegionDecl->getNameAsString() + "'";
203     }
204
205     auto R =
206         llvm::make_unique<BugReport>(*BT, ErrorMessage, N, LocUsedForUniqueing,
207                                      MoveNode->getLocationContext()->getDecl());
208     R->addVisitor(llvm::make_unique<MovedBugVisitor>(Region));
209     C.emitReport(std::move(R));
210     return N;
211   }
212   return nullptr;
213 }
214
215 // Removing the function parameters' MemRegion from the state. This is needed
216 // for PODs where the trivial destructor does not even created nor executed.
217 void MisusedMovedObjectChecker::checkEndFunction(CheckerContext &C) const {
218   auto State = C.getState();
219   TrackedRegionMapTy Objects = State->get<TrackedRegionMap>();
220   if (Objects.isEmpty())
221     return;
222
223   auto LC = C.getLocationContext();
224
225   const auto LD = dyn_cast_or_null<FunctionDecl>(LC->getDecl());
226   if (!LD)
227     return;
228   llvm::SmallSet<const MemRegion *, 8> InvalidRegions;
229
230   for (auto Param : LD->parameters()) {
231     auto Type = Param->getType().getTypePtrOrNull();
232     if (!Type)
233       continue;
234     if (!Type->isPointerType() && !Type->isReferenceType()) {
235       InvalidRegions.insert(State->getLValue(Param, LC).getAsRegion());
236     }
237   }
238
239   if (InvalidRegions.empty())
240     return;
241
242   for (const auto &E : State->get<TrackedRegionMap>()) {
243     if (InvalidRegions.count(E.first->getBaseRegion()))
244       State = State->remove<TrackedRegionMap>(E.first);
245   }
246
247   C.addTransition(State);
248 }
249
250 void MisusedMovedObjectChecker::checkPostCall(const CallEvent &Call,
251                                               CheckerContext &C) const {
252   const auto *AFC = dyn_cast<AnyFunctionCall>(&Call);
253   if (!AFC)
254     return;
255
256   ProgramStateRef State = C.getState();
257   const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(AFC->getDecl());
258   if (!MethodDecl)
259     return;
260
261   const auto *ConstructorDecl = dyn_cast<CXXConstructorDecl>(MethodDecl);
262
263   const auto *CC = dyn_cast_or_null<CXXConstructorCall>(&Call);
264   // Check if an object became moved-from.
265   // Object can become moved from after a call to move assignment operator or
266   // move constructor .
267   if (ConstructorDecl && !ConstructorDecl->isMoveConstructor())
268     return;
269
270   if (!ConstructorDecl && !MethodDecl->isMoveAssignmentOperator())
271     return;
272
273   const auto ArgRegion = AFC->getArgSVal(0).getAsRegion();
274   if (!ArgRegion)
275     return;
276
277   // Skip moving the object to itself.
278   if (CC && CC->getCXXThisVal().getAsRegion() == ArgRegion)
279     return;
280   if (const auto *IC = dyn_cast<CXXInstanceCall>(AFC))
281     if (IC->getCXXThisVal().getAsRegion() == ArgRegion)
282       return;
283
284   const MemRegion *BaseRegion = ArgRegion->getBaseRegion();
285   // Skip temp objects because of their short lifetime.
286   if (BaseRegion->getAs<CXXTempObjectRegion>() ||
287       AFC->getArgExpr(0)->isRValue())
288     return;
289   // If it has already been reported do not need to modify the state.
290
291   if (State->get<TrackedRegionMap>(ArgRegion))
292     return;
293   // Mark object as moved-from.
294   State = State->set<TrackedRegionMap>(ArgRegion, RegionState::getMoved());
295   C.addTransition(State);
296 }
297
298 bool MisusedMovedObjectChecker::isMoveSafeMethod(
299     const CXXMethodDecl *MethodDec) const {
300   // We abandon the cases where bool/void/void* conversion happens.
301   if (const auto *ConversionDec =
302           dyn_cast_or_null<CXXConversionDecl>(MethodDec)) {
303     const Type *Tp = ConversionDec->getConversionType().getTypePtrOrNull();
304     if (!Tp)
305       return false;
306     if (Tp->isBooleanType() || Tp->isVoidType() || Tp->isVoidPointerType())
307       return true;
308   }
309   // Function call `empty` can be skipped.
310   if (MethodDec && MethodDec->getDeclName().isIdentifier() &&
311       (MethodDec->getName().lower() == "empty" ||
312        MethodDec->getName().lower() == "isempty"))
313     return true;
314
315   return false;
316 }
317
318 bool MisusedMovedObjectChecker::isStateResetMethod(
319     const CXXMethodDecl *MethodDec) const {
320   if (MethodDec && MethodDec->getDeclName().isIdentifier()) {
321     std::string MethodName = MethodDec->getName().lower();
322     if (MethodName == "reset" || MethodName == "clear" ||
323         MethodName == "destroy")
324       return true;
325   }
326   return false;
327 }
328
329 // Don't report an error inside a move related operation.
330 // We assume that the programmer knows what she does.
331 bool MisusedMovedObjectChecker::isInMoveSafeContext(
332     const LocationContext *LC) const {
333   do {
334     const auto *CtxDec = LC->getDecl();
335     auto *CtorDec = dyn_cast_or_null<CXXConstructorDecl>(CtxDec);
336     auto *DtorDec = dyn_cast_or_null<CXXDestructorDecl>(CtxDec);
337     auto *MethodDec = dyn_cast_or_null<CXXMethodDecl>(CtxDec);
338     if (DtorDec || (CtorDec && CtorDec->isCopyOrMoveConstructor()) ||
339         (MethodDec && MethodDec->isOverloadedOperator() &&
340          MethodDec->getOverloadedOperator() == OO_Equal) ||
341         isStateResetMethod(MethodDec) || isMoveSafeMethod(MethodDec))
342       return true;
343   } while ((LC = LC->getParent()));
344   return false;
345 }
346
347 void MisusedMovedObjectChecker::checkPreCall(const CallEvent &Call,
348                                              CheckerContext &C) const {
349   ProgramStateRef State = C.getState();
350   const LocationContext *LC = C.getLocationContext();
351   ExplodedNode *N = nullptr;
352
353   // Remove the MemRegions from the map on which a ctor/dtor call or assignement
354   // happened.
355
356   // Checking constructor calls.
357   if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) {
358     State = removeFromState(State, CC->getCXXThisVal().getAsRegion());
359     auto CtorDec = CC->getDecl();
360     // Check for copying a moved-from object and report the bug.
361     if (CtorDec && CtorDec->isCopyOrMoveConstructor()) {
362       const MemRegion *ArgRegion = CC->getArgSVal(0).getAsRegion();
363       const RegionState *ArgState = State->get<TrackedRegionMap>(ArgRegion);
364       if (ArgState && ArgState->isMoved()) {
365         if (!isInMoveSafeContext(LC)) {
366           N = reportBug(ArgRegion, Call, C, /*isCopy=*/true);
367           State = State->set<TrackedRegionMap>(ArgRegion,
368                                                RegionState::getReported());
369         }
370       }
371     }
372     C.addTransition(State, N);
373     return;
374   }
375
376   const auto IC = dyn_cast<CXXInstanceCall>(&Call);
377   if (!IC)
378     return;
379   // In case of destructor call we do not track the object anymore.
380   const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
381   if (dyn_cast_or_null<CXXDestructorDecl>(Call.getDecl())) {
382     State = removeFromState(State, IC->getCXXThisVal().getAsRegion());
383     C.addTransition(State);
384     return;
385   }
386
387   const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(IC->getDecl());
388   if (!MethodDecl)
389     return;
390   // Checking assignment operators.
391   bool OperatorEq = MethodDecl->isOverloadedOperator() &&
392                     MethodDecl->getOverloadedOperator() == OO_Equal;
393   // Remove the tracked object for every assignment operator, but report bug
394   // only for move or copy assignment's argument.
395   if (OperatorEq) {
396     State = removeFromState(State, ThisRegion);
397     if (MethodDecl->isCopyAssignmentOperator() ||
398         MethodDecl->isMoveAssignmentOperator()) {
399       const RegionState *ArgState =
400           State->get<TrackedRegionMap>(IC->getArgSVal(0).getAsRegion());
401       if (ArgState && ArgState->isMoved() && !isInMoveSafeContext(LC)) {
402         const MemRegion *ArgRegion = IC->getArgSVal(0).getAsRegion();
403         N = reportBug(ArgRegion, Call, C, /*isCopy=*/true);
404         State =
405             State->set<TrackedRegionMap>(ArgRegion, RegionState::getReported());
406       }
407     }
408     C.addTransition(State, N);
409     return;
410   }
411
412   // The remaining part is check only for method call on a moved-from object.
413   if (isMoveSafeMethod(MethodDecl))
414     return;
415
416   if (isStateResetMethod(MethodDecl)) {
417     State = State->remove<TrackedRegionMap>(ThisRegion);
418     C.addTransition(State);
419     return;
420   }
421
422   // If it is already reported then we dont report the bug again.
423   const RegionState *ThisState = State->get<TrackedRegionMap>(ThisRegion);
424   if (!(ThisState && ThisState->isMoved()))
425     return;
426
427   // Dont report it in case if any base region is already reported
428   if (isAnyBaseRegionReported(State, ThisRegion))
429     return;
430
431   if (isInMoveSafeContext(LC))
432     return;
433
434   N = reportBug(ThisRegion, Call, C);
435   State = State->set<TrackedRegionMap>(ThisRegion, RegionState::getReported());
436   C.addTransition(State, N);
437 }
438
439 void MisusedMovedObjectChecker::checkDeadSymbols(SymbolReaper &SymReaper,
440                                                  CheckerContext &C) const {
441   ProgramStateRef State = C.getState();
442   TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
443   for (TrackedRegionMapTy::value_type E : TrackedRegions) {
444     const MemRegion *Region = E.first;
445     bool IsRegDead = !SymReaper.isLiveRegion(Region);
446
447     // Remove the dead regions from the region map.
448     if (IsRegDead) {
449       State = State->remove<TrackedRegionMap>(Region);
450     }
451   }
452   C.addTransition(State);
453 }
454
455 ProgramStateRef MisusedMovedObjectChecker::checkRegionChanges(
456     ProgramStateRef State, const InvalidatedSymbols *Invalidated,
457     ArrayRef<const MemRegion *> ExplicitRegions,
458     ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx,
459     const CallEvent *Call) const {
460   // In case of an InstanceCall don't remove the ThisRegion from the GDM since
461   // it is handled in checkPreCall and checkPostCall.
462   const MemRegion *ThisRegion = nullptr;
463   if (const auto *IC = dyn_cast_or_null<CXXInstanceCall>(Call)) {
464     ThisRegion = IC->getCXXThisVal().getAsRegion();
465   }
466
467   for (ArrayRef<const MemRegion *>::iterator I = ExplicitRegions.begin(),
468                                              E = ExplicitRegions.end();
469        I != E; ++I) {
470     const auto *Region = *I;
471     if (ThisRegion != Region) {
472       State = removeFromState(State, Region);
473     }
474   }
475
476   return State;
477 }
478
479 void ento::registerMisusedMovedObjectChecker(CheckerManager &mgr) {
480   mgr.registerChecker<MisusedMovedObjectChecker>();
481 }