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