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  const ExplodedNode *PrevN,
76  BugReporterContext &BRC,
77  BugReport &BR) override;
78  };
79 };
80 } // end namespace
81 
82 // GDM (generic data map) to the memregion of this for the ctor and dtor.
83 REGISTER_MAP_WITH_PROGRAMSTATE(CtorDtorMap, const MemRegion *, ObjectState)
84 
85 std::shared_ptr<PathDiagnosticPiece>
86 VirtualCallChecker::VirtualBugVisitor::VisitNode(const ExplodedNode *N,
87  const ExplodedNode *PrevN,
88  BugReporterContext &BRC,
89  BugReport &BR) {
90  // We need the last ctor/dtor which call the virtual function.
91  // The visitor walks the ExplodedGraph backwards.
92  if (Found)
93  return nullptr;
94 
95  ProgramStateRef State = N->getState();
96  const LocationContext *LCtx = N->getLocationContext();
97  const CXXConstructorDecl *CD =
98  dyn_cast_or_null<CXXConstructorDecl>(LCtx->getDecl());
99  const CXXDestructorDecl *DD =
100  dyn_cast_or_null<CXXDestructorDecl>(LCtx->getDecl());
101 
102  if (!CD && !DD)
103  return nullptr;
104 
105  ProgramStateManager &PSM = State->getStateManager();
106  auto &SVB = PSM.getSValBuilder();
107  const auto *MD = dyn_cast<CXXMethodDecl>(LCtx->getDecl());
108  if (!MD)
109  return nullptr;
110  auto ThiSVal =
111  State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame()));
112  const MemRegion *Reg = ThiSVal.castAs<loc::MemRegionVal>().getRegion();
113  if (!Reg)
114  return nullptr;
115  if (Reg != ObjectRegion)
116  return nullptr;
117 
119  if (!S)
120  return nullptr;
121  Found = true;
122 
123  std::string InfoText;
124  if (CD)
125  InfoText = "This constructor of an object of type '" +
126  CD->getNameAsString() +
127  "' has not returned when the virtual method was called";
128  else
129  InfoText = "This destructor of an object of type '" +
130  DD->getNameAsString() +
131  "' has not returned when the virtual method was called";
132 
133  // Generate the extra diagnostic.
134  PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
135  N->getLocationContext());
136  return std::make_shared<PathDiagnosticEventPiece>(Pos, InfoText, true);
137 }
138 
139 // The function to check if a callexpr is a virtual function.
140 static bool isVirtualCall(const CallExpr *CE) {
141  bool CallIsNonVirtual = false;
142 
143  if (const MemberExpr *CME = dyn_cast<MemberExpr>(CE->getCallee())) {
144  // The member access is fully qualified (i.e., X::F).
145  // Treat this as a non-virtual call and do not warn.
146  if (CME->getQualifier())
147  CallIsNonVirtual = true;
148 
149  if (const Expr *Base = CME->getBase()) {
150  // The most derived class is marked final.
151  if (Base->getBestDynamicClassType()->hasAttr<FinalAttr>())
152  CallIsNonVirtual = true;
153  }
154  }
155 
156  const CXXMethodDecl *MD =
157  dyn_cast_or_null<CXXMethodDecl>(CE->getDirectCallee());
158  if (MD && MD->isVirtual() && !CallIsNonVirtual && !MD->hasAttr<FinalAttr>() &&
159  !MD->getParent()->hasAttr<FinalAttr>())
160  return true;
161  return false;
162 }
163 
164 // The BeginFunction callback when enter a constructor or a destructor.
165 void VirtualCallChecker::checkBeginFunction(CheckerContext &C) const {
166  registerCtorDtorCallInState(true, C);
167 }
168 
169 // The EndFunction callback when leave a constructor or a destructor.
170 void VirtualCallChecker::checkEndFunction(const ReturnStmt *RS,
171  CheckerContext &C) const {
172  registerCtorDtorCallInState(false, C);
173 }
174 
175 void VirtualCallChecker::checkPreCall(const CallEvent &Call,
176  CheckerContext &C) const {
177  const auto MC = dyn_cast<CXXMemberCall>(&Call);
178  if (!MC)
179  return;
180 
181  const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
182  if (!MD)
183  return;
184  ProgramStateRef State = C.getState();
185  const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
186 
187  if (IsPureOnly && !MD->isPure())
188  return;
189  if (!isVirtualCall(CE))
190  return;
191 
192  const MemRegion *Reg = MC->getCXXThisVal().getAsRegion();
193  const ObjectState *ObState = State->get<CtorDtorMap>(Reg);
194  if (!ObState)
195  return;
196  // Check if a virtual method is called.
197  // The GDM of constructor and destructor should be true.
198  if (*ObState == ObjectState::CtorCalled) {
199  if (IsPureOnly && MD->isPure())
200  reportBug("Call to pure virtual function during construction", true, Reg,
201  C);
202  else if (!MD->isPure())
203  reportBug("Call to virtual function during construction", false, Reg, C);
204  else
205  reportBug("Call to pure virtual function during construction", false, Reg,
206  C);
207  }
208 
209  if (*ObState == ObjectState::DtorCalled) {
210  if (IsPureOnly && MD->isPure())
211  reportBug("Call to pure virtual function during destruction", true, Reg,
212  C);
213  else if (!MD->isPure())
214  reportBug("Call to virtual function during destruction", false, Reg, C);
215  else
216  reportBug("Call to pure virtual function during construction", false, Reg,
217  C);
218  }
219 }
220 
221 void VirtualCallChecker::registerCtorDtorCallInState(bool IsBeginFunction,
222  CheckerContext &C) const {
223  const auto *LCtx = C.getLocationContext();
224  const auto *MD = dyn_cast_or_null<CXXMethodDecl>(LCtx->getDecl());
225  if (!MD)
226  return;
227 
228  ProgramStateRef State = C.getState();
229  auto &SVB = C.getSValBuilder();
230 
231  // Enter a constructor, set the corresponding memregion be true.
232  if (isa<CXXConstructorDecl>(MD)) {
233  auto ThiSVal =
234  State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame()));
235  const MemRegion *Reg = ThiSVal.getAsRegion();
236  if (IsBeginFunction)
237  State = State->set<CtorDtorMap>(Reg, ObjectState::CtorCalled);
238  else
239  State = State->remove<CtorDtorMap>(Reg);
240 
241  C.addTransition(State);
242  return;
243  }
244 
245  // Enter a Destructor, set the corresponding memregion be true.
246  if (isa<CXXDestructorDecl>(MD)) {
247  auto ThiSVal =
248  State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame()));
249  const MemRegion *Reg = ThiSVal.getAsRegion();
250  if (IsBeginFunction)
251  State = State->set<CtorDtorMap>(Reg, ObjectState::DtorCalled);
252  else
253  State = State->remove<CtorDtorMap>(Reg);
254 
255  C.addTransition(State);
256  return;
257  }
258 }
259 
260 void VirtualCallChecker::reportBug(StringRef Msg, bool IsSink,
261  const MemRegion *Reg,
262  CheckerContext &C) const {
263  ExplodedNode *N;
264  if (IsSink)
265  N = C.generateErrorNode();
266  else
267  N = C.generateNonFatalErrorNode();
268 
269  if (!N)
270  return;
271  if (!BT)
272  BT.reset(new BugType(
273  this, "Call to virtual function during construction or destruction",
274  "C++ Object Lifecycle"));
275 
276  auto Reporter = llvm::make_unique<BugReport>(*BT, Msg, N);
277  Reporter->addVisitor(llvm::make_unique<VirtualBugVisitor>(Reg));
278  C.emitReport(std::move(Reporter));
279 }
280 
281 void ento::registerVirtualCallChecker(CheckerManager &mgr) {
282  VirtualCallChecker *checker = mgr.registerChecker<VirtualCallChecker>();
283 
284  checker->IsPureOnly =
285  mgr.getAnalyzerOptions().getBooleanOption("PureOnly", false, checker);
286 }
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:2088
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
Represents a C++ constructor within a class.
Definition: DeclCXX.h:2480
LineState State
Represents a non-static C++ member function call.
Definition: CallEvent.h:717
bool hasAttr() const
Definition: DeclBase.h:531
static void Profile(ObjectState X, FoldingSetNodeID &ID)
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
This represents one expression.
Definition: Expr.h:105
Represents a C++ destructor within a class.
Definition: DeclCXX.h:2702
const Expr * getCallee() const
Definition: Expr.h:2325
#define bool
Definition: stdbool.h:31
ReturnStmt - This represents a return, optionally of an expression: return; return 4;...
Definition: Stmt.h:1444
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:1988
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:2043
Dataflow Directional Tag Classes.
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return 0.
Definition: Expr.cpp:1251
const CXXRecordDecl * getParent() const
Returns the parent of this method declaration, which is the class in which this method is defined...
Definition: DeclCXX.h:2168
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:13824
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:2467
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition: Expr.h:2285
static bool isVirtualCall(const CallExpr *CE)