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