1 //=======- RawPtrRefMemberChecker.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 "ASTUtils.h" 10 #include "DiagOutputUtils.h" 11 #include "PtrTypesSemantics.h" 12 #include "clang/AST/CXXInheritance.h" 13 #include "clang/AST/Decl.h" 14 #include "clang/AST/DeclCXX.h" 15 #include "clang/AST/DynamicRecursiveASTVisitor.h" 16 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 17 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 18 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 19 #include "clang/StaticAnalyzer/Core/Checker.h" 20 #include "llvm/Support/Casting.h" 21 #include <optional> 22 23 using namespace clang; 24 using namespace ento; 25 26 namespace { 27 28 class RawPtrRefMemberChecker 29 : public Checker<check::ASTDecl<TranslationUnitDecl>> { 30 private: 31 BugType Bug; 32 mutable BugReporter *BR; 33 34 public: 35 RawPtrRefMemberChecker(const char *description) 36 : Bug(this, description, "WebKit coding guidelines") {} 37 38 virtual std::optional<bool> 39 isPtrCompatible(const clang::CXXRecordDecl *) const = 0; 40 virtual bool isPtrCls(const clang::CXXRecordDecl *) const = 0; 41 virtual const char *typeName() const = 0; 42 virtual const char *invariant() const = 0; 43 44 void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR, 45 BugReporter &BRArg) const { 46 BR = &BRArg; 47 48 // The calls to checkAST* from AnalysisConsumer don't 49 // visit template instantiations or lambda classes. We 50 // want to visit those, so we make our own RecursiveASTVisitor. 51 struct LocalVisitor : ConstDynamicRecursiveASTVisitor { 52 const RawPtrRefMemberChecker *Checker; 53 explicit LocalVisitor(const RawPtrRefMemberChecker *Checker) 54 : Checker(Checker) { 55 assert(Checker); 56 ShouldVisitTemplateInstantiations = true; 57 ShouldVisitImplicitCode = false; 58 } 59 60 bool VisitRecordDecl(const RecordDecl *RD) override { 61 Checker->visitRecordDecl(RD); 62 return true; 63 } 64 }; 65 66 LocalVisitor visitor(this); 67 visitor.TraverseDecl(TUD); 68 } 69 70 void visitRecordDecl(const RecordDecl *RD) const { 71 if (shouldSkipDecl(RD)) 72 return; 73 74 for (auto *Member : RD->fields()) { 75 const Type *MemberType = Member->getType().getTypePtrOrNull(); 76 if (!MemberType) 77 continue; 78 79 if (auto *MemberCXXRD = MemberType->getPointeeCXXRecordDecl()) { 80 // If we don't see the definition we just don't know. 81 if (MemberCXXRD->hasDefinition()) { 82 std::optional<bool> isRCAble = isPtrCompatible(MemberCXXRD); 83 if (isRCAble && *isRCAble) 84 reportBug(Member, MemberType, MemberCXXRD, RD); 85 } 86 } 87 } 88 } 89 90 bool shouldSkipDecl(const RecordDecl *RD) const { 91 if (!RD->isThisDeclarationADefinition()) 92 return true; 93 94 if (RD->isImplicit()) 95 return true; 96 97 if (RD->isLambda()) 98 return true; 99 100 // If the construct doesn't have a source file, then it's not something 101 // we want to diagnose. 102 const auto RDLocation = RD->getLocation(); 103 if (!RDLocation.isValid()) 104 return true; 105 106 const auto Kind = RD->getTagKind(); 107 // FIMXE: Should we check union members too? 108 if (Kind != TagTypeKind::Struct && Kind != TagTypeKind::Class) 109 return true; 110 111 // Ignore CXXRecords that come from system headers. 112 if (BR->getSourceManager().isInSystemHeader(RDLocation)) 113 return true; 114 115 // Ref-counted smartpointers actually have raw-pointer to uncounted type as 116 // a member but we trust them to handle it correctly. 117 auto CXXRD = llvm::dyn_cast_or_null<CXXRecordDecl>(RD); 118 if (CXXRD) 119 return isPtrCls(CXXRD); 120 121 return false; 122 } 123 124 void reportBug(const FieldDecl *Member, const Type *MemberType, 125 const CXXRecordDecl *MemberCXXRD, 126 const RecordDecl *ClassCXXRD) const { 127 assert(Member); 128 assert(MemberType); 129 assert(MemberCXXRD); 130 131 SmallString<100> Buf; 132 llvm::raw_svector_ostream Os(Buf); 133 134 Os << "Member variable "; 135 printQuotedName(Os, Member); 136 Os << " in "; 137 printQuotedQualifiedName(Os, ClassCXXRD); 138 Os << " is a " 139 << (isa<PointerType>(MemberType) ? "raw pointer" : "reference") << " to " 140 << typeName() << " "; 141 printQuotedQualifiedName(Os, MemberCXXRD); 142 Os << "; " << invariant() << "."; 143 144 PathDiagnosticLocation BSLoc(Member->getSourceRange().getBegin(), 145 BR->getSourceManager()); 146 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc); 147 Report->addRange(Member->getSourceRange()); 148 BR->emitReport(std::move(Report)); 149 } 150 }; 151 152 class NoUncountedMemberChecker final : public RawPtrRefMemberChecker { 153 public: 154 NoUncountedMemberChecker() 155 : RawPtrRefMemberChecker("Member variable is a raw-pointer/reference to " 156 "reference-countable type") {} 157 158 std::optional<bool> 159 isPtrCompatible(const clang::CXXRecordDecl *R) const final { 160 return isRefCountable(R); 161 } 162 163 bool isPtrCls(const clang::CXXRecordDecl *R) const final { 164 return isRefCounted(R); 165 } 166 167 const char *typeName() const final { return "ref-countable type"; } 168 169 const char *invariant() const final { 170 return "member variables must be Ref, RefPtr, WeakRef, or WeakPtr"; 171 } 172 }; 173 174 class NoUncheckedPtrMemberChecker final : public RawPtrRefMemberChecker { 175 public: 176 NoUncheckedPtrMemberChecker() 177 : RawPtrRefMemberChecker("Member variable is a raw-pointer/reference to " 178 "checked-pointer capable type") {} 179 180 std::optional<bool> 181 isPtrCompatible(const clang::CXXRecordDecl *R) const final { 182 return isCheckedPtrCapable(R); 183 } 184 185 bool isPtrCls(const clang::CXXRecordDecl *R) const final { 186 return isCheckedPtr(R); 187 } 188 189 const char *typeName() const final { return "CheckedPtr capable type"; } 190 191 const char *invariant() const final { 192 return "member variables must be a CheckedPtr, CheckedRef, WeakRef, or " 193 "WeakPtr"; 194 } 195 }; 196 197 } // namespace 198 199 void ento::registerNoUncountedMemberChecker(CheckerManager &Mgr) { 200 Mgr.registerChecker<NoUncountedMemberChecker>(); 201 } 202 203 bool ento::shouldRegisterNoUncountedMemberChecker(const CheckerManager &Mgr) { 204 return true; 205 } 206 207 void ento::registerNoUncheckedPtrMemberChecker(CheckerManager &Mgr) { 208 Mgr.registerChecker<NoUncheckedPtrMemberChecker>(); 209 } 210 211 bool ento::shouldRegisterNoUncheckedPtrMemberChecker( 212 const CheckerManager &Mgr) { 213 return true; 214 } 215