1 //=======- VirtualCallChecker.cpp --------------------------------*- C++ -*-==//
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
7 //===----------------------------------------------------------------------===//
9 // This file defines a checker that checks virtual method calls during
10 // construction or destruction of C++ objects.
12 //===----------------------------------------------------------------------===//
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"
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
> {
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;
49 void checkBeginFunction(CheckerContext
&C
) const;
50 void checkEndFunction(const ReturnStmt
*RS
, CheckerContext
&C
) const;
51 void checkPreCall(const CallEvent
&Call
, CheckerContext
&C
) const;
54 void registerCtorDtorCallInState(bool IsBeginFunction
,
55 CheckerContext
&C
) const;
59 // GDM (generic data map) to the memregion of this for the ctor and dtor.
60 REGISTER_MAP_WITH_PROGRAMSTATE(CtorDtorMap
, const MemRegion
*, ObjectState
)
62 // The function to check if a callexpr is a virtual method call.
63 static bool isVirtualCall(const CallExpr
*CE
) {
64 bool CallIsNonVirtual
= false;
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;
72 if (const Expr
*Base
= CME
->getBase()) {
73 // The most derived class is marked final.
74 if (Base
->getBestDynamicClassType()->hasAttr
<FinalAttr
>())
75 CallIsNonVirtual
= true;
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
>())
87 // The BeginFunction callback when enter a constructor or a destructor.
88 void VirtualCallChecker::checkBeginFunction(CheckerContext
&C
) const {
89 registerCtorDtorCallInState(true, C
);
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
);
98 void VirtualCallChecker::checkPreCall(const CallEvent
&Call
,
99 CheckerContext
&C
) const {
100 const auto MC
= dyn_cast
<CXXMemberCall
>(&Call
);
104 const CXXMethodDecl
*MD
= dyn_cast_or_null
<CXXMethodDecl
>(Call
.getDecl());
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
))
114 const MemRegion
*Reg
= MC
->getCXXThisVal().getAsRegion();
115 const ObjectState
*ObState
= State
->get
<CtorDtorMap
>(Reg
);
119 bool IsPure
= MD
->isPure();
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
);
128 OS
<< "virtual method '" << MD
->getParent()->getDeclName()
129 << "::" << MD
->getDeclName() << "' during ";
130 if (*ObState
== ObjectState::CtorCalled
)
131 OS
<< "construction ";
133 OS
<< "destruction ";
135 OS
<< "has undefined behavior";
137 OS
<< "bypasses virtual dispatch";
140 IsPure
? C
.generateErrorNode() : C
.generateNonFatalErrorNode();
144 const std::unique_ptr
<BugType
> &BT
= IsPure
? BT_Pure
: BT_Impure
;
146 // The respective check is disabled.
150 auto Report
= std::make_unique
<PathSensitiveBugReport
>(*BT
, OS
.str(), N
);
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
);
162 C
.emitReport(std::move(Report
));
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());
172 ProgramStateRef State
= C
.getState();
173 auto &SVB
= C
.getSValBuilder();
175 // Enter a constructor, set the corresponding memregion be true.
176 if (isa
<CXXConstructorDecl
>(MD
)) {
178 State
->getSVal(SVB
.getCXXThis(MD
, LCtx
->getStackFrame()));
179 const MemRegion
*Reg
= ThiSVal
.getAsRegion();
181 State
= State
->set
<CtorDtorMap
>(Reg
, ObjectState::CtorCalled
);
183 State
= State
->remove
<CtorDtorMap
>(Reg
);
185 C
.addTransition(State
);
189 // Enter a Destructor, set the corresponding memregion be true.
190 if (isa
<CXXDestructorDecl
>(MD
)) {
192 State
->getSVal(SVB
.getCXXThis(MD
, LCtx
->getStackFrame()));
193 const MemRegion
*Reg
= ThiSVal
.getAsRegion();
195 State
= State
->set
<CtorDtorMap
>(Reg
, ObjectState::DtorCalled
);
197 State
= State
->remove
<CtorDtorMap
>(Reg
);
199 C
.addTransition(State
);
204 void ento::registerVirtualCallModeling(CheckerManager
&Mgr
) {
205 Mgr
.registerChecker
<VirtualCallChecker
>();
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
);
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");
227 bool ento::shouldRegisterVirtualCallModeling(const CheckerManager
&mgr
) {
228 const LangOptions
&LO
= mgr
.getLangOpts();
232 bool ento::shouldRegisterPureVirtualCallChecker(const CheckerManager
&mgr
) {
233 const LangOptions
&LO
= mgr
.getLangOpts();
237 bool ento::shouldRegisterVirtualCallChecker(const CheckerManager
&mgr
) {
238 const LangOptions
&LO
= mgr
.getLangOpts();