blob: a210128bab7a9c21b3c19021caee9fe73bd8dd14 [file] [log] [blame]
Ted Kremenekb3512d32012-01-03 23:18:57 +00001//=======- VirtualCallChecker.cpp --------------------------------*- C++ -*-==//
2//
Chandler Carruth2946cd72019-01-19 08:50:56 +00003// 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
Ted Kremenekb3512d32012-01-03 23:18:57 +00006//
7//===----------------------------------------------------------------------===//
8//
Artem Dergachevd3971fe2019-08-20 21:41:14 +00009// This file defines a checker that checks virtual method calls during
Ted Kremenekb3512d32012-01-03 23:18:57 +000010// construction or destruction of C++ objects.
11//
12//===----------------------------------------------------------------------===//
13
Kristof Umann76a21502018-12-15 16:23:51 +000014#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
Ted Kremenekb3512d32012-01-03 23:18:57 +000015#include "clang/AST/DeclCXX.h"
Ted Kremenekb3512d32012-01-03 23:18:57 +000016#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
Gabor Horvath857ccd22017-08-28 08:44:43 +000017#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
Chandler Carruth3a022472012-12-04 09:13:33 +000018#include "clang/StaticAnalyzer/Core/Checker.h"
Gabor Horvath857ccd22017-08-28 08:44:43 +000019#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
20#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
21#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
22#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
Ted Kremenekb3512d32012-01-03 23:18:57 +000023
24using namespace clang;
25using namespace ento;
26
27namespace {
Gabor Horvath857ccd22017-08-28 08:44:43 +000028enum class ObjectState : bool { CtorCalled, DtorCalled };
29} // end namespace
30 // FIXME: Ascending over StackFrameContext maybe another method.
Ted Kremenekb3512d32012-01-03 23:18:57 +000031
Gabor Horvath857ccd22017-08-28 08:44:43 +000032namespace llvm {
33template <> struct FoldingSetTrait<ObjectState> {
34 static inline void Profile(ObjectState X, FoldingSetNodeID &ID) {
35 ID.AddInteger(static_cast<int>(X));
Devin Coughlin3e5f0472016-12-10 01:16:09 +000036 }
Ted Kremenekb3512d32012-01-03 23:18:57 +000037};
Gabor Horvath857ccd22017-08-28 08:44:43 +000038} // end namespace llvm
Ted Kremenekb3512d32012-01-03 23:18:57 +000039
40namespace {
Gabor Horvath857ccd22017-08-28 08:44:43 +000041class VirtualCallChecker
42 : public Checker<check::BeginFunction, check::EndFunction, check::PreCall> {
Ted Kremenekb3512d32012-01-03 23:18:57 +000043public:
Artem Dergachevd3971fe2019-08-20 21:41:14 +000044 // These are going to be null if the respective check is disabled.
45 mutable std::unique_ptr<BugType> BT_Pure, BT_Impure;
Artem Dergachev6cee4342019-09-06 20:55:29 +000046 bool ShowFixIts = false;
Devin Coughlin3e5f0472016-12-10 01:16:09 +000047
Gabor Horvath857ccd22017-08-28 08:44:43 +000048 void checkBeginFunction(CheckerContext &C) const;
Reka Kovacsed8c05c2018-07-16 20:47:45 +000049 void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
Gabor Horvath857ccd22017-08-28 08:44:43 +000050 void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
Ted Kremenekb3512d32012-01-03 23:18:57 +000051
Gabor Horvath857ccd22017-08-28 08:44:43 +000052private:
53 void registerCtorDtorCallInState(bool IsBeginFunction,
54 CheckerContext &C) const;
Ted Kremenekb3512d32012-01-03 23:18:57 +000055};
Gabor Horvath857ccd22017-08-28 08:44:43 +000056} // end namespace
57
58// GDM (generic data map) to the memregion of this for the ctor and dtor.
59REGISTER_MAP_WITH_PROGRAMSTATE(CtorDtorMap, const MemRegion *, ObjectState)
60
Artem Dergachevd3971fe2019-08-20 21:41:14 +000061// The function to check if a callexpr is a virtual method call.
Gabor Horvath857ccd22017-08-28 08:44:43 +000062static bool isVirtualCall(const CallExpr *CE) {
63 bool CallIsNonVirtual = false;
64
65 if (const MemberExpr *CME = dyn_cast<MemberExpr>(CE->getCallee())) {
66 // The member access is fully qualified (i.e., X::F).
67 // Treat this as a non-virtual call and do not warn.
68 if (CME->getQualifier())
69 CallIsNonVirtual = true;
70
Gabor Horvath5536a012017-09-21 08:18:59 +000071 if (const Expr *Base = CME->getBase()) {
Gabor Horvath857ccd22017-08-28 08:44:43 +000072 // The most derived class is marked final.
73 if (Base->getBestDynamicClassType()->hasAttr<FinalAttr>())
74 CallIsNonVirtual = true;
75 }
76 }
77
78 const CXXMethodDecl *MD =
79 dyn_cast_or_null<CXXMethodDecl>(CE->getDirectCallee());
80 if (MD && MD->isVirtual() && !CallIsNonVirtual && !MD->hasAttr<FinalAttr>() &&
81 !MD->getParent()->hasAttr<FinalAttr>())
82 return true;
83 return false;
84}
85
86// The BeginFunction callback when enter a constructor or a destructor.
87void VirtualCallChecker::checkBeginFunction(CheckerContext &C) const {
88 registerCtorDtorCallInState(true, C);
89}
90
91// The EndFunction callback when leave a constructor or a destructor.
Reka Kovacsed8c05c2018-07-16 20:47:45 +000092void VirtualCallChecker::checkEndFunction(const ReturnStmt *RS,
93 CheckerContext &C) const {
Gabor Horvath857ccd22017-08-28 08:44:43 +000094 registerCtorDtorCallInState(false, C);
95}
96
97void VirtualCallChecker::checkPreCall(const CallEvent &Call,
98 CheckerContext &C) const {
99 const auto MC = dyn_cast<CXXMemberCall>(&Call);
100 if (!MC)
101 return;
102
103 const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
104 if (!MD)
105 return;
Artem Dergachevd3971fe2019-08-20 21:41:14 +0000106
Gabor Horvath857ccd22017-08-28 08:44:43 +0000107 ProgramStateRef State = C.getState();
Artem Dergachev630f7da2019-08-28 18:44:38 +0000108 // Member calls are always represented by a call-expression.
109 const auto *CE = cast<CallExpr>(Call.getOriginExpr());
Gabor Horvath857ccd22017-08-28 08:44:43 +0000110 if (!isVirtualCall(CE))
111 return;
112
113 const MemRegion *Reg = MC->getCXXThisVal().getAsRegion();
114 const ObjectState *ObState = State->get<CtorDtorMap>(Reg);
115 if (!ObState)
116 return;
Artem Dergachevd3971fe2019-08-20 21:41:14 +0000117
118 bool IsPure = MD->isPure();
119
120 // At this point we're sure that we're calling a virtual method
121 // during construction or destruction, so we'll emit a report.
122 SmallString<128> Msg;
123 llvm::raw_svector_ostream OS(Msg);
124 OS << "Call to ";
125 if (IsPure)
126 OS << "pure ";
127 OS << "virtual method '" << MD->getParent()->getNameAsString()
128 << "::" << MD->getNameAsString() << "' during ";
129 if (*ObState == ObjectState::CtorCalled)
130 OS << "construction ";
131 else
132 OS << "destruction ";
133 if (IsPure)
134 OS << "has undefined behavior";
135 else
136 OS << "bypasses virtual dispatch";
137
138 ExplodedNode *N =
139 IsPure ? C.generateErrorNode() : C.generateNonFatalErrorNode();
140 if (!N)
141 return;
142
143 const std::unique_ptr<BugType> &BT = IsPure ? BT_Pure : BT_Impure;
144 if (!BT) {
145 // The respective check is disabled.
146 return;
Gabor Horvath857ccd22017-08-28 08:44:43 +0000147 }
148
Artem Dergachevd3971fe2019-08-20 21:41:14 +0000149 auto Report = std::make_unique<BugReport>(*BT, OS.str(), N);
Artem Dergachev6cee4342019-09-06 20:55:29 +0000150
151 if (ShowFixIts && !IsPure) {
152 // FIXME: These hints are valid only when the virtual call is made
153 // directly from the constructor/destructor. Otherwise the dispatch
154 // will work just fine from other callees, and the fix may break
155 // the otherwise correct program.
156 FixItHint Fixit = FixItHint::CreateInsertion(
157 CE->getBeginLoc(), MD->getParent()->getNameAsString() + "::");
158 Report->addFixItHint(Fixit);
159 }
160
Artem Dergachevd3971fe2019-08-20 21:41:14 +0000161 C.emitReport(std::move(Report));
Gabor Horvath857ccd22017-08-28 08:44:43 +0000162}
163
164void VirtualCallChecker::registerCtorDtorCallInState(bool IsBeginFunction,
165 CheckerContext &C) const {
166 const auto *LCtx = C.getLocationContext();
167 const auto *MD = dyn_cast_or_null<CXXMethodDecl>(LCtx->getDecl());
168 if (!MD)
169 return;
170
171 ProgramStateRef State = C.getState();
172 auto &SVB = C.getSValBuilder();
173
174 // Enter a constructor, set the corresponding memregion be true.
175 if (isa<CXXConstructorDecl>(MD)) {
176 auto ThiSVal =
George Karpenkovdd18b112018-06-27 01:51:55 +0000177 State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame()));
Gabor Horvath857ccd22017-08-28 08:44:43 +0000178 const MemRegion *Reg = ThiSVal.getAsRegion();
179 if (IsBeginFunction)
180 State = State->set<CtorDtorMap>(Reg, ObjectState::CtorCalled);
181 else
182 State = State->remove<CtorDtorMap>(Reg);
183
184 C.addTransition(State);
185 return;
186 }
187
188 // Enter a Destructor, set the corresponding memregion be true.
189 if (isa<CXXDestructorDecl>(MD)) {
190 auto ThiSVal =
George Karpenkovdd18b112018-06-27 01:51:55 +0000191 State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame()));
Gabor Horvath857ccd22017-08-28 08:44:43 +0000192 const MemRegion *Reg = ThiSVal.getAsRegion();
193 if (IsBeginFunction)
194 State = State->set<CtorDtorMap>(Reg, ObjectState::DtorCalled);
195 else
196 State = State->remove<CtorDtorMap>(Reg);
197
198 C.addTransition(State);
199 return;
200 }
201}
202
Artem Dergachevd3971fe2019-08-20 21:41:14 +0000203void ento::registerVirtualCallModeling(CheckerManager &Mgr) {
204 Mgr.registerChecker<VirtualCallChecker>();
Alexander Kornienkoab9db512015-06-22 23:07:51 +0000205}
Ted Kremenekb3512d32012-01-03 23:18:57 +0000206
Artem Dergachevd3971fe2019-08-20 21:41:14 +0000207void ento::registerPureVirtualCallChecker(CheckerManager &Mgr) {
208 auto *Chk = Mgr.getChecker<VirtualCallChecker>();
209 Chk->BT_Pure = std::make_unique<BugType>(
210 Mgr.getCurrentCheckName(), "Pure virtual method call",
211 categories::CXXObjectLifecycle);
212}
Devin Coughlin3e5f0472016-12-10 01:16:09 +0000213
Artem Dergachevd3971fe2019-08-20 21:41:14 +0000214void ento::registerVirtualCallChecker(CheckerManager &Mgr) {
215 auto *Chk = Mgr.getChecker<VirtualCallChecker>();
216 if (!Mgr.getAnalyzerOptions().getCheckerBooleanOption(
217 Mgr.getCurrentCheckName(), "PureOnly")) {
218 Chk->BT_Impure = std::make_unique<BugType>(
219 Mgr.getCurrentCheckName(), "Unexpected loss of virtual dispatch",
220 categories::CXXObjectLifecycle);
Artem Dergachev6cee4342019-09-06 20:55:29 +0000221 Chk->ShowFixIts = Mgr.getAnalyzerOptions().getCheckerBooleanOption(
222 Mgr.getCurrentCheckName(), "ShowFixIts");
Artem Dergachevd3971fe2019-08-20 21:41:14 +0000223 }
224}
225
226bool ento::shouldRegisterVirtualCallModeling(const LangOptions &LO) {
227 return LO.CPlusPlus;
228}
229
230bool ento::shouldRegisterPureVirtualCallChecker(const LangOptions &LO) {
231 return LO.CPlusPlus;
Ted Kremenekb3512d32012-01-03 23:18:57 +0000232}
Kristof Umann058a7a42019-01-26 14:23:08 +0000233
234bool ento::shouldRegisterVirtualCallChecker(const LangOptions &LO) {
Artem Dergachevd3971fe2019-08-20 21:41:14 +0000235 return LO.CPlusPlus;
Kristof Umann058a7a42019-01-26 14:23:08 +0000236}