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