clang  6.0.0svn
MisusedMovedObjectChecker.cpp
Go to the documentation of this file.
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"
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;
54  checkRegionChanges(ProgramStateRef State,
55  const InvalidatedSymbols *Invalidated,
56  ArrayRef<const MemRegion *> ExplicitRegions,
58  const LocationContext *LCtx, const CallEvent *Call) const;
59  void printState(raw_ostream &Out, ProgramStateRef State,
60  const char *NL, const char *Sep) const override;
61 
62 private:
63  enum MisuseKind {MK_FunCall, MK_Copy, MK_Move};
64  class MovedBugVisitor : public BugReporterVisitorImpl<MovedBugVisitor> {
65  public:
66  MovedBugVisitor(const MemRegion *R) : Region(R), Found(false) {}
67 
68  void Profile(llvm::FoldingSetNodeID &ID) const override {
69  static int X = 0;
70  ID.AddPointer(&X);
71  ID.AddPointer(Region);
72  }
73 
74  std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
75  const ExplodedNode *PrevN,
76  BugReporterContext &BRC,
77  BugReport &BR) override;
78 
79  private:
80  // The tracked region.
81  const MemRegion *Region;
82  bool Found;
83  };
84 
85  mutable std::unique_ptr<BugType> BT;
86  ExplodedNode *reportBug(const MemRegion *Region, const CallEvent &Call,
87  CheckerContext &C, MisuseKind MK) const;
88  bool isInMoveSafeContext(const LocationContext *LC) const;
89  bool isStateResetMethod(const CXXMethodDecl *MethodDec) const;
90  bool isMoveSafeMethod(const CXXMethodDecl *MethodDec) const;
91  const ExplodedNode *getMoveLocation(const ExplodedNode *N,
92  const MemRegion *Region,
93  CheckerContext &C) const;
94 };
95 } // end anonymous namespace
96 
97 REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, RegionState)
98 
99 // If a region is removed all of the subregions needs to be removed too.
101  const MemRegion *Region) {
102  if (!Region)
103  return State;
104  // Note: The isSubRegionOf function is not reflexive.
105  State = State->remove<TrackedRegionMap>(Region);
106  for (auto &E : State->get<TrackedRegionMap>()) {
107  if (E.first->isSubRegionOf(Region))
108  State = State->remove<TrackedRegionMap>(E.first);
109  }
110  return State;
111 }
112 
114  const MemRegion *Region) {
115  for (auto &E : State->get<TrackedRegionMap>()) {
116  if (Region->isSubRegionOf(E.first) && E.second.isReported())
117  return true;
118  }
119  return false;
120 }
121 
122 std::shared_ptr<PathDiagnosticPiece>
123 MisusedMovedObjectChecker::MovedBugVisitor::VisitNode(const ExplodedNode *N,
124  const ExplodedNode *PrevN,
125  BugReporterContext &BRC,
126  BugReport &BR) {
127  // We need only the last move of the reported object's region.
128  // The visitor walks the ExplodedGraph backwards.
129  if (Found)
130  return nullptr;
132  ProgramStateRef StatePrev = PrevN->getState();
133  const RegionState *TrackedObject = State->get<TrackedRegionMap>(Region);
134  const RegionState *TrackedObjectPrev =
135  StatePrev->get<TrackedRegionMap>(Region);
136  if (!TrackedObject)
137  return nullptr;
138  if (TrackedObjectPrev && TrackedObject)
139  return nullptr;
140 
141  // Retrieve the associated statement.
143  if (!S)
144  return nullptr;
145  Found = true;
146 
147  std::string ObjectName;
148  if (const auto DecReg = Region->getAs<DeclRegion>()) {
149  const auto *RegionDecl = dyn_cast<NamedDecl>(DecReg->getDecl());
150  ObjectName = RegionDecl->getNameAsString();
151  }
152  std::string InfoText;
153  if (ObjectName != "")
154  InfoText = "'" + ObjectName + "' became 'moved-from' here";
155  else
156  InfoText = "Became 'moved-from' here";
157 
158  // Generate the extra diagnostic.
160  N->getLocationContext());
161  return std::make_shared<PathDiagnosticEventPiece>(Pos, InfoText, true);
162 }
163 
164 const ExplodedNode *MisusedMovedObjectChecker::getMoveLocation(
165  const ExplodedNode *N, const MemRegion *Region, CheckerContext &C) const {
166  // Walk the ExplodedGraph backwards and find the first node that referred to
167  // the tracked region.
168  const ExplodedNode *MoveNode = N;
169 
170  while (N) {
172  if (!State->get<TrackedRegionMap>(Region))
173  break;
174  MoveNode = N;
175  N = N->pred_empty() ? nullptr : *(N->pred_begin());
176  }
177  return MoveNode;
178 }
179 
180 ExplodedNode *MisusedMovedObjectChecker::reportBug(const MemRegion *Region,
181  const CallEvent &Call,
182  CheckerContext &C,
183  MisuseKind MK) const {
185  if (!BT)
186  BT.reset(new BugType(this, "Usage of a 'moved-from' object",
187  "C++ move semantics"));
188 
189  // Uniqueing report to the same object.
190  PathDiagnosticLocation LocUsedForUniqueing;
191  const ExplodedNode *MoveNode = getMoveLocation(N, Region, C);
192 
193  if (const Stmt *MoveStmt = PathDiagnosticLocation::getStmt(MoveNode))
194  LocUsedForUniqueing = PathDiagnosticLocation::createBegin(
195  MoveStmt, C.getSourceManager(), MoveNode->getLocationContext());
196 
197  // Creating the error message.
198  std::string ErrorMessage;
199  switch(MK) {
200  case MK_FunCall:
201  ErrorMessage = "Method call on a 'moved-from' object";
202  break;
203  case MK_Copy:
204  ErrorMessage = "Copying a 'moved-from' object";
205  break;
206  case MK_Move:
207  ErrorMessage = "Moving a 'moved-from' object";
208  break;
209  }
210  if (const auto DecReg = Region->getAs<DeclRegion>()) {
211  const auto *RegionDecl = dyn_cast<NamedDecl>(DecReg->getDecl());
212  ErrorMessage += " '" + RegionDecl->getNameAsString() + "'";
213  }
214 
215  auto R =
216  llvm::make_unique<BugReport>(*BT, ErrorMessage, N, LocUsedForUniqueing,
217  MoveNode->getLocationContext()->getDecl());
218  R->addVisitor(llvm::make_unique<MovedBugVisitor>(Region));
219  C.emitReport(std::move(R));
220  return N;
221  }
222  return nullptr;
223 }
224 
225 // Removing the function parameters' MemRegion from the state. This is needed
226 // for PODs where the trivial destructor does not even created nor executed.
227 void MisusedMovedObjectChecker::checkEndFunction(CheckerContext &C) const {
228  auto State = C.getState();
229  TrackedRegionMapTy Objects = State->get<TrackedRegionMap>();
230  if (Objects.isEmpty())
231  return;
232 
233  auto LC = C.getLocationContext();
234 
235  const auto LD = dyn_cast_or_null<FunctionDecl>(LC->getDecl());
236  if (!LD)
237  return;
238  llvm::SmallSet<const MemRegion *, 8> InvalidRegions;
239 
240  for (auto Param : LD->parameters()) {
241  auto Type = Param->getType().getTypePtrOrNull();
242  if (!Type)
243  continue;
244  if (!Type->isPointerType() && !Type->isReferenceType()) {
245  InvalidRegions.insert(State->getLValue(Param, LC).getAsRegion());
246  }
247  }
248 
249  if (InvalidRegions.empty())
250  return;
251 
252  for (const auto &E : State->get<TrackedRegionMap>()) {
253  if (InvalidRegions.count(E.first->getBaseRegion()))
254  State = State->remove<TrackedRegionMap>(E.first);
255  }
256 
257  C.addTransition(State);
258 }
259 
260 void MisusedMovedObjectChecker::checkPostCall(const CallEvent &Call,
261  CheckerContext &C) const {
262  const auto *AFC = dyn_cast<AnyFunctionCall>(&Call);
263  if (!AFC)
264  return;
265 
267  const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(AFC->getDecl());
268  if (!MethodDecl)
269  return;
270 
271  const auto *ConstructorDecl = dyn_cast<CXXConstructorDecl>(MethodDecl);
272 
273  const auto *CC = dyn_cast_or_null<CXXConstructorCall>(&Call);
274  // Check if an object became moved-from.
275  // Object can become moved from after a call to move assignment operator or
276  // move constructor .
277  if (ConstructorDecl && !ConstructorDecl->isMoveConstructor())
278  return;
279 
280  if (!ConstructorDecl && !MethodDecl->isMoveAssignmentOperator())
281  return;
282 
283  const auto ArgRegion = AFC->getArgSVal(0).getAsRegion();
284  if (!ArgRegion)
285  return;
286 
287  // Skip moving the object to itself.
288  if (CC && CC->getCXXThisVal().getAsRegion() == ArgRegion)
289  return;
290  if (const auto *IC = dyn_cast<CXXInstanceCall>(AFC))
291  if (IC->getCXXThisVal().getAsRegion() == ArgRegion)
292  return;
293 
294  const MemRegion *BaseRegion = ArgRegion->getBaseRegion();
295  // Skip temp objects because of their short lifetime.
296  if (BaseRegion->getAs<CXXTempObjectRegion>() ||
297  AFC->getArgExpr(0)->isRValue())
298  return;
299  // If it has already been reported do not need to modify the state.
300 
301  if (State->get<TrackedRegionMap>(ArgRegion))
302  return;
303  // Mark object as moved-from.
304  State = State->set<TrackedRegionMap>(ArgRegion, RegionState::getMoved());
305  C.addTransition(State);
306 }
307 
308 bool MisusedMovedObjectChecker::isMoveSafeMethod(
309  const CXXMethodDecl *MethodDec) const {
310  // We abandon the cases where bool/void/void* conversion happens.
311  if (const auto *ConversionDec =
312  dyn_cast_or_null<CXXConversionDecl>(MethodDec)) {
313  const Type *Tp = ConversionDec->getConversionType().getTypePtrOrNull();
314  if (!Tp)
315  return false;
316  if (Tp->isBooleanType() || Tp->isVoidType() || Tp->isVoidPointerType())
317  return true;
318  }
319  // Function call `empty` can be skipped.
320  if (MethodDec && MethodDec->getDeclName().isIdentifier() &&
321  (MethodDec->getName().lower() == "empty" ||
322  MethodDec->getName().lower() == "isempty"))
323  return true;
324 
325  return false;
326 }
327 
328 bool MisusedMovedObjectChecker::isStateResetMethod(
329  const CXXMethodDecl *MethodDec) const {
330  if (MethodDec && MethodDec->getDeclName().isIdentifier()) {
331  std::string MethodName = MethodDec->getName().lower();
332  if (MethodName == "reset" || MethodName == "clear" ||
333  MethodName == "destroy")
334  return true;
335  }
336  return false;
337 }
338 
339 // Don't report an error inside a move related operation.
340 // We assume that the programmer knows what she does.
341 bool MisusedMovedObjectChecker::isInMoveSafeContext(
342  const LocationContext *LC) const {
343  do {
344  const auto *CtxDec = LC->getDecl();
345  auto *CtorDec = dyn_cast_or_null<CXXConstructorDecl>(CtxDec);
346  auto *DtorDec = dyn_cast_or_null<CXXDestructorDecl>(CtxDec);
347  auto *MethodDec = dyn_cast_or_null<CXXMethodDecl>(CtxDec);
348  if (DtorDec || (CtorDec && CtorDec->isCopyOrMoveConstructor()) ||
349  (MethodDec && MethodDec->isOverloadedOperator() &&
350  MethodDec->getOverloadedOperator() == OO_Equal) ||
351  isStateResetMethod(MethodDec) || isMoveSafeMethod(MethodDec))
352  return true;
353  } while ((LC = LC->getParent()));
354  return false;
355 }
356 
357 void MisusedMovedObjectChecker::checkPreCall(const CallEvent &Call,
358  CheckerContext &C) const {
360  const LocationContext *LC = C.getLocationContext();
361  ExplodedNode *N = nullptr;
362 
363  // Remove the MemRegions from the map on which a ctor/dtor call or assignment
364  // happened.
365 
366  // Checking constructor calls.
367  if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) {
368  State = removeFromState(State, CC->getCXXThisVal().getAsRegion());
369  auto CtorDec = CC->getDecl();
370  // Check for copying a moved-from object and report the bug.
371  if (CtorDec && CtorDec->isCopyOrMoveConstructor()) {
372  const MemRegion *ArgRegion = CC->getArgSVal(0).getAsRegion();
373  const RegionState *ArgState = State->get<TrackedRegionMap>(ArgRegion);
374  if (ArgState && ArgState->isMoved()) {
375  if (!isInMoveSafeContext(LC)) {
376  if(CtorDec->isMoveConstructor())
377  N = reportBug(ArgRegion, Call, C, MK_Move);
378  else
379  N = reportBug(ArgRegion, Call, C, MK_Copy);
380  State = State->set<TrackedRegionMap>(ArgRegion,
381  RegionState::getReported());
382  }
383  }
384  }
385  C.addTransition(State, N);
386  return;
387  }
388 
389  const auto IC = dyn_cast<CXXInstanceCall>(&Call);
390  if (!IC)
391  return;
392  // In case of destructor call we do not track the object anymore.
393  const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
394  if (!ThisRegion)
395  return;
396 
397  if (dyn_cast_or_null<CXXDestructorDecl>(Call.getDecl())) {
398  State = removeFromState(State, ThisRegion);
399  C.addTransition(State);
400  return;
401  }
402 
403  const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(IC->getDecl());
404  if (!MethodDecl)
405  return;
406  // Checking assignment operators.
407  bool OperatorEq = MethodDecl->isOverloadedOperator() &&
408  MethodDecl->getOverloadedOperator() == OO_Equal;
409  // Remove the tracked object for every assignment operator, but report bug
410  // only for move or copy assignment's argument.
411  if (OperatorEq) {
412  State = removeFromState(State, ThisRegion);
413  if (MethodDecl->isCopyAssignmentOperator() ||
414  MethodDecl->isMoveAssignmentOperator()) {
415  const RegionState *ArgState =
416  State->get<TrackedRegionMap>(IC->getArgSVal(0).getAsRegion());
417  if (ArgState && ArgState->isMoved() && !isInMoveSafeContext(LC)) {
418  const MemRegion *ArgRegion = IC->getArgSVal(0).getAsRegion();
419  if(MethodDecl->isMoveAssignmentOperator())
420  N = reportBug(ArgRegion, Call, C, MK_Move);
421  else
422  N = reportBug(ArgRegion, Call, C, MK_Copy);
423  State =
424  State->set<TrackedRegionMap>(ArgRegion, RegionState::getReported());
425  }
426  }
427  C.addTransition(State, N);
428  return;
429  }
430 
431  // The remaining part is check only for method call on a moved-from object.
432 
433  // We want to investigate the whole object, not only sub-object of a parent
434  // class in which the encountered method defined.
435  while (const CXXBaseObjectRegion *BR =
436  dyn_cast<CXXBaseObjectRegion>(ThisRegion))
437  ThisRegion = BR->getSuperRegion();
438 
439  if (isMoveSafeMethod(MethodDecl))
440  return;
441 
442  if (isStateResetMethod(MethodDecl)) {
443  State = removeFromState(State, ThisRegion);
444  C.addTransition(State);
445  return;
446  }
447 
448  // If it is already reported then we don't report the bug again.
449  const RegionState *ThisState = State->get<TrackedRegionMap>(ThisRegion);
450  if (!(ThisState && ThisState->isMoved()))
451  return;
452 
453  // Don't report it in case if any base region is already reported
454  if (isAnyBaseRegionReported(State, ThisRegion))
455  return;
456 
457  if (isInMoveSafeContext(LC))
458  return;
459 
460  N = reportBug(ThisRegion, Call, C, MK_FunCall);
461  State = State->set<TrackedRegionMap>(ThisRegion, RegionState::getReported());
462  C.addTransition(State, N);
463 }
464 
465 void MisusedMovedObjectChecker::checkDeadSymbols(SymbolReaper &SymReaper,
466  CheckerContext &C) const {
468  TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
469  for (TrackedRegionMapTy::value_type E : TrackedRegions) {
470  const MemRegion *Region = E.first;
471  bool IsRegDead = !SymReaper.isLiveRegion(Region);
472 
473  // Remove the dead regions from the region map.
474  if (IsRegDead) {
475  State = State->remove<TrackedRegionMap>(Region);
476  }
477  }
478  C.addTransition(State);
479 }
480 
481 ProgramStateRef MisusedMovedObjectChecker::checkRegionChanges(
482  ProgramStateRef State, const InvalidatedSymbols *Invalidated,
483  ArrayRef<const MemRegion *> ExplicitRegions,
484  ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx,
485  const CallEvent *Call) const {
486  // In case of an InstanceCall don't remove the ThisRegion from the GDM since
487  // it is handled in checkPreCall and checkPostCall.
488  const MemRegion *ThisRegion = nullptr;
489  if (const auto *IC = dyn_cast_or_null<CXXInstanceCall>(Call)) {
490  ThisRegion = IC->getCXXThisVal().getAsRegion();
491  }
492 
493  for (ArrayRef<const MemRegion *>::iterator I = ExplicitRegions.begin(),
494  E = ExplicitRegions.end();
495  I != E; ++I) {
496  const auto *Region = *I;
497  if (ThisRegion != Region) {
498  State = removeFromState(State, Region);
499  }
500  }
501 
502  return State;
503 }
504 
505 void MisusedMovedObjectChecker::printState(raw_ostream &Out,
506  ProgramStateRef State,
507  const char *NL,
508  const char *Sep) const {
509 
510  TrackedRegionMapTy RS = State->get<TrackedRegionMap>();
511 
512  if (!RS.isEmpty()) {
513  Out << Sep << "Moved-from objects :" << NL;
514  for (auto I: RS) {
515  I.first->dumpToStream(Out);
516  if (I.second.isMoved())
517  Out << ": moved";
518  else
519  Out << ": moved and reported";
520  Out << NL;
521  }
522  }
523 }
524 void ento::registerMisusedMovedObjectChecker(CheckerManager &mgr) {
525  mgr.registerChecker<MisusedMovedObjectChecker>();
526 }
MemRegion - The root abstract class for all memory regions.
Definition: MemRegion.h:79
bool operator==(CanQual< T > x, CanQual< U > y)
Stmt - This represents one statement.
Definition: Stmt.h:66
ExplodedNode * addTransition(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generates a new transition in the program state graph (ExplodedGraph).
The base class of the type hierarchy.
Definition: Type.h:1353
const ProgramStateRef & getState() const
Represents a C++ constructor within a class.
Definition: DeclCXX.h:2397
Defines the clang::Expr interface and subclasses for C++ expressions.
DeclarationName getDeclName() const
getDeclName - Get the actual, stored name of the declaration, which may be a special name...
Definition: Decl.h:291
LineState State
This class provides a convenience implementation for clone() using the Curiously-Recurring Template P...
bool isReferenceType() const
Definition: Type.h:5956
bool isLiveRegion(const MemRegion *region)
const LocationContext * getLocationContext() const
const LocationContext * getParent() const
Represents a non-static C++ member function call, no matter how it is written.
Definition: CallEvent.h:599
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
static ProgramStateRef removeFromState(ProgramStateRef State, const MemRegion *Region)
const RegionTy * getAs() const
Definition: MemRegion.h:1174
Represents a call to any sort of function that might have a FunctionDecl.
Definition: CallEvent.h:422
virtual const Decl * getDecl() const
Returns the declaration of the function or method that will be called.
Definition: CallEvent.h:205
bool isIdentifier() const
Predicate functions for querying what type of name this is.
ExplodedNode * generateNonFatalErrorNode(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generate a transition to a node that will be used to report an error.
bool isVoidPointerType() const
Definition: Type.cpp:426
void emitReport(std::unique_ptr< BugReport > R)
Emit the diagnostics report.
static const Stmt * getStmt(const ExplodedNode *N)
Given an exploded node, retrieve the statement that should be used for the diagnostic location...
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
#define false
Definition: stdbool.h:33
Kind
static bool isAnyBaseRegionReported(ProgramStateRef State, const MemRegion *Region)
CHECKER * registerChecker()
Used to register checkers.
std::string getNameAsString() const
getNameAsString - Get a human-readable name for the declaration, even if it is one of the special kin...
Definition: Decl.h:285
Represents a static or instance method of a struct/union/class.
Definition: DeclCXX.h:1964
A class responsible for cleaning up unused symbols.
Dataflow Directional Tag Classes.
bool isBooleanType() const
Definition: Type.h:6234
Represents an abstract call to a function or method along a particular path.
Definition: CallEvent.h:140
const Decl * getDecl() const
const ProgramStateRef & getState() const
X
Add a minimal nested name specifier fixit hint to allow lookup of a tag name from an outer enclosing ...
Definition: SemaDecl.cpp:13010
virtual bool isSubRegionOf(const MemRegion *R) const
Check if the region is a subregion of the given region.
Definition: MemRegion.cpp:1111
bool isVoidType() const
Definition: Type.h:6171
pred_iterator pred_begin()
SourceManager & getSourceManager()
const MemRegion * getBaseRegion() const
Definition: MemRegion.cpp:1093
StringRef getName() const
getName - Get the name of identifier for this declaration as a StringRef.
Definition: Decl.h:270
bool isPointerType() const
Definition: Type.h:5944
NamedDecl - This represents a decl with a name.
Definition: Decl.h:245
This class provides an interface through which checkers can create individual bug reports...
Definition: BugReporter.h:55
const LocationContext * getLocationContext() const
SourceManager & getSourceManager()
Definition: BugReporter.h:565