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