• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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   const CheckerBase *Checker;
32   BugReporter &BR;
33   AnalysisDeclContext *AC;
34 
35   typedef const CallExpr * WorkListUnit;
36   typedef SmallVector<WorkListUnit, 20> DFSWorkList;
37 
38   /// A vector representing the worklist which has a chain of CallExprs.
39   DFSWorkList WList;
40 
41   // PreVisited : A CallExpr to this FunctionDecl is in the worklist, but the
42   // body has not been visited yet.
43   // PostVisited : A CallExpr to this FunctionDecl is in the worklist, and the
44   // body has been visited.
45   enum Kind { NotVisited,
46               PreVisited,  /**< A CallExpr to this FunctionDecl is in the
47                                 worklist, but the body has not yet been
48                                 visited. */
49               PostVisited  /**< A CallExpr to this FunctionDecl is in the
50                                 worklist, and the body has been visited. */
51   };
52 
53   /// A DenseMap that records visited states of FunctionDecls.
54   llvm::DenseMap<const FunctionDecl *, Kind> VisitedFunctions;
55 
56   /// The CallExpr whose body is currently being visited.  This is used for
57   /// generating bug reports.  This is null while visiting the body of a
58   /// constructor or destructor.
59   const CallExpr *visitingCallExpr;
60 
61 public:
WalkAST(const CheckerBase * checker,BugReporter & br,AnalysisDeclContext * ac)62   WalkAST(const CheckerBase *checker, BugReporter &br,
63           AnalysisDeclContext *ac)
64       : Checker(checker), BR(br), AC(ac), visitingCallExpr(nullptr) {}
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 : S->children())
129     if (Child)
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     if (Expr *base = CME->getBase()->IgnoreImpCasts()) {
150       // Elide analyzing the call entirely if the base pointer is not 'this'.
151       if (!isa<CXXThisExpr>(base))
152         return;
153 
154       // If the most derived class is marked final, we know that now subclass
155       // can override this member.
156       if (base->getBestDynamicClassType()->hasAttr<FinalAttr>())
157         callIsNonVirtual = true;
158     }
159   }
160 
161   // Get the callee.
162   const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(CE->getDirectCallee());
163   if (MD && MD->isVirtual() && !callIsNonVirtual && !MD->hasAttr<FinalAttr>() &&
164       !MD->getParent()->hasAttr<FinalAttr>())
165     ReportVirtualCall(CE, MD->isPure());
166 
167   Enqueue(CE);
168 }
169 
ReportVirtualCall(const CallExpr * CE,bool isPure)170 void WalkAST::ReportVirtualCall(const CallExpr *CE, bool isPure) {
171   SmallString<100> buf;
172   llvm::raw_svector_ostream os(buf);
173 
174   os << "Call Path : ";
175   // Name of current visiting CallExpr.
176   os << *CE->getDirectCallee();
177 
178   // Name of the CallExpr whose body is current walking.
179   if (visitingCallExpr)
180     os << " <-- " << *visitingCallExpr->getDirectCallee();
181   // Names of FunctionDecls in worklist with state PostVisited.
182   for (SmallVectorImpl<const CallExpr *>::iterator I = WList.end(),
183          E = WList.begin(); I != E; --I) {
184     const FunctionDecl *FD = (*(I-1))->getDirectCallee();
185     assert(FD);
186     if (VisitedFunctions[FD] == PostVisited)
187       os << " <-- " << *FD;
188   }
189 
190   PathDiagnosticLocation CELoc =
191     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
192   SourceRange R = CE->getCallee()->getSourceRange();
193 
194   if (isPure) {
195     os << "\n" <<  "Call pure virtual functions during construction or "
196        << "destruction may leads undefined behaviour";
197     BR.EmitBasicReport(AC->getDecl(), Checker,
198                        "Call pure virtual function during construction or "
199                        "Destruction",
200                        "Cplusplus", os.str(), CELoc, R);
201     return;
202   }
203   else {
204     os << "\n" << "Call virtual functions during construction or "
205        << "destruction will never go to a more derived class";
206     BR.EmitBasicReport(AC->getDecl(), Checker,
207                        "Call virtual function during construction or "
208                        "Destruction",
209                        "Cplusplus", os.str(), CELoc, R);
210     return;
211   }
212 }
213 
214 //===----------------------------------------------------------------------===//
215 // VirtualCallChecker
216 //===----------------------------------------------------------------------===//
217 
218 namespace {
219 class VirtualCallChecker : public Checker<check::ASTDecl<CXXRecordDecl> > {
220 public:
checkASTDecl(const CXXRecordDecl * RD,AnalysisManager & mgr,BugReporter & BR) const221   void checkASTDecl(const CXXRecordDecl *RD, AnalysisManager& mgr,
222                     BugReporter &BR) const {
223     WalkAST walker(this, BR, mgr.getAnalysisDeclContext(RD));
224 
225     // Check the constructors.
226     for (const auto *I : RD->ctors()) {
227       if (!I->isCopyOrMoveConstructor())
228         if (Stmt *Body = I->getBody()) {
229           walker.Visit(Body);
230           walker.Execute();
231         }
232     }
233 
234     // Check the destructor.
235     if (CXXDestructorDecl *DD = RD->getDestructor())
236       if (Stmt *Body = DD->getBody()) {
237         walker.Visit(Body);
238         walker.Execute();
239       }
240   }
241 };
242 }
243 
registerVirtualCallChecker(CheckerManager & mgr)244 void ento::registerVirtualCallChecker(CheckerManager &mgr) {
245   mgr.registerChecker<VirtualCallChecker>();
246 }
247