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