xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefMemberChecker.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
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:
RawPtrRefMemberChecker(const char * description)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 
checkASTDecl(const TranslationUnitDecl * TUD,AnalysisManager & MGR,BugReporter & BRArg) const45   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 
visitRecordDecl(const RecordDecl * RD) const84   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 
visitMember(const FieldDecl * Member,const RecordDecl * RD) const92   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 
getObjCDecl(const Type * TypePtr) const115   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 
visitObjCDecl(const ObjCContainerDecl * CD) const128   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 
visitIvarDecl(const ObjCContainerDecl * CD,const ObjCIvarDecl * Ivar) const151   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 
visitObjCPropertyDecl(const ObjCContainerDecl * CD,const ObjCPropertyDecl * PD) const176   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 
visitPropImpl(const ObjCContainerDecl * CD,const ObjCPropertyImplDecl * PID) const197   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 *>
isPropImplUnsafePtr(const ObjCPropertyDecl * PD) const222   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 
shouldSkipDecl(const RecordDecl * RD) const238   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>
reportBug(const DeclType * Member,const Type * MemberType,const PointeeType * Pointee,const ParentDeclType * ClassCXXRD) const273   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 };
printPointer(llvm::raw_svector_ostream & Os,const Type * T) const313   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:
NoUncountedMemberChecker()324   NoUncountedMemberChecker()
325       : RawPtrRefMemberChecker("Member variable is a raw-pointer/reference to "
326                                "reference-countable type") {}
327 
isUnsafePtr(QualType QT,bool) const328   std::optional<bool> isUnsafePtr(QualType QT, bool) const final {
329     return isUncountedPtr(QT.getCanonicalType());
330   }
331 
typeName() const332   const char *typeName() const final { return "ref-countable type"; }
333 
invariant() const334   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:
NoUncheckedPtrMemberChecker()341   NoUncheckedPtrMemberChecker()
342       : RawPtrRefMemberChecker("Member variable is a raw-pointer/reference to "
343                                "checked-pointer capable type") {}
344 
isUnsafePtr(QualType QT,bool) const345   std::optional<bool> isUnsafePtr(QualType QT, bool) const final {
346     return isUncheckedPtr(QT.getCanonicalType());
347   }
348 
typeName() const349   const char *typeName() const final { return "CheckedPtr capable type"; }
350 
invariant() const351   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:
NoUnretainedMemberChecker()359   NoUnretainedMemberChecker()
360       : RawPtrRefMemberChecker("Member variable is a raw-pointer/reference to "
361                                "retainable type") {
362     RTC = RetainTypeChecker();
363   }
364 
isUnsafePtr(QualType QT,bool ignoreARC) const365   std::optional<bool> isUnsafePtr(QualType QT, bool ignoreARC) const final {
366     return RTC->isUnretained(QT, ignoreARC);
367   }
368 
typeName() const369   const char *typeName() const final { return "retainable type"; }
370 
invariant() const371   const char *invariant() const final {
372     return "member variables must be a RetainPtr";
373   }
374 
printPointer(llvm::raw_svector_ostream & Os,const Type * T) const375   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 
registerNoUncountedMemberChecker(CheckerManager & Mgr)387 void ento::registerNoUncountedMemberChecker(CheckerManager &Mgr) {
388   Mgr.registerChecker<NoUncountedMemberChecker>();
389 }
390 
shouldRegisterNoUncountedMemberChecker(const CheckerManager & Mgr)391 bool ento::shouldRegisterNoUncountedMemberChecker(const CheckerManager &Mgr) {
392   return true;
393 }
394 
registerNoUncheckedPtrMemberChecker(CheckerManager & Mgr)395 void ento::registerNoUncheckedPtrMemberChecker(CheckerManager &Mgr) {
396   Mgr.registerChecker<NoUncheckedPtrMemberChecker>();
397 }
398 
shouldRegisterNoUncheckedPtrMemberChecker(const CheckerManager & Mgr)399 bool ento::shouldRegisterNoUncheckedPtrMemberChecker(
400     const CheckerManager &Mgr) {
401   return true;
402 }
403 
registerNoUnretainedMemberChecker(CheckerManager & Mgr)404 void ento::registerNoUnretainedMemberChecker(CheckerManager &Mgr) {
405   Mgr.registerChecker<NoUnretainedMemberChecker>();
406 }
407 
shouldRegisterNoUnretainedMemberChecker(const CheckerManager & Mgr)408 bool ento::shouldRegisterNoUnretainedMemberChecker(const CheckerManager &Mgr) {
409   return true;
410 }
411