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