1 //=======- RefCntblBaseVirtualDtor.cpp ---------------------------*- C++ -*-==// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "DiagOutputUtils.h" 10 #include "PtrTypesSemantics.h" 11 #include "clang/AST/CXXInheritance.h" 12 #include "clang/AST/RecursiveASTVisitor.h" 13 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 14 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 15 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 16 #include "clang/StaticAnalyzer/Core/Checker.h" 17 18 using namespace clang; 19 using namespace ento; 20 21 namespace { 22 class RefCntblBaseVirtualDtorChecker 23 : public Checker<check::ASTDecl<TranslationUnitDecl>> { 24 private: 25 BugType Bug; 26 mutable BugReporter *BR; 27 28 public: 29 RefCntblBaseVirtualDtorChecker() 30 : Bug(this, 31 "Reference-countable base class doesn't have virtual destructor", 32 "WebKit coding guidelines") {} 33 34 void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR, 35 BugReporter &BRArg) const { 36 BR = &BRArg; 37 38 // The calls to checkAST* from AnalysisConsumer don't 39 // visit template instantiations or lambda classes. We 40 // want to visit those, so we make our own RecursiveASTVisitor. 41 struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> { 42 const RefCntblBaseVirtualDtorChecker *Checker; 43 explicit LocalVisitor(const RefCntblBaseVirtualDtorChecker *Checker) 44 : Checker(Checker) { 45 assert(Checker); 46 } 47 48 bool shouldVisitTemplateInstantiations() const { return true; } 49 bool shouldVisitImplicitCode() const { return false; } 50 51 bool VisitCXXRecordDecl(const CXXRecordDecl *RD) { 52 Checker->visitCXXRecordDecl(RD); 53 return true; 54 } 55 }; 56 57 LocalVisitor visitor(this); 58 visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD)); 59 } 60 61 void visitCXXRecordDecl(const CXXRecordDecl *RD) const { 62 if (shouldSkipDecl(RD)) 63 return; 64 65 CXXBasePaths Paths; 66 Paths.setOrigin(RD); 67 68 const CXXBaseSpecifier *ProblematicBaseSpecifier = nullptr; 69 const CXXRecordDecl *ProblematicBaseClass = nullptr; 70 71 const auto IsPublicBaseRefCntblWOVirtualDtor = 72 [RD, &ProblematicBaseSpecifier, 73 &ProblematicBaseClass](const CXXBaseSpecifier *Base, CXXBasePath &) { 74 const auto AccSpec = Base->getAccessSpecifier(); 75 if (AccSpec == AS_protected || AccSpec == AS_private || 76 (AccSpec == AS_none && RD->isClass())) 77 return false; 78 79 llvm::Optional<const clang::CXXRecordDecl *> MaybeRefCntblBaseRD = 80 isRefCountable(Base); 81 if (!MaybeRefCntblBaseRD.hasValue()) 82 return false; 83 84 const CXXRecordDecl *RefCntblBaseRD = MaybeRefCntblBaseRD.getValue(); 85 if (!RefCntblBaseRD) 86 return false; 87 88 const auto *Dtor = RefCntblBaseRD->getDestructor(); 89 if (!Dtor || !Dtor->isVirtual()) { 90 ProblematicBaseSpecifier = Base; 91 ProblematicBaseClass = RefCntblBaseRD; 92 return true; 93 } 94 95 return false; 96 }; 97 98 if (RD->lookupInBases(IsPublicBaseRefCntblWOVirtualDtor, Paths, 99 /*LookupInDependent =*/true)) { 100 reportBug(RD, ProblematicBaseSpecifier, ProblematicBaseClass); 101 } 102 } 103 104 bool shouldSkipDecl(const CXXRecordDecl *RD) const { 105 if (!RD->isThisDeclarationADefinition()) 106 return true; 107 108 if (RD->isImplicit()) 109 return true; 110 111 if (RD->isLambda()) 112 return true; 113 114 // If the construct doesn't have a source file, then it's not something 115 // we want to diagnose. 116 const auto RDLocation = RD->getLocation(); 117 if (!RDLocation.isValid()) 118 return true; 119 120 const auto Kind = RD->getTagKind(); 121 if (Kind != TTK_Struct && Kind != TTK_Class) 122 return true; 123 124 // Ignore CXXRecords that come from system headers. 125 if (BR->getSourceManager().getFileCharacteristic(RDLocation) != 126 SrcMgr::C_User) 127 return true; 128 129 return false; 130 } 131 132 void reportBug(const CXXRecordDecl *DerivedClass, 133 const CXXBaseSpecifier *BaseSpec, 134 const CXXRecordDecl *ProblematicBaseClass) const { 135 assert(DerivedClass); 136 assert(BaseSpec); 137 assert(ProblematicBaseClass); 138 139 SmallString<100> Buf; 140 llvm::raw_svector_ostream Os(Buf); 141 142 Os << (ProblematicBaseClass->isClass() ? "Class" : "Struct") << " "; 143 printQuotedQualifiedName(Os, ProblematicBaseClass); 144 145 Os << " is used as a base of " 146 << (DerivedClass->isClass() ? "class" : "struct") << " "; 147 printQuotedQualifiedName(Os, DerivedClass); 148 149 Os << " but doesn't have virtual destructor"; 150 151 PathDiagnosticLocation BSLoc(BaseSpec->getSourceRange().getBegin(), 152 BR->getSourceManager()); 153 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc); 154 Report->addRange(BaseSpec->getSourceRange()); 155 BR->emitReport(std::move(Report)); 156 } 157 }; 158 } // namespace 159 160 void ento::registerRefCntblBaseVirtualDtorChecker(CheckerManager &Mgr) { 161 Mgr.registerChecker<RefCntblBaseVirtualDtorChecker>(); 162 } 163 164 bool ento::shouldRegisterRefCntblBaseVirtualDtorChecker( 165 const CheckerManager &mgr) { 166 return true; 167 } 168