xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp (revision 2b3d49b610bd2a45884115edcb21110bfa325f51)
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 function 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   mutable std::unique_ptr<BugType> BT;
44 
45 public:
46   // The flag to determine if pure virtual functions should be issued only.
47   DefaultBool IsPureOnly;
48 
49   void checkBeginFunction(CheckerContext &C) const;
50   void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
51   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
52 
53 private:
54   void registerCtorDtorCallInState(bool IsBeginFunction,
55                                    CheckerContext &C) const;
56   void reportBug(StringRef Msg, bool PureError, const MemRegion *Reg,
57                  CheckerContext &C) const;
58 
59   class VirtualBugVisitor : public BugReporterVisitor {
60   private:
61     const MemRegion *ObjectRegion;
62     bool Found;
63 
64   public:
65     VirtualBugVisitor(const MemRegion *R) : ObjectRegion(R), Found(false) {}
66 
67     void Profile(llvm::FoldingSetNodeID &ID) const override {
68       static int X = 0;
69       ID.AddPointer(&X);
70       ID.AddPointer(ObjectRegion);
71     }
72 
73     PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
74                                      BugReporterContext &BRC,
75                                      BugReport &BR) override;
76   };
77 };
78 } // end namespace
79 
80 // GDM (generic data map) to the memregion of this for the ctor and dtor.
81 REGISTER_MAP_WITH_PROGRAMSTATE(CtorDtorMap, const MemRegion *, ObjectState)
82 
83 PathDiagnosticPieceRef VirtualCallChecker::VirtualBugVisitor::VisitNode(
84     const ExplodedNode *N, BugReporterContext &BRC, BugReport &) {
85   // We need the last ctor/dtor which call the virtual function.
86   // The visitor walks the ExplodedGraph backwards.
87   if (Found)
88     return nullptr;
89 
90   ProgramStateRef State = N->getState();
91   const LocationContext *LCtx = N->getLocationContext();
92   const CXXConstructorDecl *CD =
93       dyn_cast_or_null<CXXConstructorDecl>(LCtx->getDecl());
94   const CXXDestructorDecl *DD =
95       dyn_cast_or_null<CXXDestructorDecl>(LCtx->getDecl());
96 
97   if (!CD && !DD)
98     return nullptr;
99 
100   ProgramStateManager &PSM = State->getStateManager();
101   auto &SVB = PSM.getSValBuilder();
102   const auto *MD = dyn_cast<CXXMethodDecl>(LCtx->getDecl());
103   if (!MD)
104     return nullptr;
105   auto ThiSVal =
106       State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame()));
107   const MemRegion *Reg = ThiSVal.castAs<loc::MemRegionVal>().getRegion();
108   if (!Reg)
109     return nullptr;
110   if (Reg != ObjectRegion)
111     return nullptr;
112 
113   const Stmt *S = PathDiagnosticLocation::getStmt(N);
114   if (!S)
115     return nullptr;
116   Found = true;
117 
118   std::string InfoText;
119   if (CD)
120     InfoText = "This constructor of an object of type '" +
121                CD->getNameAsString() +
122                "' has not returned when the virtual method was called";
123   else
124     InfoText = "This destructor of an object of type '" +
125                DD->getNameAsString() +
126                "' has not returned when the virtual method was called";
127 
128   // Generate the extra diagnostic.
129   PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
130                              N->getLocationContext());
131   return std::make_shared<PathDiagnosticEventPiece>(Pos, InfoText, true);
132 }
133 
134 // The function to check if a callexpr is a virtual function.
135 static bool isVirtualCall(const CallExpr *CE) {
136   bool CallIsNonVirtual = false;
137 
138   if (const MemberExpr *CME = dyn_cast<MemberExpr>(CE->getCallee())) {
139     // The member access is fully qualified (i.e., X::F).
140     // Treat this as a non-virtual call and do not warn.
141     if (CME->getQualifier())
142       CallIsNonVirtual = true;
143 
144     if (const Expr *Base = CME->getBase()) {
145       // The most derived class is marked final.
146       if (Base->getBestDynamicClassType()->hasAttr<FinalAttr>())
147         CallIsNonVirtual = true;
148     }
149   }
150 
151   const CXXMethodDecl *MD =
152       dyn_cast_or_null<CXXMethodDecl>(CE->getDirectCallee());
153   if (MD && MD->isVirtual() && !CallIsNonVirtual && !MD->hasAttr<FinalAttr>() &&
154       !MD->getParent()->hasAttr<FinalAttr>())
155     return true;
156   return false;
157 }
158 
159 // The BeginFunction callback when enter a constructor or a destructor.
160 void VirtualCallChecker::checkBeginFunction(CheckerContext &C) const {
161   registerCtorDtorCallInState(true, C);
162 }
163 
164 // The EndFunction callback when leave a constructor or a destructor.
165 void VirtualCallChecker::checkEndFunction(const ReturnStmt *RS,
166                                           CheckerContext &C) const {
167   registerCtorDtorCallInState(false, C);
168 }
169 
170 void VirtualCallChecker::checkPreCall(const CallEvent &Call,
171                                       CheckerContext &C) const {
172   const auto MC = dyn_cast<CXXMemberCall>(&Call);
173   if (!MC)
174     return;
175 
176   const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
177   if (!MD)
178     return;
179   ProgramStateRef State = C.getState();
180   const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
181 
182   if (IsPureOnly && !MD->isPure())
183     return;
184   if (!isVirtualCall(CE))
185     return;
186 
187   const MemRegion *Reg = MC->getCXXThisVal().getAsRegion();
188   const ObjectState *ObState = State->get<CtorDtorMap>(Reg);
189   if (!ObState)
190     return;
191   // Check if a virtual method is called.
192   // The GDM of constructor and destructor should be true.
193   if (*ObState == ObjectState::CtorCalled) {
194     if (IsPureOnly && MD->isPure())
195       reportBug("Call to pure virtual function during construction", true, Reg,
196                 C);
197     else if (!MD->isPure())
198       reportBug("Call to virtual function during construction", false, Reg, C);
199     else
200       reportBug("Call to pure virtual function during construction", false, Reg,
201                 C);
202   }
203 
204   if (*ObState == ObjectState::DtorCalled) {
205     if (IsPureOnly && MD->isPure())
206       reportBug("Call to pure virtual function during destruction", true, Reg,
207                 C);
208     else if (!MD->isPure())
209       reportBug("Call to virtual function during destruction", false, Reg, C);
210     else
211       reportBug("Call to pure virtual function during construction", false, Reg,
212                 C);
213   }
214 }
215 
216 void VirtualCallChecker::registerCtorDtorCallInState(bool IsBeginFunction,
217                                                      CheckerContext &C) const {
218   const auto *LCtx = C.getLocationContext();
219   const auto *MD = dyn_cast_or_null<CXXMethodDecl>(LCtx->getDecl());
220   if (!MD)
221     return;
222 
223   ProgramStateRef State = C.getState();
224   auto &SVB = C.getSValBuilder();
225 
226   // Enter a constructor, set the corresponding memregion be true.
227   if (isa<CXXConstructorDecl>(MD)) {
228     auto ThiSVal =
229         State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame()));
230     const MemRegion *Reg = ThiSVal.getAsRegion();
231     if (IsBeginFunction)
232       State = State->set<CtorDtorMap>(Reg, ObjectState::CtorCalled);
233     else
234       State = State->remove<CtorDtorMap>(Reg);
235 
236     C.addTransition(State);
237     return;
238   }
239 
240   // Enter a Destructor, set the corresponding memregion be true.
241   if (isa<CXXDestructorDecl>(MD)) {
242     auto ThiSVal =
243         State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame()));
244     const MemRegion *Reg = ThiSVal.getAsRegion();
245     if (IsBeginFunction)
246       State = State->set<CtorDtorMap>(Reg, ObjectState::DtorCalled);
247     else
248       State = State->remove<CtorDtorMap>(Reg);
249 
250     C.addTransition(State);
251     return;
252   }
253 }
254 
255 void VirtualCallChecker::reportBug(StringRef Msg, bool IsSink,
256                                    const MemRegion *Reg,
257                                    CheckerContext &C) const {
258   ExplodedNode *N;
259   if (IsSink)
260     N = C.generateErrorNode();
261   else
262     N = C.generateNonFatalErrorNode();
263 
264   if (!N)
265     return;
266   if (!BT)
267     BT.reset(new BugType(
268         this, "Call to virtual function during construction or destruction",
269         "C++ Object Lifecycle"));
270 
271   auto Reporter = std::make_unique<BugReport>(*BT, Msg, N);
272   Reporter->addVisitor(std::make_unique<VirtualBugVisitor>(Reg));
273   C.emitReport(std::move(Reporter));
274 }
275 
276 void ento::registerVirtualCallChecker(CheckerManager &mgr) {
277   VirtualCallChecker *checker = mgr.registerChecker<VirtualCallChecker>();
278 
279   checker->IsPureOnly =
280       mgr.getAnalyzerOptions().getCheckerBooleanOption(checker, "PureOnly");
281 }
282 
283 bool ento::shouldRegisterVirtualCallChecker(const LangOptions &LO) {
284   return true;
285 }
286