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