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 #include <optional> 18 19 using namespace clang; 20 using namespace ento; 21 22 namespace { 23 class RefCntblBaseVirtualDtorChecker 24 : public Checker<check::ASTDecl<TranslationUnitDecl>> { 25 private: 26 BugType Bug; 27 mutable BugReporter *BR; 28 29 public: 30 RefCntblBaseVirtualDtorChecker() 31 : Bug(this, 32 "Reference-countable base class doesn't have virtual destructor", 33 "WebKit coding guidelines") {} 34 35 void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR, 36 BugReporter &BRArg) const { 37 BR = &BRArg; 38 39 // The calls to checkAST* from AnalysisConsumer don't 40 // visit template instantiations or lambda classes. We 41 // want to visit those, so we make our own RecursiveASTVisitor. 42 struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> { 43 const RefCntblBaseVirtualDtorChecker *Checker; 44 explicit LocalVisitor(const RefCntblBaseVirtualDtorChecker *Checker) 45 : Checker(Checker) { 46 assert(Checker); 47 } 48 49 bool shouldVisitTemplateInstantiations() const { return true; } 50 bool shouldVisitImplicitCode() const { return false; } 51 52 bool VisitCXXRecordDecl(const CXXRecordDecl *RD) { 53 Checker->visitCXXRecordDecl(RD); 54 return true; 55 } 56 }; 57 58 LocalVisitor visitor(this); 59 visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD)); 60 } 61 62 void visitCXXRecordDecl(const CXXRecordDecl *RD) const { 63 if (shouldSkipDecl(RD)) 64 return; 65 66 CXXBasePaths Paths; 67 Paths.setOrigin(RD); 68 69 const CXXBaseSpecifier *ProblematicBaseSpecifier = nullptr; 70 const CXXRecordDecl *ProblematicBaseClass = nullptr; 71 72 const auto IsPublicBaseRefCntblWOVirtualDtor = 73 [RD, &ProblematicBaseSpecifier, 74 &ProblematicBaseClass](const CXXBaseSpecifier *Base, CXXBasePath &) { 75 const auto AccSpec = Base->getAccessSpecifier(); 76 if (AccSpec == AS_protected || AccSpec == AS_private || 77 (AccSpec == AS_none && RD->isClass())) 78 return false; 79 80 auto hasRefInBase = clang::hasPublicMethodInBase(Base, "ref"); 81 auto hasDerefInBase = clang::hasPublicMethodInBase(Base, "deref"); 82 83 bool hasRef = hasRefInBase && *hasRefInBase != nullptr; 84 bool hasDeref = hasDerefInBase && *hasDerefInBase != nullptr; 85 86 QualType T = Base->getType(); 87 if (T.isNull()) 88 return false; 89 90 const CXXRecordDecl *C = T->getAsCXXRecordDecl(); 91 if (!C) 92 return false; 93 bool AnyInconclusiveBase = false; 94 const auto hasPublicRefInBase = 95 [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, 96 CXXBasePath &) { 97 auto hasRefInBase = clang::hasPublicMethodInBase(Base, "ref"); 98 if (!hasRefInBase) { 99 AnyInconclusiveBase = true; 100 return false; 101 } 102 return (*hasRefInBase) != nullptr; 103 }; 104 const auto hasPublicDerefInBase = [&AnyInconclusiveBase]( 105 const CXXBaseSpecifier *Base, 106 CXXBasePath &) { 107 auto hasDerefInBase = clang::hasPublicMethodInBase(Base, "deref"); 108 if (!hasDerefInBase) { 109 AnyInconclusiveBase = true; 110 return false; 111 } 112 return (*hasDerefInBase) != nullptr; 113 }; 114 CXXBasePaths Paths; 115 Paths.setOrigin(C); 116 hasRef = hasRef || C->lookupInBases(hasPublicRefInBase, Paths, 117 /*LookupInDependent =*/true); 118 hasDeref = hasDeref || C->lookupInBases(hasPublicDerefInBase, Paths, 119 /*LookupInDependent =*/true); 120 if (AnyInconclusiveBase || !hasRef || !hasDeref) 121 return false; 122 123 const auto *Dtor = C->getDestructor(); 124 if (!Dtor || !Dtor->isVirtual()) { 125 ProblematicBaseSpecifier = Base; 126 ProblematicBaseClass = C; 127 return true; 128 } 129 130 return false; 131 }; 132 133 if (RD->lookupInBases(IsPublicBaseRefCntblWOVirtualDtor, Paths, 134 /*LookupInDependent =*/true)) { 135 reportBug(RD, ProblematicBaseSpecifier, ProblematicBaseClass); 136 } 137 } 138 139 bool shouldSkipDecl(const CXXRecordDecl *RD) const { 140 if (!RD->isThisDeclarationADefinition()) 141 return true; 142 143 if (RD->isImplicit()) 144 return true; 145 146 if (RD->isLambda()) 147 return true; 148 149 // If the construct doesn't have a source file, then it's not something 150 // we want to diagnose. 151 const auto RDLocation = RD->getLocation(); 152 if (!RDLocation.isValid()) 153 return true; 154 155 const auto Kind = RD->getTagKind(); 156 if (Kind != TagTypeKind::Struct && Kind != TagTypeKind::Class) 157 return true; 158 159 // Ignore CXXRecords that come from system headers. 160 if (BR->getSourceManager().getFileCharacteristic(RDLocation) != 161 SrcMgr::C_User) 162 return true; 163 164 return false; 165 } 166 167 void reportBug(const CXXRecordDecl *DerivedClass, 168 const CXXBaseSpecifier *BaseSpec, 169 const CXXRecordDecl *ProblematicBaseClass) const { 170 assert(DerivedClass); 171 assert(BaseSpec); 172 assert(ProblematicBaseClass); 173 174 SmallString<100> Buf; 175 llvm::raw_svector_ostream Os(Buf); 176 177 Os << (ProblematicBaseClass->isClass() ? "Class" : "Struct") << " "; 178 printQuotedQualifiedName(Os, ProblematicBaseClass); 179 180 Os << " is used as a base of " 181 << (DerivedClass->isClass() ? "class" : "struct") << " "; 182 printQuotedQualifiedName(Os, DerivedClass); 183 184 Os << " but doesn't have virtual destructor"; 185 186 PathDiagnosticLocation BSLoc(BaseSpec->getSourceRange().getBegin(), 187 BR->getSourceManager()); 188 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc); 189 Report->addRange(BaseSpec->getSourceRange()); 190 BR->emitReport(std::move(Report)); 191 } 192 }; 193 } // namespace 194 195 void ento::registerRefCntblBaseVirtualDtorChecker(CheckerManager &Mgr) { 196 Mgr.registerChecker<RefCntblBaseVirtualDtorChecker>(); 197 } 198 199 bool ento::shouldRegisterRefCntblBaseVirtualDtorChecker( 200 const CheckerManager &mgr) { 201 return true; 202 } 203