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 /// The root constructor or destructor whose callees are being analyzed. 36 const CXXMethodDecl *RootMethod = nullptr; 37 38 /// Whether the checker should walk into bodies of called functions. 39 /// Controlled by the "Interprocedural" analyzer-config option. 40 bool IsInterprocedural = false; 41 42 /// Whether the checker should only warn for calls to pure virtual functions 43 /// (which is undefined behavior) or for all virtual functions (which may 44 /// may result in unexpected behavior). 45 bool ReportPureOnly = false; 46 47 typedef const CallExpr * WorkListUnit; 48 typedef SmallVector<WorkListUnit, 20> DFSWorkList; 49 50 /// A vector representing the worklist which has a chain of CallExprs. 51 DFSWorkList WList; 52 53 // PreVisited : A CallExpr to this FunctionDecl is in the worklist, but the 54 // body has not been visited yet. 55 // PostVisited : A CallExpr to this FunctionDecl is in the worklist, and the 56 // body has been visited. 57 enum Kind { NotVisited, 58 PreVisited, /**< A CallExpr to this FunctionDecl is in the 59 worklist, but the body has not yet been 60 visited. */ 61 PostVisited /**< A CallExpr to this FunctionDecl is in the 62 worklist, and the body has been visited. */ 63 }; 64 65 /// A DenseMap that records visited states of FunctionDecls. 66 llvm::DenseMap<const FunctionDecl *, Kind> VisitedFunctions; 67 68 /// The CallExpr whose body is currently being visited. This is used for 69 /// generating bug reports. This is null while visiting the body of a 70 /// constructor or destructor. 71 const CallExpr *visitingCallExpr; 72 73 public: 74 WalkAST(const CheckerBase *checker, BugReporter &br, AnalysisDeclContext *ac, 75 const CXXMethodDecl *rootMethod, bool isInterprocedural, 76 bool reportPureOnly) 77 : Checker(checker), BR(br), AC(ac), RootMethod(rootMethod), 78 IsInterprocedural(isInterprocedural), ReportPureOnly(reportPureOnly), 79 visitingCallExpr(nullptr) { 80 // Walking should always start from either a constructor or a destructor. 81 assert(isa<CXXConstructorDecl>(rootMethod) || 82 isa<CXXDestructorDecl>(rootMethod)); 83 } 84 85 bool hasWork() const { return !WList.empty(); } 86 87 /// This method adds a CallExpr to the worklist and marks the callee as 88 /// being PreVisited. 89 void Enqueue(WorkListUnit WLUnit) { 90 const FunctionDecl *FD = WLUnit->getDirectCallee(); 91 if (!FD || !FD->getBody()) 92 return; 93 Kind &K = VisitedFunctions[FD]; 94 if (K != NotVisited) 95 return; 96 K = PreVisited; 97 WList.push_back(WLUnit); 98 } 99 100 /// This method returns an item from the worklist without removing it. 101 WorkListUnit Dequeue() { 102 assert(!WList.empty()); 103 return WList.back(); 104 } 105 106 void Execute() { 107 while (hasWork()) { 108 WorkListUnit WLUnit = Dequeue(); 109 const FunctionDecl *FD = WLUnit->getDirectCallee(); 110 assert(FD && FD->getBody()); 111 112 if (VisitedFunctions[FD] == PreVisited) { 113 // If the callee is PreVisited, walk its body. 114 // Visit the body. 115 SaveAndRestore<const CallExpr *> SaveCall(visitingCallExpr, WLUnit); 116 Visit(FD->getBody()); 117 118 // Mark the function as being PostVisited to indicate we have 119 // scanned the body. 120 VisitedFunctions[FD] = PostVisited; 121 continue; 122 } 123 124 // Otherwise, the callee is PostVisited. 125 // Remove it from the worklist. 126 assert(VisitedFunctions[FD] == PostVisited); 127 WList.pop_back(); 128 } 129 } 130 131 // Stmt visitor methods. 132 void VisitCallExpr(CallExpr *CE); 133 void VisitCXXMemberCallExpr(CallExpr *CE); 134 void VisitStmt(Stmt *S) { VisitChildren(S); } 135 void VisitChildren(Stmt *S); 136 137 void ReportVirtualCall(const CallExpr *CE, bool isPure); 138 139 }; 140 } // end anonymous namespace 141 142 //===----------------------------------------------------------------------===// 143 // AST walking. 144 //===----------------------------------------------------------------------===// 145 146 void WalkAST::VisitChildren(Stmt *S) { 147 for (Stmt *Child : S->children()) 148 if (Child) 149 Visit(Child); 150 } 151 152 void WalkAST::VisitCallExpr(CallExpr *CE) { 153 VisitChildren(CE); 154 if (IsInterprocedural) 155 Enqueue(CE); 156 } 157 158 void WalkAST::VisitCXXMemberCallExpr(CallExpr *CE) { 159 VisitChildren(CE); 160 bool callIsNonVirtual = false; 161 162 // Several situations to elide for checking. 163 if (MemberExpr *CME = dyn_cast<MemberExpr>(CE->getCallee())) { 164 // If the member access is fully qualified (i.e., X::F), then treat 165 // this as a non-virtual call and do not warn. 166 if (CME->getQualifier()) 167 callIsNonVirtual = true; 168 169 if (Expr *base = CME->getBase()->IgnoreImpCasts()) { 170 // Elide analyzing the call entirely if the base pointer is not 'this'. 171 if (!isa<CXXThisExpr>(base)) 172 return; 173 174 // If the most derived class is marked final, we know that now subclass 175 // can override this member. 176 if (base->getBestDynamicClassType()->hasAttr<FinalAttr>()) 177 callIsNonVirtual = true; 178 } 179 } 180 181 // Get the callee. 182 const CXXMethodDecl *MD = 183 dyn_cast_or_null<CXXMethodDecl>(CE->getDirectCallee()); 184 if (MD && MD->isVirtual() && !callIsNonVirtual && !MD->hasAttr<FinalAttr>() && 185 !MD->getParent()->hasAttr<FinalAttr>()) 186 ReportVirtualCall(CE, MD->isPure()); 187 188 if (IsInterprocedural) 189 Enqueue(CE); 190 } 191 192 void WalkAST::ReportVirtualCall(const CallExpr *CE, bool isPure) { 193 if (ReportPureOnly && !isPure) 194 return; 195 196 SmallString<100> buf; 197 llvm::raw_svector_ostream os(buf); 198 199 // FIXME: The interprocedural diagnostic experience here is not good. 200 // Ultimately this checker should be re-written to be path sensitive. 201 // For now, only diagnose intraprocedurally, by default. 202 if (IsInterprocedural) { 203 os << "Call Path : "; 204 // Name of current visiting CallExpr. 205 os << *CE->getDirectCallee(); 206 207 // Name of the CallExpr whose body is current being walked. 208 if (visitingCallExpr) 209 os << " <-- " << *visitingCallExpr->getDirectCallee(); 210 // Names of FunctionDecls in worklist with state PostVisited. 211 for (SmallVectorImpl<const CallExpr *>::iterator I = WList.end(), 212 E = WList.begin(); I != E; --I) { 213 const FunctionDecl *FD = (*(I-1))->getDirectCallee(); 214 assert(FD); 215 if (VisitedFunctions[FD] == PostVisited) 216 os << " <-- " << *FD; 217 } 218 219 os << "\n"; 220 } 221 222 PathDiagnosticLocation CELoc = 223 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); 224 SourceRange R = CE->getCallee()->getSourceRange(); 225 226 os << "Call to "; 227 if (isPure) 228 os << "pure "; 229 230 os << "virtual function during "; 231 232 if (isa<CXXConstructorDecl>(RootMethod)) 233 os << "construction "; 234 else 235 os << "destruction "; 236 237 if (isPure) 238 os << "has undefined behavior"; 239 else 240 os << "will not dispatch to derived class"; 241 242 BR.EmitBasicReport(AC->getDecl(), Checker, 243 "Call to virtual function during construction or " 244 "destruction", 245 "C++ Object Lifecycle", os.str(), CELoc, R); 246 } 247 248 //===----------------------------------------------------------------------===// 249 // VirtualCallChecker 250 //===----------------------------------------------------------------------===// 251 252 namespace { 253 class VirtualCallChecker : public Checker<check::ASTDecl<CXXRecordDecl> > { 254 public: 255 DefaultBool isInterprocedural; 256 DefaultBool isPureOnly; 257 258 void checkASTDecl(const CXXRecordDecl *RD, AnalysisManager& mgr, 259 BugReporter &BR) const { 260 AnalysisDeclContext *ADC = mgr.getAnalysisDeclContext(RD); 261 262 // Check the constructors. 263 for (const auto *I : RD->ctors()) { 264 if (!I->isCopyOrMoveConstructor()) 265 if (Stmt *Body = I->getBody()) { 266 WalkAST walker(this, BR, ADC, I, isInterprocedural, isPureOnly); 267 walker.Visit(Body); 268 walker.Execute(); 269 } 270 } 271 272 // Check the destructor. 273 if (CXXDestructorDecl *DD = RD->getDestructor()) 274 if (Stmt *Body = DD->getBody()) { 275 WalkAST walker(this, BR, ADC, DD, isInterprocedural, isPureOnly); 276 walker.Visit(Body); 277 walker.Execute(); 278 } 279 } 280 }; 281 } 282 283 void ento::registerVirtualCallChecker(CheckerManager &mgr) { 284 VirtualCallChecker *checker = mgr.registerChecker<VirtualCallChecker>(); 285 checker->isInterprocedural = 286 mgr.getAnalyzerOptions().getBooleanOption("Interprocedural", false, 287 checker); 288 289 checker->isPureOnly = 290 mgr.getAnalyzerOptions().getBooleanOption("PureOnly", false, 291 checker); 292 } 293