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