VirtualCallChecker.cpp revision 344779
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 "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
16#include "clang/AST/DeclCXX.h"
17#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
18#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
19#include "clang/StaticAnalyzer/Core/Checker.h"
20#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
21#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
22#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
23#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
24
25using namespace clang;
26using namespace ento;
27
28namespace {
29enum class ObjectState : bool { CtorCalled, DtorCalled };
30} // end namespace
31  // FIXME: Ascending over StackFrameContext maybe another method.
32
33namespace llvm {
34template <> 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
41namespace {
42class VirtualCallChecker
43    : public Checker<check::BeginFunction, check::EndFunction, check::PreCall> {
44  mutable std::unique_ptr<BugType> BT;
45
46public:
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
54private:
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.
82REGISTER_MAP_WITH_PROGRAMSTATE(CtorDtorMap, const MemRegion *, ObjectState)
83
84std::shared_ptr<PathDiagnosticPiece>
85VirtualCallChecker::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
116  const Stmt *S = PathDiagnosticLocation::getStmt(N);
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.
138static 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.
163void VirtualCallChecker::checkBeginFunction(CheckerContext &C) const {
164  registerCtorDtorCallInState(true, C);
165}
166
167// The EndFunction callback when leave a constructor or a destructor.
168void VirtualCallChecker::checkEndFunction(const ReturnStmt *RS,
169                                          CheckerContext &C) const {
170  registerCtorDtorCallInState(false, C);
171}
172
173void 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
219void 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
258void 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
279void ento::registerVirtualCallChecker(CheckerManager &mgr) {
280  VirtualCallChecker *checker = mgr.registerChecker<VirtualCallChecker>();
281
282  checker->IsPureOnly =
283      mgr.getAnalyzerOptions().getCheckerBooleanOption("PureOnly", false,
284                                                       checker);
285}
286