1 //=======- VirtualCallChecker.cpp --------------------------------*- C++ -*-==//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This file defines a checker that checks virtual function calls during
11 // construction or destruction of C++ objects.
13 //===----------------------------------------------------------------------===//
15 #include "ClangSACheckers.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"
25 using namespace clang;
29 enum class ObjectState : bool { CtorCalled, DtorCalled };
31 // FIXME: Ascending over StackFrameContext maybe another method.
34 template <> struct FoldingSetTrait<ObjectState> {
35 static inline void Profile(ObjectState X, FoldingSetNodeID &ID) {
36 ID.AddInteger(static_cast<int>(X));
39 } // end namespace llvm
42 class VirtualCallChecker
43 : public Checker<check::BeginFunction, check::EndFunction, check::PreCall> {
44 mutable std::unique_ptr<BugType> BT;
47 // The flag to determine if pure virtual functions should be issued only.
48 DefaultBool IsPureOnly;
50 void checkBeginFunction(CheckerContext &C) const;
51 void checkEndFunction(CheckerContext &C) const;
52 void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
55 void registerCtorDtorCallInState(bool IsBeginFunction,
56 CheckerContext &C) const;
57 void reportBug(StringRef Msg, bool PureError, const MemRegion *Reg,
58 CheckerContext &C) const;
60 class VirtualBugVisitor : public BugReporterVisitorImpl<VirtualBugVisitor> {
62 const MemRegion *ObjectRegion;
66 VirtualBugVisitor(const MemRegion *R) : ObjectRegion(R), Found(false) {}
68 void Profile(llvm::FoldingSetNodeID &ID) const override {
71 ID.AddPointer(ObjectRegion);
74 std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
75 const ExplodedNode *PrevN,
76 BugReporterContext &BRC,
77 BugReport &BR) override;
82 // GDM (generic data map) to the memregion of this for the ctor and dtor.
83 REGISTER_MAP_WITH_PROGRAMSTATE(CtorDtorMap, const MemRegion *, ObjectState)
85 std::shared_ptr<PathDiagnosticPiece>
86 VirtualCallChecker::VirtualBugVisitor::VisitNode(const ExplodedNode *N,
87 const ExplodedNode *PrevN,
88 BugReporterContext &BRC,
90 // We need the last ctor/dtor which call the virtual function.
91 // The visitor walks the ExplodedGraph backwards.
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());
105 ProgramStateManager &PSM = State->getStateManager();
106 auto &SVB = PSM.getSValBuilder();
107 const auto *MD = dyn_cast<CXXMethodDecl>(LCtx->getDecl());
111 State->getSVal(SVB.getCXXThis(MD, LCtx->getCurrentStackFrame()));
112 const MemRegion *Reg = ThiSVal.castAs<loc::MemRegionVal>().getRegion();
115 if (Reg != ObjectRegion)
118 const Stmt *S = PathDiagnosticLocation::getStmt(N);
123 std::string InfoText;
125 InfoText = "This constructor of an object of type '" +
126 CD->getNameAsString() +
127 "' has not returned when the virtual method was called";
129 InfoText = "This destructor of an object of type '" +
130 DD->getNameAsString() +
131 "' has not returned when the virtual method was called";
133 // Generate the extra diagnostic.
134 PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
135 N->getLocationContext());
136 return std::make_shared<PathDiagnosticEventPiece>(Pos, InfoText, true);
139 // The function to check if a callexpr is a virtual function.
140 static bool isVirtualCall(const CallExpr *CE) {
141 bool CallIsNonVirtual = false;
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;
149 if (const Expr *Base = CME->getBase()) {
150 // The most derived class is marked final.
151 if (Base->getBestDynamicClassType()->hasAttr<FinalAttr>())
152 CallIsNonVirtual = true;
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>())
164 // The BeginFunction callback when enter a constructor or a destructor.
165 void VirtualCallChecker::checkBeginFunction(CheckerContext &C) const {
166 registerCtorDtorCallInState(true, C);
169 // The EndFunction callback when leave a constructor or a destructor.
170 void VirtualCallChecker::checkEndFunction(CheckerContext &C) const {
171 registerCtorDtorCallInState(false, C);
174 void VirtualCallChecker::checkPreCall(const CallEvent &Call,
175 CheckerContext &C) const {
176 const auto MC = dyn_cast<CXXMemberCall>(&Call);
180 const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
183 ProgramStateRef State = C.getState();
184 const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
186 if (IsPureOnly && !MD->isPure())
188 if (!isVirtualCall(CE))
191 const MemRegion *Reg = MC->getCXXThisVal().getAsRegion();
192 const ObjectState *ObState = State->get<CtorDtorMap>(Reg);
195 // Check if a virtual method is called.
196 // The GDM of constructor and destructor should be true.
197 if (*ObState == ObjectState::CtorCalled) {
198 if (IsPureOnly && MD->isPure())
199 reportBug("Call to pure virtual function during construction", true, Reg,
201 else if (!MD->isPure())
202 reportBug("Call to virtual function during construction", false, Reg, C);
204 reportBug("Call to pure virtual function during construction", false, Reg,
208 if (*ObState == ObjectState::DtorCalled) {
209 if (IsPureOnly && MD->isPure())
210 reportBug("Call to pure virtual function during destruction", true, Reg,
212 else if (!MD->isPure())
213 reportBug("Call to virtual function during destruction", false, Reg, C);
215 reportBug("Call to pure virtual function during construction", false, Reg,
220 void VirtualCallChecker::registerCtorDtorCallInState(bool IsBeginFunction,
221 CheckerContext &C) const {
222 const auto *LCtx = C.getLocationContext();
223 const auto *MD = dyn_cast_or_null<CXXMethodDecl>(LCtx->getDecl());
227 ProgramStateRef State = C.getState();
228 auto &SVB = C.getSValBuilder();
230 // Enter a constructor, set the corresponding memregion be true.
231 if (isa<CXXConstructorDecl>(MD)) {
233 State->getSVal(SVB.getCXXThis(MD, LCtx->getCurrentStackFrame()));
234 const MemRegion *Reg = ThiSVal.getAsRegion();
236 State = State->set<CtorDtorMap>(Reg, ObjectState::CtorCalled);
238 State = State->remove<CtorDtorMap>(Reg);
240 C.addTransition(State);
244 // Enter a Destructor, set the corresponding memregion be true.
245 if (isa<CXXDestructorDecl>(MD)) {
247 State->getSVal(SVB.getCXXThis(MD, LCtx->getCurrentStackFrame()));
248 const MemRegion *Reg = ThiSVal.getAsRegion();
250 State = State->set<CtorDtorMap>(Reg, ObjectState::DtorCalled);
252 State = State->remove<CtorDtorMap>(Reg);
254 C.addTransition(State);
259 void VirtualCallChecker::reportBug(StringRef Msg, bool IsSink,
260 const MemRegion *Reg,
261 CheckerContext &C) const {
264 N = C.generateErrorNode();
266 N = C.generateNonFatalErrorNode();
271 BT.reset(new BugType(
272 this, "Call to virtual function during construction or destruction",
273 "C++ Object Lifecycle"));
275 auto Reporter = llvm::make_unique<BugReport>(*BT, Msg, N);
276 Reporter->addVisitor(llvm::make_unique<VirtualBugVisitor>(Reg));
277 C.emitReport(std::move(Reporter));
280 void ento::registerVirtualCallChecker(CheckerManager &mgr) {
281 VirtualCallChecker *checker = mgr.registerChecker<VirtualCallChecker>();
283 checker->IsPureOnly =
284 mgr.getAnalyzerOptions().getBooleanOption("PureOnly", false, checker);