clang  8.0.0svn
VirtualCallChecker.cpp
Go to the documentation of this file.
1 //=======- VirtualCallChecker.cpp --------------------------------*- 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 file defines a checker that checks virtual function calls during
11 // construction or destruction of C++ objects.
12 //
13 //===----------------------------------------------------------------------===//
14 
15 #include "ClangSACheckers.h"
16 #include "clang/AST/DeclCXX.h"
24 
25 using namespace clang;
26 using namespace ento;
27 
28 namespace {
29 enum class ObjectState : bool { CtorCalled, DtorCalled };
30 } // end namespace
31  // FIXME: Ascending over StackFrameContext maybe another method.
32 
33 namespace llvm {
34 template <> struct FoldingSetTrait<ObjectState> {
35  static inline void Profile(ObjectState X, FoldingSetNodeID &ID) {
36  ID.AddInteger(static_cast<int>(X));
37  }
38 };
39 } // end namespace llvm
40 
41 namespace {
42 class VirtualCallChecker
43  : public Checker<check::BeginFunction, check::EndFunction, check::PreCall> {
44  mutable std::unique_ptr<BugType> BT;
45 
46 public:
47  // The flag to determine if pure virtual functions should be issued only.
48  DefaultBool IsPureOnly;
49 
50  void checkBeginFunction(CheckerContext &C) const;
51  void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
52  void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
53 
54 private:
55  void registerCtorDtorCallInState(bool IsBeginFunction,
56  CheckerContext &C) const;
57  void reportBug(StringRef Msg, bool PureError, const MemRegion *Reg,
58  CheckerContext &C) const;
59 
60  class VirtualBugVisitor : public BugReporterVisitor {
61  private:
62  const MemRegion *ObjectRegion;
63  bool Found;
64 
65  public:
66  VirtualBugVisitor(const MemRegion *R) : ObjectRegion(R), Found(false) {}
67 
68  void Profile(llvm::FoldingSetNodeID &ID) const override {
69  static int X = 0;
70  ID.AddPointer(&X);
71  ID.AddPointer(ObjectRegion);
72  }
73 
74  std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
75  BugReporterContext &BRC,
76  BugReport &BR) override;
77  };
78 };
79 } // end namespace
80 
81 // GDM (generic data map) to the memregion of this for the ctor and dtor.
82 REGISTER_MAP_WITH_PROGRAMSTATE(CtorDtorMap, const MemRegion *, ObjectState)
83 
84 std::shared_ptr<PathDiagnosticPiece>
85 VirtualCallChecker::VirtualBugVisitor::VisitNode(const ExplodedNode *N,
86  BugReporterContext &BRC,
87  BugReport &) {
88  // We need the last ctor/dtor which call the virtual function.
89  // The visitor walks the ExplodedGraph backwards.
90  if (Found)
91  return nullptr;
92 
93  ProgramStateRef State = N->getState();
94  const LocationContext *LCtx = N->getLocationContext();
95  const CXXConstructorDecl *CD =
96  dyn_cast_or_null<CXXConstructorDecl>(LCtx->getDecl());
97  const CXXDestructorDecl *DD =
98  dyn_cast_or_null<CXXDestructorDecl>(LCtx->getDecl());
99 
100  if (!CD && !DD)
101  return nullptr;
102 
103  ProgramStateManager &PSM = State->getStateManager();
104  auto &SVB = PSM.getSValBuilder();
105  const auto *MD = dyn_cast<CXXMethodDecl>(LCtx->getDecl());
106  if (!MD)
107  return nullptr;
108  auto ThiSVal =
109  State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame()));
110  const MemRegion *Reg = ThiSVal.castAs<loc::MemRegionVal>().getRegion();
111  if (!Reg)
112  return nullptr;
113  if (Reg != ObjectRegion)
114  return nullptr;
115 
117  if (!S)
118  return nullptr;
119  Found = true;
120 
121  std::string InfoText;
122  if (CD)
123  InfoText = "This constructor of an object of type '" +
124  CD->getNameAsString() +
125  "' has not returned when the virtual method was called";
126  else
127  InfoText = "This destructor of an object of type '" +
128  DD->getNameAsString() +
129  "' has not returned when the virtual method was called";
130 
131  // Generate the extra diagnostic.
132  PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
133  N->getLocationContext());
134  return std::make_shared<PathDiagnosticEventPiece>(Pos, InfoText, true);
135 }
136 
137 // The function to check if a callexpr is a virtual function.
138 static bool isVirtualCall(const CallExpr *CE) {
139  bool CallIsNonVirtual = false;
140 
141  if (const MemberExpr *CME = dyn_cast<MemberExpr>(CE->getCallee())) {
142  // The member access is fully qualified (i.e., X::F).
143  // Treat this as a non-virtual call and do not warn.
144  if (CME->getQualifier())
145  CallIsNonVirtual = true;
146 
147  if (const Expr *Base = CME->getBase()) {
148  // The most derived class is marked final.
149  if (Base->getBestDynamicClassType()->hasAttr<FinalAttr>())
150  CallIsNonVirtual = true;
151  }
152  }
153 
154  const CXXMethodDecl *MD =
155  dyn_cast_or_null<CXXMethodDecl>(CE->getDirectCallee());
156  if (MD && MD->isVirtual() && !CallIsNonVirtual && !MD->hasAttr<FinalAttr>() &&
157  !MD->getParent()->hasAttr<FinalAttr>())
158  return true;
159  return false;
160 }
161 
162 // The BeginFunction callback when enter a constructor or a destructor.
163 void VirtualCallChecker::checkBeginFunction(CheckerContext &C) const {
164  registerCtorDtorCallInState(true, C);
165 }
166 
167 // The EndFunction callback when leave a constructor or a destructor.
168 void VirtualCallChecker::checkEndFunction(const ReturnStmt *RS,
169  CheckerContext &C) const {
170  registerCtorDtorCallInState(false, C);
171 }
172 
173 void VirtualCallChecker::checkPreCall(const CallEvent &Call,
174  CheckerContext &C) const {
175  const auto MC = dyn_cast<CXXMemberCall>(&Call);
176  if (!MC)
177  return;
178 
179  const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
180  if (!MD)
181  return;
182  ProgramStateRef State = C.getState();
183  const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
184 
185  if (IsPureOnly && !MD->isPure())
186  return;
187  if (!isVirtualCall(CE))
188  return;
189 
190  const MemRegion *Reg = MC->getCXXThisVal().getAsRegion();
191  const ObjectState *ObState = State->get<CtorDtorMap>(Reg);
192  if (!ObState)
193  return;
194  // Check if a virtual method is called.
195  // The GDM of constructor and destructor should be true.
196  if (*ObState == ObjectState::CtorCalled) {
197  if (IsPureOnly && MD->isPure())
198  reportBug("Call to pure virtual function during construction", true, Reg,
199  C);
200  else if (!MD->isPure())
201  reportBug("Call to virtual function during construction", false, Reg, C);
202  else
203  reportBug("Call to pure virtual function during construction", false, Reg,
204  C);
205  }
206 
207  if (*ObState == ObjectState::DtorCalled) {
208  if (IsPureOnly && MD->isPure())
209  reportBug("Call to pure virtual function during destruction", true, Reg,
210  C);
211  else if (!MD->isPure())
212  reportBug("Call to virtual function during destruction", false, Reg, C);
213  else
214  reportBug("Call to pure virtual function during construction", false, Reg,
215  C);
216  }
217 }
218 
219 void VirtualCallChecker::registerCtorDtorCallInState(bool IsBeginFunction,
220  CheckerContext &C) const {
221  const auto *LCtx = C.getLocationContext();
222  const auto *MD = dyn_cast_or_null<CXXMethodDecl>(LCtx->getDecl());
223  if (!MD)
224  return;
225 
226  ProgramStateRef State = C.getState();
227  auto &SVB = C.getSValBuilder();
228 
229  // Enter a constructor, set the corresponding memregion be true.
230  if (isa<CXXConstructorDecl>(MD)) {
231  auto ThiSVal =
232  State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame()));
233  const MemRegion *Reg = ThiSVal.getAsRegion();
234  if (IsBeginFunction)
235  State = State->set<CtorDtorMap>(Reg, ObjectState::CtorCalled);
236  else
237  State = State->remove<CtorDtorMap>(Reg);
238 
239  C.addTransition(State);
240  return;
241  }
242 
243  // Enter a Destructor, set the corresponding memregion be true.
244  if (isa<CXXDestructorDecl>(MD)) {
245  auto ThiSVal =
246  State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame()));
247  const MemRegion *Reg = ThiSVal.getAsRegion();
248  if (IsBeginFunction)
249  State = State->set<CtorDtorMap>(Reg, ObjectState::DtorCalled);
250  else
251  State = State->remove<CtorDtorMap>(Reg);
252 
253  C.addTransition(State);
254  return;
255  }
256 }
257 
258 void VirtualCallChecker::reportBug(StringRef Msg, bool IsSink,
259  const MemRegion *Reg,
260  CheckerContext &C) const {
261  ExplodedNode *N;
262  if (IsSink)
263  N = C.generateErrorNode();
264  else
265  N = C.generateNonFatalErrorNode();
266 
267  if (!N)
268  return;
269  if (!BT)
270  BT.reset(new BugType(
271  this, "Call to virtual function during construction or destruction",
272  "C++ Object Lifecycle"));
273 
274  auto Reporter = llvm::make_unique<BugReport>(*BT, Msg, N);
275  Reporter->addVisitor(llvm::make_unique<VirtualBugVisitor>(Reg));
276  C.emitReport(std::move(Reporter));
277 }
278 
279 void ento::registerVirtualCallChecker(CheckerManager &mgr) {
280  VirtualCallChecker *checker = mgr.registerChecker<VirtualCallChecker>();
281 
282  checker->IsPureOnly =
283  mgr.getAnalyzerOptions().getCheckerBooleanOption("PureOnly", false,
284  checker);
285 }
DominatorTree GraphTraits specialization so the DominatorTree can be iterable by generic graph iterat...
Definition: Dominators.h:30
Stmt - This represents one statement.
Definition: Stmt.h:66
bool isVirtual() const
Definition: DeclCXX.h:2086
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
Represents a C++ constructor within a class.
Definition: DeclCXX.h:2478
LineState State
Represents a non-static C++ member function call.
Definition: CallEvent.h:706
bool hasAttr() const
Definition: DeclBase.h:531
static void Profile(ObjectState X, FoldingSetNodeID &ID)
This represents one expression.
Definition: Expr.h:106
Represents a C++ destructor within a class.
Definition: DeclCXX.h:2700
const Expr * getCallee() const
Definition: Expr.h:2445
#define bool
Definition: stdbool.h:31
ReturnStmt - This represents a return, optionally of an expression: return; return 4;...
Definition: Stmt.h:2141
static const Stmt * getStmt(const ExplodedNode *N)
Given an exploded node, retrieve the statement that should be used for the diagnostic location...
bool isPure() const
Whether this virtual function is pure, i.e.
Definition: Decl.h:2003
#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.
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return 0.
Definition: Expr.cpp:1279
const CXXRecordDecl * getParent() const
Returns the parent of this method declaration, which is the class in which this method is defined...
Definition: DeclCXX.h:2166
const Decl * getDecl() const
const StackFrameContext * getStackFrame() const
X
Add a minimal nested name specifier fixit hint to allow lookup of a tag name from an outer enclosing ...
Definition: SemaDecl.cpp:13853
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate.h) and friends (in DeclFriend.h).
MemberExpr - [C99 6.5.2.3] Structure and Union Members.
Definition: Expr.h:2587
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition: Expr.h:2405
static bool isVirtualCall(const CallExpr *CE)