clang  10.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 method 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 public:
44  // These are going to be null if the respective check is disabled.
45  mutable std::unique_ptr<BugType> BT_Pure, BT_Impure;
46  bool ShowFixIts = false;
47 
48  void checkBeginFunction(CheckerContext &C) const;
49  void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
50  void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
51 
52 private:
53  void registerCtorDtorCallInState(bool IsBeginFunction,
54  CheckerContext &C) const;
55 };
56 } // end namespace
57 
58 // GDM (generic data map) to the memregion of this for the ctor and dtor.
59 REGISTER_MAP_WITH_PROGRAMSTATE(CtorDtorMap, const MemRegion *, ObjectState)
60 
61 // The function to check if a callexpr is a virtual method call.
62 static bool isVirtualCall(const CallExpr *CE) {
63  bool CallIsNonVirtual = false;
64 
65  if (const MemberExpr *CME = dyn_cast<MemberExpr>(CE->getCallee())) {
66  // The member access is fully qualified (i.e., X::F).
67  // Treat this as a non-virtual call and do not warn.
68  if (CME->getQualifier())
69  CallIsNonVirtual = true;
70 
71  if (const Expr *Base = CME->getBase()) {
72  // The most derived class is marked final.
73  if (Base->getBestDynamicClassType()->hasAttr<FinalAttr>())
74  CallIsNonVirtual = true;
75  }
76  }
77 
78  const CXXMethodDecl *MD =
79  dyn_cast_or_null<CXXMethodDecl>(CE->getDirectCallee());
80  if (MD && MD->isVirtual() && !CallIsNonVirtual && !MD->hasAttr<FinalAttr>() &&
81  !MD->getParent()->hasAttr<FinalAttr>())
82  return true;
83  return false;
84 }
85 
86 // The BeginFunction callback when enter a constructor or a destructor.
87 void VirtualCallChecker::checkBeginFunction(CheckerContext &C) const {
88  registerCtorDtorCallInState(true, C);
89 }
90 
91 // The EndFunction callback when leave a constructor or a destructor.
92 void VirtualCallChecker::checkEndFunction(const ReturnStmt *RS,
93  CheckerContext &C) const {
94  registerCtorDtorCallInState(false, C);
95 }
96 
97 void VirtualCallChecker::checkPreCall(const CallEvent &Call,
98  CheckerContext &C) const {
99  const auto MC = dyn_cast<CXXMemberCall>(&Call);
100  if (!MC)
101  return;
102 
103  const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
104  if (!MD)
105  return;
106 
107  ProgramStateRef State = C.getState();
108  // Member calls are always represented by a call-expression.
109  const auto *CE = cast<CallExpr>(Call.getOriginExpr());
110  if (!isVirtualCall(CE))
111  return;
112 
113  const MemRegion *Reg = MC->getCXXThisVal().getAsRegion();
114  const ObjectState *ObState = State->get<CtorDtorMap>(Reg);
115  if (!ObState)
116  return;
117 
118  bool IsPure = MD->isPure();
119 
120  // At this point we're sure that we're calling a virtual method
121  // during construction or destruction, so we'll emit a report.
122  SmallString<128> Msg;
123  llvm::raw_svector_ostream OS(Msg);
124  OS << "Call to ";
125  if (IsPure)
126  OS << "pure ";
127  OS << "virtual method '" << MD->getParent()->getNameAsString()
128  << "::" << MD->getNameAsString() << "' during ";
129  if (*ObState == ObjectState::CtorCalled)
130  OS << "construction ";
131  else
132  OS << "destruction ";
133  if (IsPure)
134  OS << "has undefined behavior";
135  else
136  OS << "bypasses virtual dispatch";
137 
138  ExplodedNode *N =
139  IsPure ? C.generateErrorNode() : C.generateNonFatalErrorNode();
140  if (!N)
141  return;
142 
143  const std::unique_ptr<BugType> &BT = IsPure ? BT_Pure : BT_Impure;
144  if (!BT) {
145  // The respective check is disabled.
146  return;
147  }
148 
149  auto Report = std::make_unique<PathSensitiveBugReport>(*BT, OS.str(), N);
150 
151  if (ShowFixIts && !IsPure) {
152  // FIXME: These hints are valid only when the virtual call is made
153  // directly from the constructor/destructor. Otherwise the dispatch
154  // will work just fine from other callees, and the fix may break
155  // the otherwise correct program.
157  CE->getBeginLoc(), MD->getParent()->getNameAsString() + "::");
158  Report->addFixItHint(Fixit);
159  }
160 
161  C.emitReport(std::move(Report));
162 }
163 
164 void VirtualCallChecker::registerCtorDtorCallInState(bool IsBeginFunction,
165  CheckerContext &C) const {
166  const auto *LCtx = C.getLocationContext();
167  const auto *MD = dyn_cast_or_null<CXXMethodDecl>(LCtx->getDecl());
168  if (!MD)
169  return;
170 
171  ProgramStateRef State = C.getState();
172  auto &SVB = C.getSValBuilder();
173 
174  // Enter a constructor, set the corresponding memregion be true.
175  if (isa<CXXConstructorDecl>(MD)) {
176  auto ThiSVal =
177  State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame()));
178  const MemRegion *Reg = ThiSVal.getAsRegion();
179  if (IsBeginFunction)
180  State = State->set<CtorDtorMap>(Reg, ObjectState::CtorCalled);
181  else
182  State = State->remove<CtorDtorMap>(Reg);
183 
184  C.addTransition(State);
185  return;
186  }
187 
188  // Enter a Destructor, set the corresponding memregion be true.
189  if (isa<CXXDestructorDecl>(MD)) {
190  auto ThiSVal =
191  State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame()));
192  const MemRegion *Reg = ThiSVal.getAsRegion();
193  if (IsBeginFunction)
194  State = State->set<CtorDtorMap>(Reg, ObjectState::DtorCalled);
195  else
196  State = State->remove<CtorDtorMap>(Reg);
197 
198  C.addTransition(State);
199  return;
200  }
201 }
202 
203 void ento::registerVirtualCallModeling(CheckerManager &Mgr) {
204  Mgr.registerChecker<VirtualCallChecker>();
205 }
206 
207 void ento::registerPureVirtualCallChecker(CheckerManager &Mgr) {
208  auto *Chk = Mgr.getChecker<VirtualCallChecker>();
209  Chk->BT_Pure = std::make_unique<BugType>(Mgr.getCurrentCheckerName(),
210  "Pure virtual method call",
212 }
213 
214 void ento::registerVirtualCallChecker(CheckerManager &Mgr) {
215  auto *Chk = Mgr.getChecker<VirtualCallChecker>();
216  if (!Mgr.getAnalyzerOptions().getCheckerBooleanOption(
217  Mgr.getCurrentCheckerName(), "PureOnly")) {
218  Chk->BT_Impure = std::make_unique<BugType>(
219  Mgr.getCurrentCheckerName(), "Unexpected loss of virtual dispatch",
221  Chk->ShowFixIts = Mgr.getAnalyzerOptions().getCheckerBooleanOption(
222  Mgr.getCurrentCheckerName(), "ShowFixIts");
223  }
224 }
225 
226 bool ento::shouldRegisterVirtualCallModeling(const LangOptions &LO) {
227  return LO.CPlusPlus;
228 }
229 
230 bool ento::shouldRegisterPureVirtualCallChecker(const LangOptions &LO) {
231  return LO.CPlusPlus;
232 }
233 
234 bool ento::shouldRegisterVirtualCallChecker(const LangOptions &LO) {
235  return LO.CPlusPlus;
236 }
Specialize PointerLikeTypeTraits to allow LazyGenerationalUpdatePtr to be placed into a PointerUnion...
Definition: Dominators.h:30
bool isVirtual() const
Definition: DeclCXX.h:2111
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
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:674
bool hasAttr() const
Definition: DeclBase.h:542
static void Profile(ObjectState X, FoldingSetNodeID &ID)
This represents one expression.
Definition: Expr.h:108
#define bool
Definition: stdbool.h:15
ReturnStmt - This represents a return, optionally of an expression: return; return 4;...
Definition: Stmt.h:2610
bool isPure() const
Whether this virtual function is pure, i.e.
Definition: Decl.h:2031
#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:2068
Dataflow Directional Tag Classes.
const CXXRecordDecl * getParent() const
Return the parent of this method declaration, which is the class in which this method is defined...
Definition: DeclCXX.h:2191
Indicates that the tracking object is a descendant of a referenced-counted OSObject, used in the Darwin kernel.
static FixItHint CreateInsertion(SourceLocation InsertionLoc, StringRef Code, bool BeforePreviousInsertions=false)
Create a code modification hint that inserts the given code string at a specific location.
Definition: Diagnostic.h:92
X
Add a minimal nested name specifier fixit hint to allow lookup of a tag name from an outer enclosing ...
Definition: SemaDecl.cpp:14584
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:2806
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition: Expr.h:2516
const char *const CXXObjectLifecycle
Annotates a diagnostic with some code that should be inserted, removed, or replaced to fix the proble...
Definition: Diagnostic.h:66
static bool isVirtualCall(const CallExpr *CE)