clang  19.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->isPureVirtual();
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()->getDeclName()
129  << "::" << MD->getDeclName() << "' 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>();
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 }
static char ID
Definition: Arena.cpp:183
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....
#define X(type, name)
Definition: Value.h:143
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
LineState State
static bool isVirtualCall(const CallExpr *CE)
bool getCheckerBooleanOption(StringRef CheckerName, StringRef OptionName, bool SearchInParents=false) const
Interprets an option's string value as a boolean.
Represents a static or instance method of a struct/union/class.
Definition: DeclCXX.h:2060
bool isVirtual() const
Definition: DeclCXX.h:2115
const CXXRecordDecl * getParent() const
Return the parent of this method declaration, which is the class in which this method is defined.
Definition: DeclCXX.h:2186
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition: Expr.h:2872
bool hasAttr() const
Definition: DeclBase.h:583
This represents one expression.
Definition: Expr.h:110
Annotates a diagnostic with some code that should be inserted, removed, or replaced to fix the proble...
Definition: Diagnostic.h:72
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:98
bool isPureVirtual() const
Whether this virtual function is pure, i.e.
Definition: Decl.h:2325
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:482
MemberExpr - [C99 6.5.2.3] Structure and Union Members.
Definition: Expr.h:3224
DeclarationName getDeclName() const
Get the actual, stored name of the declaration, which may be a special name.
Definition: Decl.h:315
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:292
ReturnStmt - This represents a return, optionally of an expression: return; return 4;.
Definition: Stmt.h:3019
Represents an abstract call to a function or method along a particular path.
Definition: CallEvent.h:153
const AnalyzerOptions & getAnalyzerOptions() const
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
const LangOptions & getLangOpts() const
CheckerNameRef getCurrentCheckerName() const
MemRegion - The root abstract class for all memory regions.
Definition: MemRegion.h:96
const char *const CXXObjectLifecycle
bool Call(InterpState &S, CodePtr OpPC, const Function *Func, uint32_t VarArgSize)
Definition: Interp.h:2179
The JSON file list parser is used to communicate input to InstallAPI.
Diagnostic wrappers for TextAPI types for error reporting.
Definition: Dominators.h:30
#define bool
Definition: stdbool.h:24
static void Profile(ObjectState X, FoldingSetNodeID &ID)