clang 8.0.0

VirtualCallChecker.cpp
Go to the documentation of this file.
1 //=======- VirtualCallChecker.cpp --------------------------------*- C++ -*-==//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This file defines a checker that checks virtual function calls during
11 // construction or destruction of C++ objects.
12 //
13 //===----------------------------------------------------------------------===//
14
15 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
16 #include "clang/AST/DeclCXX.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 mutable std::unique_ptr<BugType> BT;
45
46 public:
47 // The flag to determine if pure virtual functions should be issued only.
48 DefaultBool IsPureOnly;
49
50 void checkBeginFunction(CheckerContext &C) const;
51 void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
52 void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
53
54 private:
55 void registerCtorDtorCallInState(bool IsBeginFunction,
56 CheckerContext &C) const;
57 void reportBug(StringRef Msg, bool PureError, const MemRegion *Reg,
58 CheckerContext &C) const;
59
60 class VirtualBugVisitor : public BugReporterVisitor {
61 private:
62 const MemRegion *ObjectRegion;
63 bool Found;
64
65 public:
66 VirtualBugVisitor(const MemRegion *R) : ObjectRegion(R), Found(false) {}
67
68 void Profile(llvm::FoldingSetNodeID &ID) const override {
69 static int X = 0;
70 ID.AddPointer(&X);
71 ID.AddPointer(ObjectRegion);
72 }
73
74 std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
75 BugReporterContext &BRC,
76 BugReport &BR) override;
77 };
78 };
79 } // end namespace
80
81 // GDM (generic data map) to the memregion of this for the ctor and dtor.
82 REGISTER_MAP_WITH_PROGRAMSTATE(CtorDtorMap, const MemRegion *, ObjectState)
83
84 std::shared_ptr<PathDiagnosticPiece>
85 VirtualCallChecker::VirtualBugVisitor::VisitNode(const ExplodedNode *N,
86 BugReporterContext &BRC,
87 BugReport &) {
88 // We need the last ctor/dtor which call the virtual function.
89 // The visitor walks the ExplodedGraph backwards.
90 if (Found)
91 return nullptr;
92
93 ProgramStateRef State = N->getState();
94 const LocationContext *LCtx = N->getLocationContext();
95 const CXXConstructorDecl *CD =
96 dyn_cast_or_null<CXXConstructorDecl>(LCtx->getDecl());
97 const CXXDestructorDecl *DD =
98 dyn_cast_or_null<CXXDestructorDecl>(LCtx->getDecl());
99
100 if (!CD && !DD)
101 return nullptr;
102
103 ProgramStateManager &PSM = State->getStateManager();
104 auto &SVB = PSM.getSValBuilder();
105 const auto *MD = dyn_cast<CXXMethodDecl>(LCtx->getDecl());
106 if (!MD)
107 return nullptr;
108 auto ThiSVal =
109 State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame()));
110 const MemRegion *Reg = ThiSVal.castAs<loc::MemRegionVal>().getRegion();
111 if (!Reg)
112 return nullptr;
113 if (Reg != ObjectRegion)
114 return nullptr;
115
116 const Stmt *S = PathDiagnosticLocation::getStmt(N);
117 if (!S)
118 return nullptr;
119 Found = true;
120
121 std::string InfoText;
122 if (CD)
123 InfoText = "This constructor of an object of type '" +
124 CD->getNameAsString() +
125 "' has not returned when the virtual method was called";
126 else
127 InfoText = "This destructor of an object of type '" +
128 DD->getNameAsString() +
129 "' has not returned when the virtual method was called";
130
131 // Generate the extra diagnostic.
132 PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
133 N->getLocationContext());
134 return std::make_shared<PathDiagnosticEventPiece>(Pos, InfoText, true);
135 }
136
137 // The function to check if a callexpr is a virtual function.
138 static bool isVirtualCall(const CallExpr *CE) {
139 bool CallIsNonVirtual = false;
140
141 if (const MemberExpr *CME = dyn_cast<MemberExpr>(CE->getCallee())) {
142 // The member access is fully qualified (i.e., X::F).
143 // Treat this as a non-virtual call and do not warn.
144 if (CME->getQualifier())
145 CallIsNonVirtual = true;
146
147 if (const Expr *Base = CME->getBase()) {
148 // The most derived class is marked final.
149 if (Base->getBestDynamicClassType()->hasAttr<FinalAttr>())
150 CallIsNonVirtual = true;
151 }
152 }
153
154 const CXXMethodDecl *MD =
155 dyn_cast_or_null<CXXMethodDecl>(CE->getDirectCallee());
156 if (MD && MD->isVirtual() && !CallIsNonVirtual && !MD->hasAttr<FinalAttr>() &&
157 !MD->getParent()->hasAttr<FinalAttr>())
158 return true;
159 return false;
160 }
161
162 // The BeginFunction callback when enter a constructor or a destructor.
163 void VirtualCallChecker::checkBeginFunction(CheckerContext &C) const {
164 registerCtorDtorCallInState(true, C);
165 }
166
167 // The EndFunction callback when leave a constructor or a destructor.
168 void VirtualCallChecker::checkEndFunction(const ReturnStmt *RS,
169 CheckerContext &C) const {
170 registerCtorDtorCallInState(false, C);
171 }
172
173 void VirtualCallChecker::checkPreCall(const CallEvent &Call,
174 CheckerContext &C) const {
175 const auto MC = dyn_cast<CXXMemberCall>(&Call);
176 if (!MC)
177 return;
178
179 const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
180 if (!MD)
181 return;
182 ProgramStateRef State = C.getState();
183 const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
184
185 if (IsPureOnly && !MD->isPure())
186 return;
187 if (!isVirtualCall(CE))
188 return;
189
190 const MemRegion *Reg = MC->getCXXThisVal().getAsRegion();
191 const ObjectState *ObState = State->get<CtorDtorMap>(Reg);
192 if (!ObState)
193 return;
194 // Check if a virtual method is called.
195 // The GDM of constructor and destructor should be true.
196 if (*ObState == ObjectState::CtorCalled) {
197 if (IsPureOnly && MD->isPure())
198 reportBug("Call to pure virtual function during construction", true, Reg,
199 C);
200 else if (!MD->isPure())
201 reportBug("Call to virtual function during construction", false, Reg, C);
202 else
203 reportBug("Call to pure virtual function during construction", false, Reg,
204 C);
205 }
206
207 if (*ObState == ObjectState::DtorCalled) {
208 if (IsPureOnly && MD->isPure())
209 reportBug("Call to pure virtual function during destruction", true, Reg,
210 C);
211 else if (!MD->isPure())
212 reportBug("Call to virtual function during destruction", false, Reg, C);
213 else
214 reportBug("Call to pure virtual function during construction", false, Reg,
215 C);
216 }
217 }
218
219 void VirtualCallChecker::registerCtorDtorCallInState(bool IsBeginFunction,
220 CheckerContext &C) const {
221 const auto *LCtx = C.getLocationContext();
222 const auto *MD = dyn_cast_or_null<CXXMethodDecl>(LCtx->getDecl());
223 if (!MD)
224 return;
225
226 ProgramStateRef State = C.getState();
227 auto &SVB = C.getSValBuilder();
228
229 // Enter a constructor, set the corresponding memregion be true.
230 if (isa<CXXConstructorDecl>(MD)) {
231 auto ThiSVal =
232 State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame()));
233 const MemRegion *Reg = ThiSVal.getAsRegion();
234 if (IsBeginFunction)
235 State = State->set<CtorDtorMap>(Reg, ObjectState::CtorCalled);
236 else
237 State = State->remove<CtorDtorMap>(Reg);
238
239 C.addTransition(State);
240 return;
241 }
242
243 // Enter a Destructor, set the corresponding memregion be true.
244 if (isa<CXXDestructorDecl>(MD)) {
245 auto ThiSVal =
246 State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame()));
247 const MemRegion *Reg = ThiSVal.getAsRegion();
248 if (IsBeginFunction)
249 State = State->set<CtorDtorMap>(Reg, ObjectState::DtorCalled);
250 else
251 State = State->remove<CtorDtorMap>(Reg);
252
253 C.addTransition(State);
254 return;
255 }
256 }
257
258 void VirtualCallChecker::reportBug(StringRef Msg, bool IsSink,
259 const MemRegion *Reg,
260 CheckerContext &C) const {
261 ExplodedNode *N;
262 if (IsSink)
263 N = C.generateErrorNode();
264 else
265 N = C.generateNonFatalErrorNode();
266
267 if (!N)
268 return;
269 if (!BT)
270 BT.reset(new BugType(
271 this, "Call to virtual function during construction or destruction",
272 "C++ Object Lifecycle"));
273
274 auto Reporter = llvm::make_unique<BugReport>(*BT, Msg, N);
275 Reporter->addVisitor(llvm::make_unique<VirtualBugVisitor>(Reg));
276 C.emitReport(std::move(Reporter));
277 }
278
279 void ento::registerVirtualCallChecker(CheckerManager &mgr) {
280 VirtualCallChecker *checker = mgr.registerChecker<VirtualCallChecker>();
281
282 checker->IsPureOnly =
283 mgr.getAnalyzerOptions().getCheckerBooleanOption("PureOnly", false,
284 checker);
285 }
clang::Builtin::ID
ID
Definition: Builtins.h:49
ento
llvm
DominatorTree GraphTraits specialization so the DominatorTree can be iterable by generic graph iterat...
Definition: Dominators.h:30
clang::Stmt
Stmt - This represents one statement.
Definition: Stmt.h:66
Base
clang::CXXMethodDecl::isVirtual
bool isVirtual() const
Definition: DeclCXX.h:2086
clang::ento::ProgramStateRef
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
Definition: ProgramState_Fwd.h:38
ObjectState
ObjectState
Definition: VirtualCallChecker.cpp:29
clang::LocationContext
Definition: AnalysisDeclContext.h:218
BugType.h
clang::CXXConstructorDecl
Represents a C++ constructor within a class.
Definition: DeclCXX.h:2484
BugReporterVisitor
State
LineState State
Definition: UnwrappedLineFormatter.cpp:877
Checker.h
clang::CXXMemberCall
Represents a non-static C++ member function call.
Definition: CallEvent.h:706
clang::Decl::hasAttr
bool hasAttr() const
Definition: DeclBase.h:531
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:106
clang::CXXDestructorDecl
Represents a C++ destructor within a class.
Definition: DeclCXX.h:2706
clang::CallExpr::getCallee
Expr * getCallee()
Definition: Expr.h:2514
bool
#define bool
Definition: stdbool.h:31
clang::CallExpr::getDirectCallee
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return null.
Definition: Expr.h:2532
clang::ReturnStmt
ReturnStmt - This represents a return, optionally of an expression: return; return 4;...
Definition: Stmt.h:2443
BuiltinCheckerRegistration.h
clang::ento::PathDiagnosticLocation::getStmt
static const Stmt * getStmt(const ExplodedNode *N)
Given an exploded node, retrieve the statement that should be used for the diagnostic location...
Definition: PathDiagnostic.cpp:767
clang::FunctionDecl::isPure
bool isPure() const
Whether this virtual function is pure, i.e.
Definition: Decl.h:2009
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:85
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:292
clang::CXXMethodDecl
Represents a static or instance method of a struct/union/class.
Definition: DeclCXX.h:2041
SValBuilder.h
clang
Dataflow Directional Tag Classes.
Definition: CFGReachabilityAnalysis.h:22
clang::CXXMethodDecl::getParent
const CXXRecordDecl * getParent() const
Returns the parent of this method declaration, which is the class in which this method is defined...
Definition: DeclCXX.h:2166
clang::LocationContext::getDecl
const Decl * getDecl() const
Definition: AnalysisDeclContext.h:253
clang::LocationContext::getStackFrame
const StackFrameContext * getStackFrame() const
Definition: AnalysisDeclContext.cpp:440
X
X
Add a minimal nested name specifier fixit hint to allow lookup of a tag name from an outer enclosing ...
Definition: SemaDecl.cpp:13954
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:2682
clang::CallExpr
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition: Expr.h:2396
ProgramStateTrait.h
isVirtualCall
static bool isVirtualCall(const CallExpr *CE)
Definition: VirtualCallChecker.cpp:138
CallEvent.h
CallEvent
CheckerContext.h