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: 62 WalkAST(const CheckerBase *checker, BugReporter &br, 63 AnalysisDeclContext *ac) 64 : Checker(checker), BR(br), AC(ac), visitingCallExpr(nullptr) {} 65 66 bool hasWork() const { return !WList.empty(); } 67 68 /// This method adds a CallExpr to the worklist and marks the callee as 69 /// being PreVisited. 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. 82 WorkListUnit Dequeue() { 83 assert(!WList.empty()); 84 return WList.back(); 85 } 86 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); 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 127 void WalkAST::VisitChildren(Stmt *S) { 128 for (Stmt *Child : S->children()) 129 if (Child) 130 Visit(Child); 131 } 132 133 void WalkAST::VisitCallExpr(CallExpr *CE) { 134 VisitChildren(CE); 135 Enqueue(CE); 136 } 137 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 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: 221 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 244 void ento::registerVirtualCallChecker(CheckerManager &mgr) { 245 mgr.registerChecker<VirtualCallChecker>(); 246 } 247