xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp (revision d3971fe97b64785c079d64bf4c8c3e2b5e1f85a1)
1 //=======- VirtualCallChecker.cpp --------------------------------*- C++ -*-==//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 //  This file defines a checker that checks virtual method calls during
10 //  construction or destruction of C++ objects.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
15 #include "clang/AST/DeclCXX.h"
16 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
17 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
18 #include "clang/StaticAnalyzer/Core/Checker.h"
19 #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"
23 
24 using namespace clang;
25 using namespace ento;
26 
27 namespace {
28 enum class ObjectState : bool { CtorCalled, DtorCalled };
29 } // end namespace
30   // FIXME: Ascending over StackFrameContext maybe another method.
31 
32 namespace llvm {
33 template <> struct FoldingSetTrait<ObjectState> {
34   static inline void Profile(ObjectState X, FoldingSetNodeID &ID) {
35     ID.AddInteger(static_cast<int>(X));
36   }
37 };
38 } // end namespace llvm
39 
40 namespace {
41 class VirtualCallChecker
42     : public Checker<check::BeginFunction, check::EndFunction, check::PreCall> {
43 public:
44   // These are going to be null if the respective check is disabled.
45   mutable std::unique_ptr<BugType> BT_Pure, BT_Impure;
46 
47   void checkBeginFunction(CheckerContext &C) const;
48   void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
49   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
50 
51 private:
52   void registerCtorDtorCallInState(bool IsBeginFunction,
53                                    CheckerContext &C) const;
54 };
55 } // end namespace
56 
57 // GDM (generic data map) to the memregion of this for the ctor and dtor.
58 REGISTER_MAP_WITH_PROGRAMSTATE(CtorDtorMap, const MemRegion *, ObjectState)
59 
60 // The function to check if a callexpr is a virtual method call.
61 static bool isVirtualCall(const CallExpr *CE) {
62   bool CallIsNonVirtual = false;
63 
64   if (const MemberExpr *CME = dyn_cast<MemberExpr>(CE->getCallee())) {
65     // The member access is fully qualified (i.e., X::F).
66     // Treat this as a non-virtual call and do not warn.
67     if (CME->getQualifier())
68       CallIsNonVirtual = true;
69 
70     if (const Expr *Base = CME->getBase()) {
71       // The most derived class is marked final.
72       if (Base->getBestDynamicClassType()->hasAttr<FinalAttr>())
73         CallIsNonVirtual = true;
74     }
75   }
76 
77   const CXXMethodDecl *MD =
78       dyn_cast_or_null<CXXMethodDecl>(CE->getDirectCallee());
79   if (MD && MD->isVirtual() && !CallIsNonVirtual && !MD->hasAttr<FinalAttr>() &&
80       !MD->getParent()->hasAttr<FinalAttr>())
81     return true;
82   return false;
83 }
84 
85 // The BeginFunction callback when enter a constructor or a destructor.
86 void VirtualCallChecker::checkBeginFunction(CheckerContext &C) const {
87   registerCtorDtorCallInState(true, C);
88 }
89 
90 // The EndFunction callback when leave a constructor or a destructor.
91 void VirtualCallChecker::checkEndFunction(const ReturnStmt *RS,
92                                           CheckerContext &C) const {
93   registerCtorDtorCallInState(false, C);
94 }
95 
96 void VirtualCallChecker::checkPreCall(const CallEvent &Call,
97                                       CheckerContext &C) const {
98   const auto MC = dyn_cast<CXXMemberCall>(&Call);
99   if (!MC)
100     return;
101 
102   const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
103   if (!MD)
104     return;
105 
106   ProgramStateRef State = C.getState();
107   const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
108   if (!isVirtualCall(CE))
109     return;
110 
111   const MemRegion *Reg = MC->getCXXThisVal().getAsRegion();
112   const ObjectState *ObState = State->get<CtorDtorMap>(Reg);
113   if (!ObState)
114     return;
115 
116   bool IsPure = MD->isPure();
117 
118   // At this point we're sure that we're calling a virtual method
119   // during construction or destruction, so we'll emit a report.
120   SmallString<128> Msg;
121   llvm::raw_svector_ostream OS(Msg);
122   OS << "Call to ";
123   if (IsPure)
124     OS << "pure ";
125   OS << "virtual method '" << MD->getParent()->getNameAsString()
126      << "::" << MD->getNameAsString() << "' during ";
127   if (*ObState == ObjectState::CtorCalled)
128     OS << "construction ";
129   else
130     OS << "destruction ";
131   if (IsPure)
132     OS << "has undefined behavior";
133   else
134     OS << "bypasses virtual dispatch";
135 
136   ExplodedNode *N =
137       IsPure ? C.generateErrorNode() : C.generateNonFatalErrorNode();
138   if (!N)
139     return;
140 
141   const std::unique_ptr<BugType> &BT = IsPure ? BT_Pure : BT_Impure;
142   if (!BT) {
143     // The respective check is disabled.
144     return;
145   }
146 
147   auto Report = std::make_unique<BugReport>(*BT, OS.str(), N);
148   C.emitReport(std::move(Report));
149 }
150 
151 void VirtualCallChecker::registerCtorDtorCallInState(bool IsBeginFunction,
152                                                      CheckerContext &C) const {
153   const auto *LCtx = C.getLocationContext();
154   const auto *MD = dyn_cast_or_null<CXXMethodDecl>(LCtx->getDecl());
155   if (!MD)
156     return;
157 
158   ProgramStateRef State = C.getState();
159   auto &SVB = C.getSValBuilder();
160 
161   // Enter a constructor, set the corresponding memregion be true.
162   if (isa<CXXConstructorDecl>(MD)) {
163     auto ThiSVal =
164         State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame()));
165     const MemRegion *Reg = ThiSVal.getAsRegion();
166     if (IsBeginFunction)
167       State = State->set<CtorDtorMap>(Reg, ObjectState::CtorCalled);
168     else
169       State = State->remove<CtorDtorMap>(Reg);
170 
171     C.addTransition(State);
172     return;
173   }
174 
175   // Enter a Destructor, set the corresponding memregion be true.
176   if (isa<CXXDestructorDecl>(MD)) {
177     auto ThiSVal =
178         State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame()));
179     const MemRegion *Reg = ThiSVal.getAsRegion();
180     if (IsBeginFunction)
181       State = State->set<CtorDtorMap>(Reg, ObjectState::DtorCalled);
182     else
183       State = State->remove<CtorDtorMap>(Reg);
184 
185     C.addTransition(State);
186     return;
187   }
188 }
189 
190 void ento::registerVirtualCallModeling(CheckerManager &Mgr) {
191   Mgr.registerChecker<VirtualCallChecker>();
192 }
193 
194 void ento::registerPureVirtualCallChecker(CheckerManager &Mgr) {
195   auto *Chk = Mgr.getChecker<VirtualCallChecker>();
196   Chk->BT_Pure = std::make_unique<BugType>(
197       Mgr.getCurrentCheckName(), "Pure virtual method call",
198       categories::CXXObjectLifecycle);
199 }
200 
201 void ento::registerVirtualCallChecker(CheckerManager &Mgr) {
202   auto *Chk = Mgr.getChecker<VirtualCallChecker>();
203   if (!Mgr.getAnalyzerOptions().getCheckerBooleanOption(
204           Mgr.getCurrentCheckName(), "PureOnly")) {
205     Chk->BT_Impure = std::make_unique<BugType>(
206         Mgr.getCurrentCheckName(), "Unexpected loss of virtual dispatch",
207         categories::CXXObjectLifecycle);
208   }
209 }
210 
211 bool ento::shouldRegisterVirtualCallModeling(const LangOptions &LO) {
212   return LO.CPlusPlus;
213 }
214 
215 bool ento::shouldRegisterPureVirtualCallChecker(const LangOptions &LO) {
216   return LO.CPlusPlus;
217 }
218 
219 bool ento::shouldRegisterVirtualCallChecker(const LangOptions &LO) {
220   return LO.CPlusPlus;
221 }
222