xref: /aosp_15_r20/external/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp (revision 67e74705e28f6214e480b399dd47ea732279e315)
1*67e74705SXin Li //=======- VirtualCallChecker.cpp --------------------------------*- C++ -*-==//
2*67e74705SXin Li //
3*67e74705SXin Li //                     The LLVM Compiler Infrastructure
4*67e74705SXin Li //
5*67e74705SXin Li // This file is distributed under the University of Illinois Open Source
6*67e74705SXin Li // License. See LICENSE.TXT for details.
7*67e74705SXin Li //
8*67e74705SXin Li //===----------------------------------------------------------------------===//
9*67e74705SXin Li //
10*67e74705SXin Li //  This file defines a checker that checks virtual function calls during
11*67e74705SXin Li //  construction or destruction of C++ objects.
12*67e74705SXin Li //
13*67e74705SXin Li //===----------------------------------------------------------------------===//
14*67e74705SXin Li 
15*67e74705SXin Li #include "ClangSACheckers.h"
16*67e74705SXin Li #include "clang/AST/DeclCXX.h"
17*67e74705SXin Li #include "clang/AST/StmtVisitor.h"
18*67e74705SXin Li #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
19*67e74705SXin Li #include "clang/StaticAnalyzer/Core/Checker.h"
20*67e74705SXin Li #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
21*67e74705SXin Li #include "llvm/ADT/SmallString.h"
22*67e74705SXin Li #include "llvm/Support/SaveAndRestore.h"
23*67e74705SXin Li #include "llvm/Support/raw_ostream.h"
24*67e74705SXin Li 
25*67e74705SXin Li using namespace clang;
26*67e74705SXin Li using namespace ento;
27*67e74705SXin Li 
28*67e74705SXin Li namespace {
29*67e74705SXin Li 
30*67e74705SXin Li class WalkAST : public StmtVisitor<WalkAST> {
31*67e74705SXin Li   const CheckerBase *Checker;
32*67e74705SXin Li   BugReporter &BR;
33*67e74705SXin Li   AnalysisDeclContext *AC;
34*67e74705SXin Li 
35*67e74705SXin Li   typedef const CallExpr * WorkListUnit;
36*67e74705SXin Li   typedef SmallVector<WorkListUnit, 20> DFSWorkList;
37*67e74705SXin Li 
38*67e74705SXin Li   /// A vector representing the worklist which has a chain of CallExprs.
39*67e74705SXin Li   DFSWorkList WList;
40*67e74705SXin Li 
41*67e74705SXin Li   // PreVisited : A CallExpr to this FunctionDecl is in the worklist, but the
42*67e74705SXin Li   // body has not been visited yet.
43*67e74705SXin Li   // PostVisited : A CallExpr to this FunctionDecl is in the worklist, and the
44*67e74705SXin Li   // body has been visited.
45*67e74705SXin Li   enum Kind { NotVisited,
46*67e74705SXin Li               PreVisited,  /**< A CallExpr to this FunctionDecl is in the
47*67e74705SXin Li                                 worklist, but the body has not yet been
48*67e74705SXin Li                                 visited. */
49*67e74705SXin Li               PostVisited  /**< A CallExpr to this FunctionDecl is in the
50*67e74705SXin Li                                 worklist, and the body has been visited. */
51*67e74705SXin Li   };
52*67e74705SXin Li 
53*67e74705SXin Li   /// A DenseMap that records visited states of FunctionDecls.
54*67e74705SXin Li   llvm::DenseMap<const FunctionDecl *, Kind> VisitedFunctions;
55*67e74705SXin Li 
56*67e74705SXin Li   /// The CallExpr whose body is currently being visited.  This is used for
57*67e74705SXin Li   /// generating bug reports.  This is null while visiting the body of a
58*67e74705SXin Li   /// constructor or destructor.
59*67e74705SXin Li   const CallExpr *visitingCallExpr;
60*67e74705SXin Li 
61*67e74705SXin Li public:
WalkAST(const CheckerBase * checker,BugReporter & br,AnalysisDeclContext * ac)62*67e74705SXin Li   WalkAST(const CheckerBase *checker, BugReporter &br,
63*67e74705SXin Li           AnalysisDeclContext *ac)
64*67e74705SXin Li       : Checker(checker), BR(br), AC(ac), visitingCallExpr(nullptr) {}
65*67e74705SXin Li 
hasWork() const66*67e74705SXin Li   bool hasWork() const { return !WList.empty(); }
67*67e74705SXin Li 
68*67e74705SXin Li   /// This method adds a CallExpr to the worklist and marks the callee as
69*67e74705SXin Li   /// being PreVisited.
Enqueue(WorkListUnit WLUnit)70*67e74705SXin Li   void Enqueue(WorkListUnit WLUnit) {
71*67e74705SXin Li     const FunctionDecl *FD = WLUnit->getDirectCallee();
72*67e74705SXin Li     if (!FD || !FD->getBody())
73*67e74705SXin Li       return;
74*67e74705SXin Li     Kind &K = VisitedFunctions[FD];
75*67e74705SXin Li     if (K != NotVisited)
76*67e74705SXin Li       return;
77*67e74705SXin Li     K = PreVisited;
78*67e74705SXin Li     WList.push_back(WLUnit);
79*67e74705SXin Li   }
80*67e74705SXin Li 
81*67e74705SXin Li   /// This method returns an item from the worklist without removing it.
Dequeue()82*67e74705SXin Li   WorkListUnit Dequeue() {
83*67e74705SXin Li     assert(!WList.empty());
84*67e74705SXin Li     return WList.back();
85*67e74705SXin Li   }
86*67e74705SXin Li 
Execute()87*67e74705SXin Li   void Execute() {
88*67e74705SXin Li     while (hasWork()) {
89*67e74705SXin Li       WorkListUnit WLUnit = Dequeue();
90*67e74705SXin Li       const FunctionDecl *FD = WLUnit->getDirectCallee();
91*67e74705SXin Li       assert(FD && FD->getBody());
92*67e74705SXin Li 
93*67e74705SXin Li       if (VisitedFunctions[FD] == PreVisited) {
94*67e74705SXin Li         // If the callee is PreVisited, walk its body.
95*67e74705SXin Li         // Visit the body.
96*67e74705SXin Li         SaveAndRestore<const CallExpr *> SaveCall(visitingCallExpr, WLUnit);
97*67e74705SXin Li         Visit(FD->getBody());
98*67e74705SXin Li 
99*67e74705SXin Li         // Mark the function as being PostVisited to indicate we have
100*67e74705SXin Li         // scanned the body.
101*67e74705SXin Li         VisitedFunctions[FD] = PostVisited;
102*67e74705SXin Li         continue;
103*67e74705SXin Li       }
104*67e74705SXin Li 
105*67e74705SXin Li       // Otherwise, the callee is PostVisited.
106*67e74705SXin Li       // Remove it from the worklist.
107*67e74705SXin Li       assert(VisitedFunctions[FD] == PostVisited);
108*67e74705SXin Li       WList.pop_back();
109*67e74705SXin Li     }
110*67e74705SXin Li   }
111*67e74705SXin Li 
112*67e74705SXin Li   // Stmt visitor methods.
113*67e74705SXin Li   void VisitCallExpr(CallExpr *CE);
114*67e74705SXin Li   void VisitCXXMemberCallExpr(CallExpr *CE);
VisitStmt(Stmt * S)115*67e74705SXin Li   void VisitStmt(Stmt *S) { VisitChildren(S); }
116*67e74705SXin Li   void VisitChildren(Stmt *S);
117*67e74705SXin Li 
118*67e74705SXin Li   void ReportVirtualCall(const CallExpr *CE, bool isPure);
119*67e74705SXin Li 
120*67e74705SXin Li };
121*67e74705SXin Li } // end anonymous namespace
122*67e74705SXin Li 
123*67e74705SXin Li //===----------------------------------------------------------------------===//
124*67e74705SXin Li // AST walking.
125*67e74705SXin Li //===----------------------------------------------------------------------===//
126*67e74705SXin Li 
VisitChildren(Stmt * S)127*67e74705SXin Li void WalkAST::VisitChildren(Stmt *S) {
128*67e74705SXin Li   for (Stmt *Child : S->children())
129*67e74705SXin Li     if (Child)
130*67e74705SXin Li       Visit(Child);
131*67e74705SXin Li }
132*67e74705SXin Li 
VisitCallExpr(CallExpr * CE)133*67e74705SXin Li void WalkAST::VisitCallExpr(CallExpr *CE) {
134*67e74705SXin Li   VisitChildren(CE);
135*67e74705SXin Li   Enqueue(CE);
136*67e74705SXin Li }
137*67e74705SXin Li 
VisitCXXMemberCallExpr(CallExpr * CE)138*67e74705SXin Li void WalkAST::VisitCXXMemberCallExpr(CallExpr *CE) {
139*67e74705SXin Li   VisitChildren(CE);
140*67e74705SXin Li   bool callIsNonVirtual = false;
141*67e74705SXin Li 
142*67e74705SXin Li   // Several situations to elide for checking.
143*67e74705SXin Li   if (MemberExpr *CME = dyn_cast<MemberExpr>(CE->getCallee())) {
144*67e74705SXin Li     // If the member access is fully qualified (i.e., X::F), then treat
145*67e74705SXin Li     // this as a non-virtual call and do not warn.
146*67e74705SXin Li     if (CME->getQualifier())
147*67e74705SXin Li       callIsNonVirtual = true;
148*67e74705SXin Li 
149*67e74705SXin Li     if (Expr *base = CME->getBase()->IgnoreImpCasts()) {
150*67e74705SXin Li       // Elide analyzing the call entirely if the base pointer is not 'this'.
151*67e74705SXin Li       if (!isa<CXXThisExpr>(base))
152*67e74705SXin Li         return;
153*67e74705SXin Li 
154*67e74705SXin Li       // If the most derived class is marked final, we know that now subclass
155*67e74705SXin Li       // can override this member.
156*67e74705SXin Li       if (base->getBestDynamicClassType()->hasAttr<FinalAttr>())
157*67e74705SXin Li         callIsNonVirtual = true;
158*67e74705SXin Li     }
159*67e74705SXin Li   }
160*67e74705SXin Li 
161*67e74705SXin Li   // Get the callee.
162*67e74705SXin Li   const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(CE->getDirectCallee());
163*67e74705SXin Li   if (MD && MD->isVirtual() && !callIsNonVirtual && !MD->hasAttr<FinalAttr>() &&
164*67e74705SXin Li       !MD->getParent()->hasAttr<FinalAttr>())
165*67e74705SXin Li     ReportVirtualCall(CE, MD->isPure());
166*67e74705SXin Li 
167*67e74705SXin Li   Enqueue(CE);
168*67e74705SXin Li }
169*67e74705SXin Li 
ReportVirtualCall(const CallExpr * CE,bool isPure)170*67e74705SXin Li void WalkAST::ReportVirtualCall(const CallExpr *CE, bool isPure) {
171*67e74705SXin Li   SmallString<100> buf;
172*67e74705SXin Li   llvm::raw_svector_ostream os(buf);
173*67e74705SXin Li 
174*67e74705SXin Li   os << "Call Path : ";
175*67e74705SXin Li   // Name of current visiting CallExpr.
176*67e74705SXin Li   os << *CE->getDirectCallee();
177*67e74705SXin Li 
178*67e74705SXin Li   // Name of the CallExpr whose body is current walking.
179*67e74705SXin Li   if (visitingCallExpr)
180*67e74705SXin Li     os << " <-- " << *visitingCallExpr->getDirectCallee();
181*67e74705SXin Li   // Names of FunctionDecls in worklist with state PostVisited.
182*67e74705SXin Li   for (SmallVectorImpl<const CallExpr *>::iterator I = WList.end(),
183*67e74705SXin Li          E = WList.begin(); I != E; --I) {
184*67e74705SXin Li     const FunctionDecl *FD = (*(I-1))->getDirectCallee();
185*67e74705SXin Li     assert(FD);
186*67e74705SXin Li     if (VisitedFunctions[FD] == PostVisited)
187*67e74705SXin Li       os << " <-- " << *FD;
188*67e74705SXin Li   }
189*67e74705SXin Li 
190*67e74705SXin Li   PathDiagnosticLocation CELoc =
191*67e74705SXin Li     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
192*67e74705SXin Li   SourceRange R = CE->getCallee()->getSourceRange();
193*67e74705SXin Li 
194*67e74705SXin Li   if (isPure) {
195*67e74705SXin Li     os << "\n" <<  "Call pure virtual functions during construction or "
196*67e74705SXin Li        << "destruction may leads undefined behaviour";
197*67e74705SXin Li     BR.EmitBasicReport(AC->getDecl(), Checker,
198*67e74705SXin Li                        "Call pure virtual function during construction or "
199*67e74705SXin Li                        "Destruction",
200*67e74705SXin Li                        "Cplusplus", os.str(), CELoc, R);
201*67e74705SXin Li     return;
202*67e74705SXin Li   }
203*67e74705SXin Li   else {
204*67e74705SXin Li     os << "\n" << "Call virtual functions during construction or "
205*67e74705SXin Li        << "destruction will never go to a more derived class";
206*67e74705SXin Li     BR.EmitBasicReport(AC->getDecl(), Checker,
207*67e74705SXin Li                        "Call virtual function during construction or "
208*67e74705SXin Li                        "Destruction",
209*67e74705SXin Li                        "Cplusplus", os.str(), CELoc, R);
210*67e74705SXin Li     return;
211*67e74705SXin Li   }
212*67e74705SXin Li }
213*67e74705SXin Li 
214*67e74705SXin Li //===----------------------------------------------------------------------===//
215*67e74705SXin Li // VirtualCallChecker
216*67e74705SXin Li //===----------------------------------------------------------------------===//
217*67e74705SXin Li 
218*67e74705SXin Li namespace {
219*67e74705SXin Li class VirtualCallChecker : public Checker<check::ASTDecl<CXXRecordDecl> > {
220*67e74705SXin Li public:
checkASTDecl(const CXXRecordDecl * RD,AnalysisManager & mgr,BugReporter & BR) const221*67e74705SXin Li   void checkASTDecl(const CXXRecordDecl *RD, AnalysisManager& mgr,
222*67e74705SXin Li                     BugReporter &BR) const {
223*67e74705SXin Li     WalkAST walker(this, BR, mgr.getAnalysisDeclContext(RD));
224*67e74705SXin Li 
225*67e74705SXin Li     // Check the constructors.
226*67e74705SXin Li     for (const auto *I : RD->ctors()) {
227*67e74705SXin Li       if (!I->isCopyOrMoveConstructor())
228*67e74705SXin Li         if (Stmt *Body = I->getBody()) {
229*67e74705SXin Li           walker.Visit(Body);
230*67e74705SXin Li           walker.Execute();
231*67e74705SXin Li         }
232*67e74705SXin Li     }
233*67e74705SXin Li 
234*67e74705SXin Li     // Check the destructor.
235*67e74705SXin Li     if (CXXDestructorDecl *DD = RD->getDestructor())
236*67e74705SXin Li       if (Stmt *Body = DD->getBody()) {
237*67e74705SXin Li         walker.Visit(Body);
238*67e74705SXin Li         walker.Execute();
239*67e74705SXin Li       }
240*67e74705SXin Li   }
241*67e74705SXin Li };
242*67e74705SXin Li }
243*67e74705SXin Li 
registerVirtualCallChecker(CheckerManager & mgr)244*67e74705SXin Li void ento::registerVirtualCallChecker(CheckerManager &mgr) {
245*67e74705SXin Li   mgr.registerChecker<VirtualCallChecker>();
246*67e74705SXin Li }
247