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