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:
RefCntblBaseVirtualDtorChecker()30 RefCntblBaseVirtualDtorChecker()
31 : Bug(this,
32 "Reference-countable base class doesn't have virtual destructor",
33 "WebKit coding guidelines") {}
34
checkASTDecl(const TranslationUnitDecl * TUD,AnalysisManager & MGR,BugReporter & BRArg) const35 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
visitCXXRecordDecl(const CXXRecordDecl * RD) const62 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 std::optional<const CXXRecordDecl*> RefCntblBaseRD = isRefCountable(Base);
81 if (!RefCntblBaseRD || !(*RefCntblBaseRD))
82 return false;
83
84 const auto *Dtor = (*RefCntblBaseRD)->getDestructor();
85 if (!Dtor || !Dtor->isVirtual()) {
86 ProblematicBaseSpecifier = Base;
87 ProblematicBaseClass = *RefCntblBaseRD;
88 return true;
89 }
90
91 return false;
92 };
93
94 if (RD->lookupInBases(IsPublicBaseRefCntblWOVirtualDtor, Paths,
95 /*LookupInDependent =*/true)) {
96 reportBug(RD, ProblematicBaseSpecifier, ProblematicBaseClass);
97 }
98 }
99
shouldSkipDecl(const CXXRecordDecl * RD) const100 bool shouldSkipDecl(const CXXRecordDecl *RD) const {
101 if (!RD->isThisDeclarationADefinition())
102 return true;
103
104 if (RD->isImplicit())
105 return true;
106
107 if (RD->isLambda())
108 return true;
109
110 // If the construct doesn't have a source file, then it's not something
111 // we want to diagnose.
112 const auto RDLocation = RD->getLocation();
113 if (!RDLocation.isValid())
114 return true;
115
116 const auto Kind = RD->getTagKind();
117 if (Kind != TTK_Struct && Kind != TTK_Class)
118 return true;
119
120 // Ignore CXXRecords that come from system headers.
121 if (BR->getSourceManager().getFileCharacteristic(RDLocation) !=
122 SrcMgr::C_User)
123 return true;
124
125 return false;
126 }
127
reportBug(const CXXRecordDecl * DerivedClass,const CXXBaseSpecifier * BaseSpec,const CXXRecordDecl * ProblematicBaseClass) const128 void reportBug(const CXXRecordDecl *DerivedClass,
129 const CXXBaseSpecifier *BaseSpec,
130 const CXXRecordDecl *ProblematicBaseClass) const {
131 assert(DerivedClass);
132 assert(BaseSpec);
133 assert(ProblematicBaseClass);
134
135 SmallString<100> Buf;
136 llvm::raw_svector_ostream Os(Buf);
137
138 Os << (ProblematicBaseClass->isClass() ? "Class" : "Struct") << " ";
139 printQuotedQualifiedName(Os, ProblematicBaseClass);
140
141 Os << " is used as a base of "
142 << (DerivedClass->isClass() ? "class" : "struct") << " ";
143 printQuotedQualifiedName(Os, DerivedClass);
144
145 Os << " but doesn't have virtual destructor";
146
147 PathDiagnosticLocation BSLoc(BaseSpec->getSourceRange().getBegin(),
148 BR->getSourceManager());
149 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
150 Report->addRange(BaseSpec->getSourceRange());
151 BR->emitReport(std::move(Report));
152 }
153 };
154 } // namespace
155
registerRefCntblBaseVirtualDtorChecker(CheckerManager & Mgr)156 void ento::registerRefCntblBaseVirtualDtorChecker(CheckerManager &Mgr) {
157 Mgr.registerChecker<RefCntblBaseVirtualDtorChecker>();
158 }
159
shouldRegisterRefCntblBaseVirtualDtorChecker(const CheckerManager & mgr)160 bool ento::shouldRegisterRefCntblBaseVirtualDtorChecker(
161 const CheckerManager &mgr) {
162 return true;
163 }
164