clang  6.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(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 BugReporterVisitorImpl<VirtualBugVisitor> {
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.
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 
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->getCurrentStackFrame()));
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.
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(CheckerContext &C) const {
171  registerCtorDtorCallInState(false, C);
172 }
173 
174 void VirtualCallChecker::checkPreCall(const CallEvent &Call,
175  CheckerContext &C) const {
176  const auto MC = dyn_cast<CXXMemberCall>(&Call);
177  if (!MC)
178  return;
179 
180  const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
181  if (!MD)
182  return;
184  const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
185 
186  if (IsPureOnly && !MD->isPure())
187  return;
188  if (!isVirtualCall(CE))
189  return;
190 
191  const MemRegion *Reg = MC->getCXXThisVal().getAsRegion();
192  const ObjectState *ObState = State->get<CtorDtorMap>(Reg);
193  if (!ObState)
194  return;
195  // Check if a virtual method is called.
196  // The GDM of constructor and destructor should be true.
197  if (*ObState == ObjectState::CtorCalled) {
198  if (IsPureOnly && MD->isPure())
199  reportBug("Call to pure virtual function during construction", true, Reg,
200  C);
201  else if (!MD->isPure())
202  reportBug("Call to virtual function during construction", false, Reg, C);
203  else
204  reportBug("Call to pure virtual function during construction", false, Reg,
205  C);
206  }
207 
208  if (*ObState == ObjectState::DtorCalled) {
209  if (IsPureOnly && MD->isPure())
210  reportBug("Call to pure virtual function during destruction", true, Reg,
211  C);
212  else if (!MD->isPure())
213  reportBug("Call to virtual function during destruction", false, Reg, C);
214  else
215  reportBug("Call to pure virtual function during construction", false, Reg,
216  C);
217  }
218 }
219 
220 void VirtualCallChecker::registerCtorDtorCallInState(bool IsBeginFunction,
221  CheckerContext &C) const {
222  const auto *LCtx = C.getLocationContext();
223  const auto *MD = dyn_cast_or_null<CXXMethodDecl>(LCtx->getDecl());
224  if (!MD)
225  return;
226 
228  auto &SVB = C.getSValBuilder();
229 
230  // Enter a constructor, set the corresponding memregion be true.
231  if (isa<CXXConstructorDecl>(MD)) {
232  auto ThiSVal =
233  State->getSVal(SVB.getCXXThis(MD, LCtx->getCurrentStackFrame()));
234  const MemRegion *Reg = ThiSVal.getAsRegion();
235  if (IsBeginFunction)
236  State = State->set<CtorDtorMap>(Reg, ObjectState::CtorCalled);
237  else
238  State = State->remove<CtorDtorMap>(Reg);
239 
240  C.addTransition(State);
241  return;
242  }
243 
244  // Enter a Destructor, set the corresponding memregion be true.
245  if (isa<CXXDestructorDecl>(MD)) {
246  auto ThiSVal =
247  State->getSVal(SVB.getCXXThis(MD, LCtx->getCurrentStackFrame()));
248  const MemRegion *Reg = ThiSVal.getAsRegion();
249  if (IsBeginFunction)
250  State = State->set<CtorDtorMap>(Reg, ObjectState::DtorCalled);
251  else
252  State = State->remove<CtorDtorMap>(Reg);
253 
254  C.addTransition(State);
255  return;
256  }
257 }
258 
259 void VirtualCallChecker::reportBug(StringRef Msg, bool IsSink,
260  const MemRegion *Reg,
261  CheckerContext &C) const {
262  ExplodedNode *N;
263  if (IsSink)
264  N = C.generateErrorNode();
265  else
267 
268  if (!N)
269  return;
270  if (!BT)
271  BT.reset(new BugType(
272  this, "Call to virtual function during construction or destruction",
273  "C++ Object Lifecycle"));
274 
275  auto Reporter = llvm::make_unique<BugReport>(*BT, Msg, N);
276  Reporter->addVisitor(llvm::make_unique<VirtualBugVisitor>(Reg));
277  C.emitReport(std::move(Reporter));
278 }
279 
280 void ento::registerVirtualCallChecker(CheckerManager &mgr) {
281  VirtualCallChecker *checker = mgr.registerChecker<VirtualCallChecker>();
282 
283  checker->IsPureOnly =
284  mgr.getAnalyzerOptions().getBooleanOption("PureOnly", false, checker);
285 }
MemRegion - The root abstract class for all memory regions.
Definition: MemRegion.h:79
ExplodedNode * generateErrorNode(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generate a transition to a node that will be used to report an error.
DominatorTree GraphTraits specialization so the DominatorTree can be iterable by generic graph iterat...
Definition: Dominators.h:26
Stmt - This represents one statement.
Definition: Stmt.h:60
A helper class which wraps a boolean value set to false by default.
Definition: Checker.h:551
ExplodedNode * addTransition(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generates a new transition in the program state graph (ExplodedGraph).
bool isVirtual() const
Definition: DeclCXX.h:1962
const ProgramStateRef & getState() const
Represents a C++ constructor within a class.
Definition: DeclCXX.h:2344
const Expr * getOriginExpr() const
Returns the expression whose value will be the result of this call.
Definition: CallEvent.h:225
LineState State
This class provides a convenience implementation for clone() using the Curiously-Recurring Template P...
const StackFrameContext * getCurrentStackFrame() const
const LocationContext * getLocationContext() const
Represents a non-static C++ member function call.
Definition: CallEvent.h:637
bool hasAttr() const
Definition: DeclBase.h:521
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.
ID
Defines the address space values used by the address space qualifier of QualType. ...
Definition: AddressSpaces.h:26
Expr - This represents one expression.
Definition: Expr.h:106
Represents a C++ destructor within a class.
Definition: DeclCXX.h:2566
const Expr * getCallee() const
Definition: Expr.h:2249
#define bool
Definition: stdbool.h:31
virtual const Decl * getDecl() const
Returns the declaration of the function or method that will be called.
Definition: CallEvent.h:205
ExplodedNode * generateNonFatalErrorNode(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generate a transition to a node that will be used to report an error.
bool getBooleanOption(StringRef Name, bool DefaultVal, const ento::CheckerBase *C=nullptr, bool SearchInParents=false)
Interprets an option&#39;s string value as a boolean.
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...
CHECKER * registerChecker()
Used to register checkers.
bool isPure() const
Whether this virtual function is pure, i.e.
Definition: Decl.h:1909
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
Represents a static or instance method of a struct/union/class.
Definition: DeclCXX.h:1918
Dataflow Directional Tag Classes.
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return 0.
Definition: Expr.cpp:1216
const CXXRecordDecl * getParent() const
Returns the parent of this method declaration, which is the class in which this method is defined...
Definition: DeclCXX.h:2033
Represents an abstract call to a function or method along a particular path.
Definition: CallEvent.h:140
AnalyzerOptions & getAnalyzerOptions()
const Decl * getDecl() const
const ProgramStateRef & getState() const
X
Add a minimal nested name specifier fixit hint to allow lookup of a tag name from an outer enclosing ...
Definition: SemaDecl.cpp:13065
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:2381
SValBuilder & getSValBuilder()
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition: Expr.h:2209
This class provides an interface through which checkers can create individual bug reports...
Definition: BugReporter.h:55
static bool isVirtualCall(const CallExpr *CE)
const LocationContext * getLocationContext() const
SourceManager & getSourceManager()
Definition: BugReporter.h:565