clang 10.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"
16 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.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
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.
157 FixItHint Fixit = FixItHint::CreateInsertion(
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",
212 categories::CXXObjectLifecycle);
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",
221 categories::CXXObjectLifecycle);
222 Chk->ShowFixIts = Mgr.getAnalyzerOptions().getCheckerBooleanOption(
223 Mgr.getCurrentCheckerName(), "ShowFixIts");
224 }
225 }
226
227 bool ento::shouldRegisterVirtualCallModeling(const LangOptions &LO) {
228 return LO.CPlusPlus;
229 }
230
231 bool ento::shouldRegisterPureVirtualCallChecker(const LangOptions &LO) {
232 return LO.CPlusPlus;
233 }
234
235 bool ento::shouldRegisterVirtualCallChecker(const LangOptions &LO) {
236 return LO.CPlusPlus;
237 }
clang::Builtin::ID
ID
Definition: Builtins.h:46
ento
llvm
Specialize PointerLikeTypeTraits to allow LazyGenerationalUpdatePtr to be placed into a PointerUnion...
Definition: Dominators.h:30
Base
clang::CXXMethodDecl::isVirtual
bool isVirtual() const
Definition: DeclCXX.h:1976
clang::ento::ProgramStateRef
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
Definition: ProgramState_Fwd.h:37
ObjectState
ObjectState
Definition: VirtualCallChecker.cpp:29
BugType.h
State
LineState State
Definition: UnwrappedLineFormatter.cpp:932
Checker.h
clang::LangOptions
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:53
llvm::SmallString< 128 >
clang::CXXMemberCall
Represents a non-static C++ member function call.
Definition: CallEvent.h:674
clang::Decl::hasAttr
bool hasAttr() const
Definition: DeclBase.h:542
llvm::FoldingSetTrait< ObjectState >::Profile
static void Profile(ObjectState X, FoldingSetNodeID &ID)
Definition: VirtualCallChecker.cpp:35
BugReporter.h
clang::Expr
This represents one expression.
Definition: Expr.h:108
bool
#define bool
Definition: stdbool.h:15
clang::ReturnStmt
ReturnStmt - This represents a return, optionally of an expression: return; return 4;...
Definition: Stmt.h:2636
BuiltinCheckerRegistration.h
Attr.h
clang::FunctionDecl::isPure
bool isPure() const
Whether this virtual function is pure, i.e.
Definition: Decl.h:2105
REGISTER_MAP_WITH_PROGRAMSTATE
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
Definition: ProgramStateTrait.h:84
clang::NamedDecl::getNameAsString
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
clang::CXXMethodDecl
Represents a static or instance method of a struct/union/class.
Definition: DeclCXX.h:1931
SValBuilder.h
clang
Dataflow Directional Tag Classes.
Definition: CFGReachabilityAnalysis.h:21
clang::CXXMethodDecl::getParent
const CXXRecordDecl * getParent() const
Return the parent of this method declaration, which is the class in which this method is defined...
Definition: DeclCXX.h:2046
clang::ento::ObjKind::OS
Indicates that the tracking object is a descendant of a referenced-counted OSObject, used in the Darwin kernel.
clang::FixItHint::CreateInsertion
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
X
Add a minimal nested name specifier fixit hint to allow lookup of a tag name from an outer enclosing ...
Definition: SemaDecl.cpp:14780
DeclCXX.h
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate.h) and friends (in DeclFriend.h).
clang::MemberExpr
MemberExpr - [C99 6.5.2.3] Structure and Union Members.
Definition: Expr.h:2836
clang::CallExpr
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition: Expr.h:2546
clang::ento::categories::CXXObjectLifecycle
const char *const CXXObjectLifecycle
Definition: CommonBugCategories.cpp:20
clang::FixItHint
Annotates a diagnostic with some code that should be inserted, removed, or replaced to fix the proble...
Definition: Diagnostic.h:66
ProgramStateTrait.h
isVirtualCall
static bool isVirtualCall(const CallExpr *CE)
Definition: VirtualCallChecker.cpp:63
CallEvent.h
CallEvent
CheckerContext.h