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 "DiagOutputUtils.h" 10 #include "PtrTypesSemantics.h" 11 #include "clang/AST/Decl.h" 12 #include "clang/AST/DeclCXX.h" 13 #include "clang/AST/DynamicRecursiveASTVisitor.h" 14 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 15 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 16 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 17 #include "clang/StaticAnalyzer/Core/Checker.h" 18 #include "llvm/Support/Casting.h" 19 #include <optional> 20 21 using namespace clang; 22 using namespace ento; 23 24 namespace { 25 26 class RawPtrRefMemberChecker 27 : public Checker<check::ASTDecl<TranslationUnitDecl>> { 28 private: 29 BugType Bug; 30 mutable BugReporter *BR; 31 mutable llvm::DenseSet<const ObjCIvarDecl *> IvarDeclsToIgnore; 32 33 protected: 34 mutable std::optional<RetainTypeChecker> RTC; 35 36 public: 37 RawPtrRefMemberChecker(const char *description) 38 : Bug(this, description, "WebKit coding guidelines") {} 39 40 virtual std::optional<bool> isUnsafePtr(QualType, 41 bool ignoreARC = false) const = 0; 42 virtual const char *typeName() const = 0; 43 virtual const char *invariant() const = 0; 44 45 void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR, 46 BugReporter &BRArg) const { 47 BR = &BRArg; 48 49 // The calls to checkAST* from AnalysisConsumer don't 50 // visit template instantiations or lambda classes. We 51 // want to visit those, so we make our own RecursiveASTVisitor. 52 struct LocalVisitor : ConstDynamicRecursiveASTVisitor { 53 const RawPtrRefMemberChecker *Checker; 54 explicit LocalVisitor(const RawPtrRefMemberChecker *Checker) 55 : Checker(Checker) { 56 assert(Checker); 57 ShouldVisitTemplateInstantiations = true; 58 ShouldVisitImplicitCode = false; 59 } 60 61 bool VisitTypedefDecl(const TypedefDecl *TD) override { 62 if (Checker->RTC) 63 Checker->RTC->visitTypedef(TD); 64 return true; 65 } 66 67 bool VisitRecordDecl(const RecordDecl *RD) override { 68 Checker->visitRecordDecl(RD); 69 return true; 70 } 71 72 bool VisitObjCContainerDecl(const ObjCContainerDecl *CD) override { 73 Checker->visitObjCDecl(CD); 74 return true; 75 } 76 }; 77 78 LocalVisitor visitor(this); 79 if (RTC) 80 RTC->visitTranslationUnitDecl(TUD); 81 visitor.TraverseDecl(TUD); 82 } 83 84 void visitRecordDecl(const RecordDecl *RD) const { 85 if (shouldSkipDecl(RD)) 86 return; 87 88 for (auto *Member : RD->fields()) 89 visitMember(Member, RD); 90 } 91 92 void visitMember(const FieldDecl *Member, const RecordDecl *RD) const { 93 auto QT = Member->getType(); 94 const Type *MemberType = QT.getTypePtrOrNull(); 95 96 while (MemberType) { 97 auto IsUnsafePtr = isUnsafePtr(QT); 98 if (IsUnsafePtr && *IsUnsafePtr) 99 break; 100 if (!MemberType->isPointerType()) 101 return; 102 QT = MemberType->getPointeeType(); 103 MemberType = QT.getTypePtrOrNull(); 104 } 105 106 if (!MemberType) 107 return; 108 109 if (auto *MemberCXXRD = MemberType->getPointeeCXXRecordDecl()) 110 reportBug(Member, MemberType, MemberCXXRD, RD); 111 else if (auto *ObjCDecl = getObjCDecl(MemberType)) 112 reportBug(Member, MemberType, ObjCDecl, RD); 113 } 114 115 ObjCInterfaceDecl *getObjCDecl(const Type *TypePtr) const { 116 auto *PointeeType = TypePtr->getPointeeType().getTypePtrOrNull(); 117 if (!PointeeType) 118 return nullptr; 119 auto *Desugared = PointeeType->getUnqualifiedDesugaredType(); 120 if (!Desugared) 121 return nullptr; 122 auto *ObjCType = dyn_cast<ObjCInterfaceType>(Desugared); 123 if (!ObjCType) 124 return nullptr; 125 return ObjCType->getDecl(); 126 } 127 128 void visitObjCDecl(const ObjCContainerDecl *CD) const { 129 if (BR->getSourceManager().isInSystemHeader(CD->getLocation())) 130 return; 131 132 ObjCContainerDecl::PropertyMap map; 133 CD->collectPropertiesToImplement(map); 134 for (auto it : map) 135 visitObjCPropertyDecl(CD, it.second); 136 137 if (auto *ID = dyn_cast<ObjCInterfaceDecl>(CD)) { 138 for (auto *Ivar : ID->ivars()) 139 visitIvarDecl(CD, Ivar); 140 return; 141 } 142 if (auto *ID = dyn_cast<ObjCImplementationDecl>(CD)) { 143 for (auto *PropImpl : ID->property_impls()) 144 visitPropImpl(CD, PropImpl); 145 for (auto *Ivar : ID->ivars()) 146 visitIvarDecl(CD, Ivar); 147 return; 148 } 149 } 150 151 void visitIvarDecl(const ObjCContainerDecl *CD, 152 const ObjCIvarDecl *Ivar) const { 153 if (BR->getSourceManager().isInSystemHeader(Ivar->getLocation())) 154 return; 155 156 if (IvarDeclsToIgnore.contains(Ivar)) 157 return; 158 159 auto QT = Ivar->getType(); 160 const Type *IvarType = QT.getTypePtrOrNull(); 161 if (!IvarType) 162 return; 163 164 auto IsUnsafePtr = isUnsafePtr(QT); 165 if (!IsUnsafePtr || !*IsUnsafePtr) 166 return; 167 168 IvarDeclsToIgnore.insert(Ivar); 169 170 if (auto *MemberCXXRD = IvarType->getPointeeCXXRecordDecl()) 171 reportBug(Ivar, IvarType, MemberCXXRD, CD); 172 else if (auto *ObjCDecl = getObjCDecl(IvarType)) 173 reportBug(Ivar, IvarType, ObjCDecl, CD); 174 } 175 176 void visitObjCPropertyDecl(const ObjCContainerDecl *CD, 177 const ObjCPropertyDecl *PD) const { 178 if (BR->getSourceManager().isInSystemHeader(PD->getLocation())) 179 return; 180 181 if (const ObjCInterfaceDecl *ID = dyn_cast<ObjCInterfaceDecl>(CD)) { 182 if (!RTC || !RTC->defaultSynthProperties() || 183 ID->isObjCRequiresPropertyDefs()) 184 return; 185 } 186 187 auto [IsUnsafe, PropType] = isPropImplUnsafePtr(PD); 188 if (!IsUnsafe) 189 return; 190 191 if (auto *MemberCXXRD = PropType->getPointeeCXXRecordDecl()) 192 reportBug(PD, PropType, MemberCXXRD, CD); 193 else if (auto *ObjCDecl = getObjCDecl(PropType)) 194 reportBug(PD, PropType, ObjCDecl, CD); 195 } 196 197 void visitPropImpl(const ObjCContainerDecl *CD, 198 const ObjCPropertyImplDecl *PID) const { 199 if (BR->getSourceManager().isInSystemHeader(PID->getLocation())) 200 return; 201 202 if (PID->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize) 203 return; 204 205 auto *PropDecl = PID->getPropertyDecl(); 206 if (auto *IvarDecl = PID->getPropertyIvarDecl()) { 207 if (IvarDeclsToIgnore.contains(IvarDecl)) 208 return; 209 IvarDeclsToIgnore.insert(IvarDecl); 210 } 211 auto [IsUnsafe, PropType] = isPropImplUnsafePtr(PropDecl); 212 if (!IsUnsafe) 213 return; 214 215 if (auto *MemberCXXRD = PropType->getPointeeCXXRecordDecl()) 216 reportBug(PropDecl, PropType, MemberCXXRD, CD); 217 else if (auto *ObjCDecl = getObjCDecl(PropType)) 218 reportBug(PropDecl, PropType, ObjCDecl, CD); 219 } 220 221 std::pair<bool, const Type *> 222 isPropImplUnsafePtr(const ObjCPropertyDecl *PD) const { 223 if (!PD) 224 return {false, nullptr}; 225 226 auto QT = PD->getType(); 227 const Type *PropType = QT.getTypePtrOrNull(); 228 if (!PropType) 229 return {false, nullptr}; 230 231 // "assign" property doesn't retain even under ARC so treat it as unsafe. 232 bool ignoreARC = 233 !PD->isReadOnly() && PD->getSetterKind() == ObjCPropertyDecl::Assign; 234 auto IsUnsafePtr = isUnsafePtr(QT, ignoreARC); 235 return {IsUnsafePtr && *IsUnsafePtr, PropType}; 236 } 237 238 bool shouldSkipDecl(const RecordDecl *RD) const { 239 if (!RD->isThisDeclarationADefinition()) 240 return true; 241 242 if (RD->isImplicit()) 243 return true; 244 245 if (RD->isLambda()) 246 return true; 247 248 // If the construct doesn't have a source file, then it's not something 249 // we want to diagnose. 250 const auto RDLocation = RD->getLocation(); 251 if (!RDLocation.isValid()) 252 return true; 253 254 const auto Kind = RD->getTagKind(); 255 if (Kind != TagTypeKind::Struct && Kind != TagTypeKind::Class && 256 Kind != TagTypeKind::Union) 257 return true; 258 259 // Ignore CXXRecords that come from system headers. 260 if (BR->getSourceManager().isInSystemHeader(RDLocation)) 261 return true; 262 263 // Ref-counted smartpointers actually have raw-pointer to uncounted type as 264 // a member but we trust them to handle it correctly. 265 auto CXXRD = llvm::dyn_cast_or_null<CXXRecordDecl>(RD); 266 if (CXXRD && isSmartPtr(CXXRD)) 267 return true; 268 269 return false; 270 } 271 272 template <typename DeclType, typename PointeeType, typename ParentDeclType> 273 void reportBug(const DeclType *Member, const Type *MemberType, 274 const PointeeType *Pointee, 275 const ParentDeclType *ClassCXXRD) const { 276 assert(Member); 277 assert(MemberType); 278 assert(Pointee); 279 280 SmallString<100> Buf; 281 llvm::raw_svector_ostream Os(Buf); 282 283 if (isa<ObjCContainerDecl>(ClassCXXRD)) { 284 if (isa<ObjCPropertyDecl>(Member)) 285 Os << "Property "; 286 else 287 Os << "Instance variable "; 288 } else 289 Os << "Member variable "; 290 printQuotedName(Os, Member); 291 Os << " in "; 292 printQuotedQualifiedName(Os, ClassCXXRD); 293 if (Member->getType().getTypePtrOrNull() == MemberType) 294 Os << " is a "; 295 else 296 Os << " contains a "; 297 if (printPointer(Os, MemberType) == PrintDeclKind::Pointer) { 298 auto Typedef = MemberType->getAs<TypedefType>(); 299 assert(Typedef); 300 printQuotedQualifiedName(Os, Typedef->getDecl()); 301 } else 302 printQuotedQualifiedName(Os, Pointee); 303 Os << "; " << invariant() << "."; 304 305 PathDiagnosticLocation BSLoc(Member->getSourceRange().getBegin(), 306 BR->getSourceManager()); 307 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc); 308 Report->addRange(Member->getSourceRange()); 309 BR->emitReport(std::move(Report)); 310 } 311 312 enum class PrintDeclKind { Pointee, Pointer }; 313 virtual PrintDeclKind printPointer(llvm::raw_svector_ostream &Os, 314 const Type *T) const { 315 T = T->getUnqualifiedDesugaredType(); 316 bool IsPtr = isa<PointerType>(T) || isa<ObjCObjectPointerType>(T); 317 Os << (IsPtr ? "raw pointer" : "reference") << " to " << typeName() << " "; 318 return PrintDeclKind::Pointee; 319 } 320 }; 321 322 class NoUncountedMemberChecker final : public RawPtrRefMemberChecker { 323 public: 324 NoUncountedMemberChecker() 325 : RawPtrRefMemberChecker("Member variable is a raw-pointer/reference to " 326 "reference-countable type") {} 327 328 std::optional<bool> isUnsafePtr(QualType QT, bool) const final { 329 return isUncountedPtr(QT.getCanonicalType()); 330 } 331 332 const char *typeName() const final { return "ref-countable type"; } 333 334 const char *invariant() const final { 335 return "member variables must be Ref, RefPtr, WeakRef, or WeakPtr"; 336 } 337 }; 338 339 class NoUncheckedPtrMemberChecker final : public RawPtrRefMemberChecker { 340 public: 341 NoUncheckedPtrMemberChecker() 342 : RawPtrRefMemberChecker("Member variable is a raw-pointer/reference to " 343 "checked-pointer capable type") {} 344 345 std::optional<bool> isUnsafePtr(QualType QT, bool) const final { 346 return isUncheckedPtr(QT.getCanonicalType()); 347 } 348 349 const char *typeName() const final { return "CheckedPtr capable type"; } 350 351 const char *invariant() const final { 352 return "member variables must be a CheckedPtr, CheckedRef, WeakRef, or " 353 "WeakPtr"; 354 } 355 }; 356 357 class NoUnretainedMemberChecker final : public RawPtrRefMemberChecker { 358 public: 359 NoUnretainedMemberChecker() 360 : RawPtrRefMemberChecker("Member variable is a raw-pointer/reference to " 361 "retainable type") { 362 RTC = RetainTypeChecker(); 363 } 364 365 std::optional<bool> isUnsafePtr(QualType QT, bool ignoreARC) const final { 366 return RTC->isUnretained(QT, ignoreARC); 367 } 368 369 const char *typeName() const final { return "retainable type"; } 370 371 const char *invariant() const final { 372 return "member variables must be a RetainPtr"; 373 } 374 375 PrintDeclKind printPointer(llvm::raw_svector_ostream &Os, 376 const Type *T) const final { 377 if (!isa<ObjCObjectPointerType>(T) && T->getAs<TypedefType>()) { 378 Os << typeName() << " "; 379 return PrintDeclKind::Pointer; 380 } 381 return RawPtrRefMemberChecker::printPointer(Os, T); 382 } 383 }; 384 385 } // namespace 386 387 void ento::registerNoUncountedMemberChecker(CheckerManager &Mgr) { 388 Mgr.registerChecker<NoUncountedMemberChecker>(); 389 } 390 391 bool ento::shouldRegisterNoUncountedMemberChecker(const CheckerManager &Mgr) { 392 return true; 393 } 394 395 void ento::registerNoUncheckedPtrMemberChecker(CheckerManager &Mgr) { 396 Mgr.registerChecker<NoUncheckedPtrMemberChecker>(); 397 } 398 399 bool ento::shouldRegisterNoUncheckedPtrMemberChecker( 400 const CheckerManager &Mgr) { 401 return true; 402 } 403 404 void ento::registerNoUnretainedMemberChecker(CheckerManager &Mgr) { 405 Mgr.registerChecker<NoUnretainedMemberChecker>(); 406 } 407 408 bool ento::shouldRegisterNoUnretainedMemberChecker(const CheckerManager &Mgr) { 409 return true; 410 } 411