clang  5.0.0
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 
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.
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 
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;
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.
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.
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 {
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 }
StringRef getName() const
getName - Get the name of identifier for this declaration as a StringRef.
Definition: Decl.h:237
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:60
ExplodedNode * addTransition(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generates a new transition in the program state graph (ExplodedGraph).
bool isVoidPointerType() const
Definition: Type.cpp:384
const RegionTy * getAs() const
Definition: MemRegion.h:1174
The base class of the type hierarchy.
Definition: Type.h:1303
bool isBooleanType() const
Definition: Type.h:5969
Represents a C++ constructor within a class.
Definition: DeclCXX.h:2329
const MemRegion * getBaseRegion() const
Definition: MemRegion.cpp:1091
Defines the clang::Expr interface and subclasses for C++ expressions.
bool isVoidType() const
Definition: Type.h:5906
LineState State
bool isIdentifier() const
Predicate functions for querying what type of name this is.
bool isReferenceType() const
Definition: Type.h:5721
This class provides a convenience implementation for clone() using the Curiously-Recurring Template P...
bool isLiveRegion(const MemRegion *region)
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:252
detail::InMemoryDirectory::const_iterator I
const LocationContext * getLocationContext() const
Represents a non-static C++ member function call, no matter how it is written.
Definition: CallEvent.h:612
#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 ProgramStateRef & getState() const
const ProgramStateRef & getState() const
Represents a call to any sort of function that might have a FunctionDecl.
Definition: CallEvent.h:422
DeclarationName getDeclName() const
getDeclName - Get the actual, stored name of the declaration, which may be a special name...
Definition: Decl.h:258
ExplodedNode * generateNonFatalErrorNode(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generate a transition to a node that will be used to report an error.
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.
const std::string ID
Represents a static or instance method of a struct/union/class.
Definition: DeclCXX.h:1903
const Decl * getDecl() const
A class responsible for cleaning up unused symbols.
virtual const Decl * getDecl() const
Returns the declaration of the function or method that will be called.
Definition: CallEvent.h:205
const LocationContext * getParent() const
detail::InMemoryDirectory::const_iterator E
Represents an abstract call to a function or method along a particular path.
Definition: CallEvent.h:140
X
Add a minimal nested name specifier fixit hint to allow lookup of a tag name from an outer enclosing ...
Definition: SemaDecl.cpp:13074
pred_iterator pred_begin()
SourceManager & getSourceManager()
virtual bool isSubRegionOf(const MemRegion *R) const
Check if the region is a subregion of the given region.
Definition: MemRegion.cpp:1109
NamedDecl - This represents a decl with a name.
Definition: Decl.h:213
This class provides an interface through which checkers can create individual bug reports...
Definition: BugReporter.h:55
SourceManager & getSourceManager()
Definition: BugReporter.h:565
const LocationContext * getLocationContext() const
bool isPointerType() const
Definition: Type.h:5712