blob: 5b602468cdd4b602fc0ca53067af384509d4b6b1 [file] [log] [blame]
Ted Kremenekb3512d32012-01-03 23:18:57 +00001//=======- 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//
Ted Kremenek3a0678e2015-09-08 03:50:52 +000010// This file defines a checker that checks virtual function calls during
Ted Kremenekb3512d32012-01-03 23:18:57 +000011// construction or destruction of C++ objects.
12//
13//===----------------------------------------------------------------------===//
14
15#include "ClangSACheckers.h"
16#include "clang/AST/DeclCXX.h"
Ted Kremenekb3512d32012-01-03 23:18:57 +000017#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
Gabor Horvath857ccd22017-08-28 08:44:43 +000018#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
Chandler Carruth3a022472012-12-04 09:13:33 +000019#include "clang/StaticAnalyzer/Core/Checker.h"
Gabor Horvath857ccd22017-08-28 08:44:43 +000020#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"
Ted Kremenekb3512d32012-01-03 23:18:57 +000024
25using namespace clang;
26using namespace ento;
27
28namespace {
Gabor Horvath857ccd22017-08-28 08:44:43 +000029enum class ObjectState : bool { CtorCalled, DtorCalled };
30} // end namespace
31 // FIXME: Ascending over StackFrameContext maybe another method.
Ted Kremenekb3512d32012-01-03 23:18:57 +000032
Gabor Horvath857ccd22017-08-28 08:44:43 +000033namespace llvm {
34template <> struct FoldingSetTrait<ObjectState> {
35 static inline void Profile(ObjectState X, FoldingSetNodeID &ID) {
36 ID.AddInteger(static_cast<int>(X));
Devin Coughlin3e5f0472016-12-10 01:16:09 +000037 }
Ted Kremenekb3512d32012-01-03 23:18:57 +000038};
Gabor Horvath857ccd22017-08-28 08:44:43 +000039} // end namespace llvm
Ted Kremenekb3512d32012-01-03 23:18:57 +000040
41namespace {
Gabor Horvath857ccd22017-08-28 08:44:43 +000042class VirtualCallChecker
43 : public Checker<check::BeginFunction, check::EndFunction, check::PreCall> {
44 mutable std::unique_ptr<BugType> BT;
45
Ted Kremenekb3512d32012-01-03 23:18:57 +000046public:
Gabor Horvath857ccd22017-08-28 08:44:43 +000047 // The flag to determine if pure virtual functions should be issued only.
48 DefaultBool IsPureOnly;
Devin Coughlin3e5f0472016-12-10 01:16:09 +000049
Gabor Horvath857ccd22017-08-28 08:44:43 +000050 void checkBeginFunction(CheckerContext &C) const;
Reka Kovacsed8c05c2018-07-16 20:47:45 +000051 void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
Gabor Horvath857ccd22017-08-28 08:44:43 +000052 void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
Ted Kremenekb3512d32012-01-03 23:18:57 +000053
Gabor Horvath857ccd22017-08-28 08:44:43 +000054private:
55 void registerCtorDtorCallInState(bool IsBeginFunction,
56 CheckerContext &C) const;
57 void reportBug(StringRef Msg, bool PureError, const MemRegion *Reg,
58 CheckerContext &C) const;
59
George Karpenkov70ec1dd2018-06-26 21:12:08 +000060 class VirtualBugVisitor : public BugReporterVisitor {
Gabor Horvath857ccd22017-08-28 08:44:43 +000061 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);
Ted Kremenekb3512d32012-01-03 23:18:57 +000072 }
73
Gabor Horvath857ccd22017-08-28 08:44:43 +000074 std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
75 const ExplodedNode *PrevN,
76 BugReporterContext &BRC,
77 BugReport &BR) override;
78 };
Ted Kremenekb3512d32012-01-03 23:18:57 +000079};
Gabor Horvath857ccd22017-08-28 08:44:43 +000080} // end namespace
81
82// GDM (generic data map) to the memregion of this for the ctor and dtor.
83REGISTER_MAP_WITH_PROGRAMSTATE(CtorDtorMap, const MemRegion *, ObjectState)
84
85std::shared_ptr<PathDiagnosticPiece>
86VirtualCallChecker::VirtualBugVisitor::VisitNode(const ExplodedNode *N,
87 const ExplodedNode *PrevN,
88 BugReporterContext &BRC,
89 BugReport &BR) {
90 // We need the last ctor/dtor which call the virtual function.
91 // The visitor walks the ExplodedGraph backwards.
92 if (Found)
93 return nullptr;
94
95 ProgramStateRef State = N->getState();
96 const LocationContext *LCtx = N->getLocationContext();
97 const CXXConstructorDecl *CD =
98 dyn_cast_or_null<CXXConstructorDecl>(LCtx->getDecl());
99 const CXXDestructorDecl *DD =
100 dyn_cast_or_null<CXXDestructorDecl>(LCtx->getDecl());
101
102 if (!CD && !DD)
103 return nullptr;
104
105 ProgramStateManager &PSM = State->getStateManager();
106 auto &SVB = PSM.getSValBuilder();
107 const auto *MD = dyn_cast<CXXMethodDecl>(LCtx->getDecl());
108 if (!MD)
109 return nullptr;
110 auto ThiSVal =
George Karpenkovdd18b112018-06-27 01:51:55 +0000111 State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame()));
Gabor Horvath857ccd22017-08-28 08:44:43 +0000112 const MemRegion *Reg = ThiSVal.castAs<loc::MemRegionVal>().getRegion();
113 if (!Reg)
114 return nullptr;
115 if (Reg != ObjectRegion)
116 return nullptr;
117
118 const Stmt *S = PathDiagnosticLocation::getStmt(N);
119 if (!S)
120 return nullptr;
121 Found = true;
122
123 std::string InfoText;
124 if (CD)
125 InfoText = "This constructor of an object of type '" +
126 CD->getNameAsString() +
127 "' has not returned when the virtual method was called";
128 else
129 InfoText = "This destructor of an object of type '" +
130 DD->getNameAsString() +
131 "' has not returned when the virtual method was called";
132
133 // Generate the extra diagnostic.
134 PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
135 N->getLocationContext());
136 return std::make_shared<PathDiagnosticEventPiece>(Pos, InfoText, true);
137}
138
139// The function to check if a callexpr is a virtual function.
140static bool isVirtualCall(const CallExpr *CE) {
141 bool CallIsNonVirtual = false;
142
143 if (const MemberExpr *CME = dyn_cast<MemberExpr>(CE->getCallee())) {
144 // The member access is fully qualified (i.e., X::F).
145 // Treat this as a non-virtual call and do not warn.
146 if (CME->getQualifier())
147 CallIsNonVirtual = true;
148
Gabor Horvath5536a012017-09-21 08:18:59 +0000149 if (const Expr *Base = CME->getBase()) {
Gabor Horvath857ccd22017-08-28 08:44:43 +0000150 // The most derived class is marked final.
151 if (Base->getBestDynamicClassType()->hasAttr<FinalAttr>())
152 CallIsNonVirtual = true;
153 }
154 }
155
156 const CXXMethodDecl *MD =
157 dyn_cast_or_null<CXXMethodDecl>(CE->getDirectCallee());
158 if (MD && MD->isVirtual() && !CallIsNonVirtual && !MD->hasAttr<FinalAttr>() &&
159 !MD->getParent()->hasAttr<FinalAttr>())
160 return true;
161 return false;
162}
163
164// The BeginFunction callback when enter a constructor or a destructor.
165void VirtualCallChecker::checkBeginFunction(CheckerContext &C) const {
166 registerCtorDtorCallInState(true, C);
167}
168
169// The EndFunction callback when leave a constructor or a destructor.
Reka Kovacsed8c05c2018-07-16 20:47:45 +0000170void VirtualCallChecker::checkEndFunction(const ReturnStmt *RS,
171 CheckerContext &C) const {
Gabor Horvath857ccd22017-08-28 08:44:43 +0000172 registerCtorDtorCallInState(false, C);
173}
174
175void VirtualCallChecker::checkPreCall(const CallEvent &Call,
176 CheckerContext &C) const {
177 const auto MC = dyn_cast<CXXMemberCall>(&Call);
178 if (!MC)
179 return;
180
181 const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
182 if (!MD)
183 return;
184 ProgramStateRef State = C.getState();
185 const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
186
187 if (IsPureOnly && !MD->isPure())
188 return;
189 if (!isVirtualCall(CE))
190 return;
191
192 const MemRegion *Reg = MC->getCXXThisVal().getAsRegion();
193 const ObjectState *ObState = State->get<CtorDtorMap>(Reg);
194 if (!ObState)
195 return;
196 // Check if a virtual method is called.
197 // The GDM of constructor and destructor should be true.
198 if (*ObState == ObjectState::CtorCalled) {
199 if (IsPureOnly && MD->isPure())
200 reportBug("Call to pure virtual function during construction", true, Reg,
201 C);
202 else if (!MD->isPure())
203 reportBug("Call to virtual function during construction", false, Reg, C);
204 else
205 reportBug("Call to pure virtual function during construction", false, Reg,
206 C);
207 }
208
209 if (*ObState == ObjectState::DtorCalled) {
210 if (IsPureOnly && MD->isPure())
211 reportBug("Call to pure virtual function during destruction", true, Reg,
212 C);
213 else if (!MD->isPure())
214 reportBug("Call to virtual function during destruction", false, Reg, C);
215 else
216 reportBug("Call to pure virtual function during construction", false, Reg,
217 C);
218 }
219}
220
221void VirtualCallChecker::registerCtorDtorCallInState(bool IsBeginFunction,
222 CheckerContext &C) const {
223 const auto *LCtx = C.getLocationContext();
224 const auto *MD = dyn_cast_or_null<CXXMethodDecl>(LCtx->getDecl());
225 if (!MD)
226 return;
227
228 ProgramStateRef State = C.getState();
229 auto &SVB = C.getSValBuilder();
230
231 // Enter a constructor, set the corresponding memregion be true.
232 if (isa<CXXConstructorDecl>(MD)) {
233 auto ThiSVal =
George Karpenkovdd18b112018-06-27 01:51:55 +0000234 State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame()));
Gabor Horvath857ccd22017-08-28 08:44:43 +0000235 const MemRegion *Reg = ThiSVal.getAsRegion();
236 if (IsBeginFunction)
237 State = State->set<CtorDtorMap>(Reg, ObjectState::CtorCalled);
238 else
239 State = State->remove<CtorDtorMap>(Reg);
240
241 C.addTransition(State);
242 return;
243 }
244
245 // Enter a Destructor, set the corresponding memregion be true.
246 if (isa<CXXDestructorDecl>(MD)) {
247 auto ThiSVal =
George Karpenkovdd18b112018-06-27 01:51:55 +0000248 State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame()));
Gabor Horvath857ccd22017-08-28 08:44:43 +0000249 const MemRegion *Reg = ThiSVal.getAsRegion();
250 if (IsBeginFunction)
251 State = State->set<CtorDtorMap>(Reg, ObjectState::DtorCalled);
252 else
253 State = State->remove<CtorDtorMap>(Reg);
254
255 C.addTransition(State);
256 return;
257 }
258}
259
260void VirtualCallChecker::reportBug(StringRef Msg, bool IsSink,
261 const MemRegion *Reg,
262 CheckerContext &C) const {
263 ExplodedNode *N;
264 if (IsSink)
265 N = C.generateErrorNode();
266 else
267 N = C.generateNonFatalErrorNode();
268
269 if (!N)
270 return;
271 if (!BT)
272 BT.reset(new BugType(
273 this, "Call to virtual function during construction or destruction",
274 "C++ Object Lifecycle"));
275
276 auto Reporter = llvm::make_unique<BugReport>(*BT, Msg, N);
277 Reporter->addVisitor(llvm::make_unique<VirtualBugVisitor>(Reg));
278 C.emitReport(std::move(Reporter));
Alexander Kornienkoab9db512015-06-22 23:07:51 +0000279}
Ted Kremenekb3512d32012-01-03 23:18:57 +0000280
281void ento::registerVirtualCallChecker(CheckerManager &mgr) {
Devin Coughlin3e5f0472016-12-10 01:16:09 +0000282 VirtualCallChecker *checker = mgr.registerChecker<VirtualCallChecker>();
Devin Coughlin3e5f0472016-12-10 01:16:09 +0000283
Gabor Horvath857ccd22017-08-28 08:44:43 +0000284 checker->IsPureOnly =
285 mgr.getAnalyzerOptions().getBooleanOption("PureOnly", false, checker);
Ted Kremenekb3512d32012-01-03 23:18:57 +0000286}