xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
1*700637cbSDimitry Andric //=======- UncountedLocalVarsChecker.cpp -------------------------*- C++ -*-==//
2*700637cbSDimitry Andric //
3*700637cbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*700637cbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*700637cbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*700637cbSDimitry Andric //
7*700637cbSDimitry Andric //===----------------------------------------------------------------------===//
8*700637cbSDimitry Andric 
9*700637cbSDimitry Andric #include "ASTUtils.h"
10*700637cbSDimitry Andric #include "DiagOutputUtils.h"
11*700637cbSDimitry Andric #include "PtrTypesSemantics.h"
12*700637cbSDimitry Andric #include "clang/AST/CXXInheritance.h"
13*700637cbSDimitry Andric #include "clang/AST/Decl.h"
14*700637cbSDimitry Andric #include "clang/AST/DeclCXX.h"
15*700637cbSDimitry Andric #include "clang/AST/DynamicRecursiveASTVisitor.h"
16*700637cbSDimitry Andric #include "clang/AST/ParentMapContext.h"
17*700637cbSDimitry Andric #include "clang/Analysis/DomainSpecific/CocoaConventions.h"
18*700637cbSDimitry Andric #include "clang/Basic/SourceLocation.h"
19*700637cbSDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
20*700637cbSDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
21*700637cbSDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
22*700637cbSDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
23*700637cbSDimitry Andric #include <optional>
24*700637cbSDimitry Andric 
25*700637cbSDimitry Andric using namespace clang;
26*700637cbSDimitry Andric using namespace ento;
27*700637cbSDimitry Andric 
28*700637cbSDimitry Andric namespace {
29*700637cbSDimitry Andric 
30*700637cbSDimitry Andric // FIXME: should be defined by anotations in the future
isRefcountedStringsHack(const VarDecl * V)31*700637cbSDimitry Andric bool isRefcountedStringsHack(const VarDecl *V) {
32*700637cbSDimitry Andric   assert(V);
33*700637cbSDimitry Andric   auto safeClass = [](const std::string &className) {
34*700637cbSDimitry Andric     return className == "String" || className == "AtomString" ||
35*700637cbSDimitry Andric            className == "UniquedString" || className == "Identifier";
36*700637cbSDimitry Andric   };
37*700637cbSDimitry Andric   QualType QT = V->getType();
38*700637cbSDimitry Andric   auto *T = QT.getTypePtr();
39*700637cbSDimitry Andric   if (auto *CXXRD = T->getAsCXXRecordDecl()) {
40*700637cbSDimitry Andric     if (safeClass(safeGetName(CXXRD)))
41*700637cbSDimitry Andric       return true;
42*700637cbSDimitry Andric   }
43*700637cbSDimitry Andric   if (T->isPointerType() || T->isReferenceType()) {
44*700637cbSDimitry Andric     if (auto *CXXRD = T->getPointeeCXXRecordDecl()) {
45*700637cbSDimitry Andric       if (safeClass(safeGetName(CXXRD)))
46*700637cbSDimitry Andric         return true;
47*700637cbSDimitry Andric     }
48*700637cbSDimitry Andric   }
49*700637cbSDimitry Andric   return false;
50*700637cbSDimitry Andric }
51*700637cbSDimitry Andric 
52*700637cbSDimitry Andric struct GuardianVisitor : DynamicRecursiveASTVisitor {
53*700637cbSDimitry Andric   const VarDecl *Guardian{nullptr};
54*700637cbSDimitry Andric 
GuardianVisitor__anon4bbed29c0111::GuardianVisitor55*700637cbSDimitry Andric   explicit GuardianVisitor(const VarDecl *Guardian) : Guardian(Guardian) {
56*700637cbSDimitry Andric     assert(Guardian);
57*700637cbSDimitry Andric   }
58*700637cbSDimitry Andric 
VisitBinaryOperator__anon4bbed29c0111::GuardianVisitor59*700637cbSDimitry Andric   bool VisitBinaryOperator(BinaryOperator *BO) override {
60*700637cbSDimitry Andric     if (BO->isAssignmentOp()) {
61*700637cbSDimitry Andric       if (auto *VarRef = dyn_cast<DeclRefExpr>(BO->getLHS())) {
62*700637cbSDimitry Andric         if (VarRef->getDecl() == Guardian)
63*700637cbSDimitry Andric           return false;
64*700637cbSDimitry Andric       }
65*700637cbSDimitry Andric     }
66*700637cbSDimitry Andric     return true;
67*700637cbSDimitry Andric   }
68*700637cbSDimitry Andric 
VisitCXXConstructExpr__anon4bbed29c0111::GuardianVisitor69*700637cbSDimitry Andric   bool VisitCXXConstructExpr(CXXConstructExpr *CE) override {
70*700637cbSDimitry Andric     if (auto *Ctor = CE->getConstructor()) {
71*700637cbSDimitry Andric       if (Ctor->isMoveConstructor() && CE->getNumArgs() == 1) {
72*700637cbSDimitry Andric         auto *Arg = CE->getArg(0)->IgnoreParenCasts();
73*700637cbSDimitry Andric         if (auto *VarRef = dyn_cast<DeclRefExpr>(Arg)) {
74*700637cbSDimitry Andric           if (VarRef->getDecl() == Guardian)
75*700637cbSDimitry Andric             return false;
76*700637cbSDimitry Andric         }
77*700637cbSDimitry Andric       }
78*700637cbSDimitry Andric     }
79*700637cbSDimitry Andric     return true;
80*700637cbSDimitry Andric   }
81*700637cbSDimitry Andric 
VisitCXXMemberCallExpr__anon4bbed29c0111::GuardianVisitor82*700637cbSDimitry Andric   bool VisitCXXMemberCallExpr(CXXMemberCallExpr *MCE) override {
83*700637cbSDimitry Andric     auto MethodName = safeGetName(MCE->getMethodDecl());
84*700637cbSDimitry Andric     if (MethodName == "swap" || MethodName == "leakRef" ||
85*700637cbSDimitry Andric         MethodName == "releaseNonNull" || MethodName == "clear") {
86*700637cbSDimitry Andric       auto *ThisArg = MCE->getImplicitObjectArgument()->IgnoreParenCasts();
87*700637cbSDimitry Andric       if (auto *VarRef = dyn_cast<DeclRefExpr>(ThisArg)) {
88*700637cbSDimitry Andric         if (VarRef->getDecl() == Guardian)
89*700637cbSDimitry Andric           return false;
90*700637cbSDimitry Andric       }
91*700637cbSDimitry Andric     }
92*700637cbSDimitry Andric     return true;
93*700637cbSDimitry Andric   }
94*700637cbSDimitry Andric 
VisitCXXOperatorCallExpr__anon4bbed29c0111::GuardianVisitor95*700637cbSDimitry Andric   bool VisitCXXOperatorCallExpr(CXXOperatorCallExpr *OCE) override {
96*700637cbSDimitry Andric     if (OCE->isAssignmentOp()) {
97*700637cbSDimitry Andric       assert(OCE->getNumArgs() == 2);
98*700637cbSDimitry Andric       auto *ThisArg = OCE->getArg(0)->IgnoreParenCasts();
99*700637cbSDimitry Andric       if (auto *VarRef = dyn_cast<DeclRefExpr>(ThisArg)) {
100*700637cbSDimitry Andric         if (VarRef->getDecl() == Guardian)
101*700637cbSDimitry Andric           return false;
102*700637cbSDimitry Andric       }
103*700637cbSDimitry Andric     }
104*700637cbSDimitry Andric     return true;
105*700637cbSDimitry Andric   }
106*700637cbSDimitry Andric };
107*700637cbSDimitry Andric 
isGuardedScopeEmbeddedInGuardianScope(const VarDecl * Guarded,const VarDecl * MaybeGuardian)108*700637cbSDimitry Andric bool isGuardedScopeEmbeddedInGuardianScope(const VarDecl *Guarded,
109*700637cbSDimitry Andric                                            const VarDecl *MaybeGuardian) {
110*700637cbSDimitry Andric   assert(Guarded);
111*700637cbSDimitry Andric   assert(MaybeGuardian);
112*700637cbSDimitry Andric 
113*700637cbSDimitry Andric   if (!MaybeGuardian->isLocalVarDecl())
114*700637cbSDimitry Andric     return false;
115*700637cbSDimitry Andric 
116*700637cbSDimitry Andric   const CompoundStmt *guardiansClosestCompStmtAncestor = nullptr;
117*700637cbSDimitry Andric 
118*700637cbSDimitry Andric   ASTContext &ctx = MaybeGuardian->getASTContext();
119*700637cbSDimitry Andric 
120*700637cbSDimitry Andric   for (DynTypedNodeList guardianAncestors = ctx.getParents(*MaybeGuardian);
121*700637cbSDimitry Andric        !guardianAncestors.empty();
122*700637cbSDimitry Andric        guardianAncestors = ctx.getParents(
123*700637cbSDimitry Andric            *guardianAncestors
124*700637cbSDimitry Andric                 .begin()) // FIXME - should we handle all of the parents?
125*700637cbSDimitry Andric   ) {
126*700637cbSDimitry Andric     for (auto &guardianAncestor : guardianAncestors) {
127*700637cbSDimitry Andric       if (auto *CStmtParentAncestor = guardianAncestor.get<CompoundStmt>()) {
128*700637cbSDimitry Andric         guardiansClosestCompStmtAncestor = CStmtParentAncestor;
129*700637cbSDimitry Andric         break;
130*700637cbSDimitry Andric       }
131*700637cbSDimitry Andric     }
132*700637cbSDimitry Andric     if (guardiansClosestCompStmtAncestor)
133*700637cbSDimitry Andric       break;
134*700637cbSDimitry Andric   }
135*700637cbSDimitry Andric 
136*700637cbSDimitry Andric   if (!guardiansClosestCompStmtAncestor)
137*700637cbSDimitry Andric     return false;
138*700637cbSDimitry Andric 
139*700637cbSDimitry Andric   // We need to skip the first CompoundStmt to avoid situation when guardian is
140*700637cbSDimitry Andric   // defined in the same scope as guarded variable.
141*700637cbSDimitry Andric   const CompoundStmt *FirstCompondStmt = nullptr;
142*700637cbSDimitry Andric   for (DynTypedNodeList guardedVarAncestors = ctx.getParents(*Guarded);
143*700637cbSDimitry Andric        !guardedVarAncestors.empty();
144*700637cbSDimitry Andric        guardedVarAncestors = ctx.getParents(
145*700637cbSDimitry Andric            *guardedVarAncestors
146*700637cbSDimitry Andric                 .begin()) // FIXME - should we handle all of the parents?
147*700637cbSDimitry Andric   ) {
148*700637cbSDimitry Andric     for (auto &guardedVarAncestor : guardedVarAncestors) {
149*700637cbSDimitry Andric       if (auto *CStmtAncestor = guardedVarAncestor.get<CompoundStmt>()) {
150*700637cbSDimitry Andric         if (!FirstCompondStmt) {
151*700637cbSDimitry Andric           FirstCompondStmt = CStmtAncestor;
152*700637cbSDimitry Andric           continue;
153*700637cbSDimitry Andric         }
154*700637cbSDimitry Andric         if (CStmtAncestor == guardiansClosestCompStmtAncestor) {
155*700637cbSDimitry Andric           GuardianVisitor guardianVisitor(MaybeGuardian);
156*700637cbSDimitry Andric           auto *GuardedScope = const_cast<CompoundStmt *>(FirstCompondStmt);
157*700637cbSDimitry Andric           return guardianVisitor.TraverseCompoundStmt(GuardedScope);
158*700637cbSDimitry Andric         }
159*700637cbSDimitry Andric       }
160*700637cbSDimitry Andric     }
161*700637cbSDimitry Andric   }
162*700637cbSDimitry Andric 
163*700637cbSDimitry Andric   return false;
164*700637cbSDimitry Andric }
165*700637cbSDimitry Andric 
166*700637cbSDimitry Andric class RawPtrRefLocalVarsChecker
167*700637cbSDimitry Andric     : public Checker<check::ASTDecl<TranslationUnitDecl>> {
168*700637cbSDimitry Andric   BugType Bug;
169*700637cbSDimitry Andric   mutable BugReporter *BR;
170*700637cbSDimitry Andric   EnsureFunctionAnalysis EFA;
171*700637cbSDimitry Andric 
172*700637cbSDimitry Andric protected:
173*700637cbSDimitry Andric   mutable std::optional<RetainTypeChecker> RTC;
174*700637cbSDimitry Andric 
175*700637cbSDimitry Andric public:
RawPtrRefLocalVarsChecker(const char * description)176*700637cbSDimitry Andric   RawPtrRefLocalVarsChecker(const char *description)
177*700637cbSDimitry Andric       : Bug(this, description, "WebKit coding guidelines") {}
178*700637cbSDimitry Andric 
179*700637cbSDimitry Andric   virtual std::optional<bool> isUnsafePtr(const QualType T) const = 0;
180*700637cbSDimitry Andric   virtual bool isSafePtr(const CXXRecordDecl *) const = 0;
181*700637cbSDimitry Andric   virtual bool isSafePtrType(const QualType) const = 0;
isSafeExpr(const Expr *) const182*700637cbSDimitry Andric   virtual bool isSafeExpr(const Expr *) const { return false; }
183*700637cbSDimitry Andric   virtual const char *ptrKind() const = 0;
184*700637cbSDimitry Andric 
checkASTDecl(const TranslationUnitDecl * TUD,AnalysisManager & MGR,BugReporter & BRArg) const185*700637cbSDimitry Andric   void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
186*700637cbSDimitry Andric                     BugReporter &BRArg) const {
187*700637cbSDimitry Andric     BR = &BRArg;
188*700637cbSDimitry Andric 
189*700637cbSDimitry Andric     // The calls to checkAST* from AnalysisConsumer don't
190*700637cbSDimitry Andric     // visit template instantiations or lambda classes. We
191*700637cbSDimitry Andric     // want to visit those, so we make our own RecursiveASTVisitor.
192*700637cbSDimitry Andric     struct LocalVisitor : DynamicRecursiveASTVisitor {
193*700637cbSDimitry Andric       const RawPtrRefLocalVarsChecker *Checker;
194*700637cbSDimitry Andric       Decl *DeclWithIssue{nullptr};
195*700637cbSDimitry Andric 
196*700637cbSDimitry Andric       TrivialFunctionAnalysis TFA;
197*700637cbSDimitry Andric 
198*700637cbSDimitry Andric       explicit LocalVisitor(const RawPtrRefLocalVarsChecker *Checker)
199*700637cbSDimitry Andric           : Checker(Checker) {
200*700637cbSDimitry Andric         assert(Checker);
201*700637cbSDimitry Andric         ShouldVisitTemplateInstantiations = true;
202*700637cbSDimitry Andric         ShouldVisitImplicitCode = false;
203*700637cbSDimitry Andric       }
204*700637cbSDimitry Andric 
205*700637cbSDimitry Andric       bool TraverseDecl(Decl *D) override {
206*700637cbSDimitry Andric         llvm::SaveAndRestore SavedDecl(DeclWithIssue);
207*700637cbSDimitry Andric         if (D && (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)))
208*700637cbSDimitry Andric           DeclWithIssue = D;
209*700637cbSDimitry Andric         return DynamicRecursiveASTVisitor::TraverseDecl(D);
210*700637cbSDimitry Andric       }
211*700637cbSDimitry Andric 
212*700637cbSDimitry Andric       bool VisitTypedefDecl(TypedefDecl *TD) override {
213*700637cbSDimitry Andric         if (Checker->RTC)
214*700637cbSDimitry Andric           Checker->RTC->visitTypedef(TD);
215*700637cbSDimitry Andric         return true;
216*700637cbSDimitry Andric       }
217*700637cbSDimitry Andric 
218*700637cbSDimitry Andric       bool VisitVarDecl(VarDecl *V) override {
219*700637cbSDimitry Andric         auto *Init = V->getInit();
220*700637cbSDimitry Andric         if (Init && V->isLocalVarDecl())
221*700637cbSDimitry Andric           Checker->visitVarDecl(V, Init, DeclWithIssue);
222*700637cbSDimitry Andric         return true;
223*700637cbSDimitry Andric       }
224*700637cbSDimitry Andric 
225*700637cbSDimitry Andric       bool VisitBinaryOperator(BinaryOperator *BO) override {
226*700637cbSDimitry Andric         if (BO->isAssignmentOp()) {
227*700637cbSDimitry Andric           if (auto *VarRef = dyn_cast<DeclRefExpr>(BO->getLHS())) {
228*700637cbSDimitry Andric             if (auto *V = dyn_cast<VarDecl>(VarRef->getDecl()))
229*700637cbSDimitry Andric               Checker->visitVarDecl(V, BO->getRHS(), DeclWithIssue);
230*700637cbSDimitry Andric           }
231*700637cbSDimitry Andric         }
232*700637cbSDimitry Andric         return true;
233*700637cbSDimitry Andric       }
234*700637cbSDimitry Andric 
235*700637cbSDimitry Andric       bool TraverseIfStmt(IfStmt *IS) override {
236*700637cbSDimitry Andric         if (!TFA.isTrivial(IS))
237*700637cbSDimitry Andric           return DynamicRecursiveASTVisitor::TraverseIfStmt(IS);
238*700637cbSDimitry Andric         return true;
239*700637cbSDimitry Andric       }
240*700637cbSDimitry Andric 
241*700637cbSDimitry Andric       bool TraverseForStmt(ForStmt *FS) override {
242*700637cbSDimitry Andric         if (!TFA.isTrivial(FS))
243*700637cbSDimitry Andric           return DynamicRecursiveASTVisitor::TraverseForStmt(FS);
244*700637cbSDimitry Andric         return true;
245*700637cbSDimitry Andric       }
246*700637cbSDimitry Andric 
247*700637cbSDimitry Andric       bool TraverseCXXForRangeStmt(CXXForRangeStmt *FRS) override {
248*700637cbSDimitry Andric         if (!TFA.isTrivial(FRS))
249*700637cbSDimitry Andric           return DynamicRecursiveASTVisitor::TraverseCXXForRangeStmt(FRS);
250*700637cbSDimitry Andric         return true;
251*700637cbSDimitry Andric       }
252*700637cbSDimitry Andric 
253*700637cbSDimitry Andric       bool TraverseWhileStmt(WhileStmt *WS) override {
254*700637cbSDimitry Andric         if (!TFA.isTrivial(WS))
255*700637cbSDimitry Andric           return DynamicRecursiveASTVisitor::TraverseWhileStmt(WS);
256*700637cbSDimitry Andric         return true;
257*700637cbSDimitry Andric       }
258*700637cbSDimitry Andric 
259*700637cbSDimitry Andric       bool TraverseCompoundStmt(CompoundStmt *CS) override {
260*700637cbSDimitry Andric         if (!TFA.isTrivial(CS))
261*700637cbSDimitry Andric           return DynamicRecursiveASTVisitor::TraverseCompoundStmt(CS);
262*700637cbSDimitry Andric         return true;
263*700637cbSDimitry Andric       }
264*700637cbSDimitry Andric 
265*700637cbSDimitry Andric       bool TraverseClassTemplateDecl(ClassTemplateDecl *Decl) override {
266*700637cbSDimitry Andric         if (isSmartPtrClass(safeGetName(Decl)))
267*700637cbSDimitry Andric           return true;
268*700637cbSDimitry Andric         return DynamicRecursiveASTVisitor::TraverseClassTemplateDecl(Decl);
269*700637cbSDimitry Andric       }
270*700637cbSDimitry Andric     };
271*700637cbSDimitry Andric 
272*700637cbSDimitry Andric     LocalVisitor visitor(this);
273*700637cbSDimitry Andric     if (RTC)
274*700637cbSDimitry Andric       RTC->visitTranslationUnitDecl(TUD);
275*700637cbSDimitry Andric     visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
276*700637cbSDimitry Andric   }
277*700637cbSDimitry Andric 
visitVarDecl(const VarDecl * V,const Expr * Value,const Decl * DeclWithIssue) const278*700637cbSDimitry Andric   void visitVarDecl(const VarDecl *V, const Expr *Value,
279*700637cbSDimitry Andric                     const Decl *DeclWithIssue) const {
280*700637cbSDimitry Andric     if (shouldSkipVarDecl(V))
281*700637cbSDimitry Andric       return;
282*700637cbSDimitry Andric 
283*700637cbSDimitry Andric     std::optional<bool> IsUncountedPtr = isUnsafePtr(V->getType());
284*700637cbSDimitry Andric     if (IsUncountedPtr && *IsUncountedPtr) {
285*700637cbSDimitry Andric       if (tryToFindPtrOrigin(
286*700637cbSDimitry Andric               Value, /*StopAtFirstRefCountedObj=*/false,
287*700637cbSDimitry Andric               [&](const clang::CXXRecordDecl *Record) {
288*700637cbSDimitry Andric                 return isSafePtr(Record);
289*700637cbSDimitry Andric               },
290*700637cbSDimitry Andric               [&](const clang::QualType Type) { return isSafePtrType(Type); },
291*700637cbSDimitry Andric               [&](const clang::Expr *InitArgOrigin, bool IsSafe) {
292*700637cbSDimitry Andric                 if (!InitArgOrigin || IsSafe)
293*700637cbSDimitry Andric                   return true;
294*700637cbSDimitry Andric 
295*700637cbSDimitry Andric                 if (isa<CXXThisExpr>(InitArgOrigin))
296*700637cbSDimitry Andric                   return true;
297*700637cbSDimitry Andric 
298*700637cbSDimitry Andric                 if (isa<CXXNullPtrLiteralExpr>(InitArgOrigin))
299*700637cbSDimitry Andric                   return true;
300*700637cbSDimitry Andric 
301*700637cbSDimitry Andric                 if (isa<IntegerLiteral>(InitArgOrigin))
302*700637cbSDimitry Andric                   return true;
303*700637cbSDimitry Andric 
304*700637cbSDimitry Andric                 if (isConstOwnerPtrMemberExpr(InitArgOrigin))
305*700637cbSDimitry Andric                   return true;
306*700637cbSDimitry Andric 
307*700637cbSDimitry Andric                 if (EFA.isACallToEnsureFn(InitArgOrigin))
308*700637cbSDimitry Andric                   return true;
309*700637cbSDimitry Andric 
310*700637cbSDimitry Andric                 if (isSafeExpr(InitArgOrigin))
311*700637cbSDimitry Andric                   return true;
312*700637cbSDimitry Andric 
313*700637cbSDimitry Andric                 if (auto *Ref = llvm::dyn_cast<DeclRefExpr>(InitArgOrigin)) {
314*700637cbSDimitry Andric                   if (auto *MaybeGuardian =
315*700637cbSDimitry Andric                           dyn_cast_or_null<VarDecl>(Ref->getFoundDecl())) {
316*700637cbSDimitry Andric                     const auto *MaybeGuardianArgType =
317*700637cbSDimitry Andric                         MaybeGuardian->getType().getTypePtr();
318*700637cbSDimitry Andric                     if (MaybeGuardianArgType) {
319*700637cbSDimitry Andric                       const CXXRecordDecl *const MaybeGuardianArgCXXRecord =
320*700637cbSDimitry Andric                           MaybeGuardianArgType->getAsCXXRecordDecl();
321*700637cbSDimitry Andric                       if (MaybeGuardianArgCXXRecord) {
322*700637cbSDimitry Andric                         if (MaybeGuardian->isLocalVarDecl() &&
323*700637cbSDimitry Andric                             (isSafePtr(MaybeGuardianArgCXXRecord) ||
324*700637cbSDimitry Andric                              isRefcountedStringsHack(MaybeGuardian)) &&
325*700637cbSDimitry Andric                             isGuardedScopeEmbeddedInGuardianScope(
326*700637cbSDimitry Andric                                 V, MaybeGuardian))
327*700637cbSDimitry Andric                           return true;
328*700637cbSDimitry Andric                       }
329*700637cbSDimitry Andric                     }
330*700637cbSDimitry Andric 
331*700637cbSDimitry Andric                     // Parameters are guaranteed to be safe for the duration of
332*700637cbSDimitry Andric                     // the call by another checker.
333*700637cbSDimitry Andric                     if (isa<ParmVarDecl>(MaybeGuardian))
334*700637cbSDimitry Andric                       return true;
335*700637cbSDimitry Andric                   }
336*700637cbSDimitry Andric                 }
337*700637cbSDimitry Andric 
338*700637cbSDimitry Andric                 return false;
339*700637cbSDimitry Andric               }))
340*700637cbSDimitry Andric         return;
341*700637cbSDimitry Andric 
342*700637cbSDimitry Andric       reportBug(V, Value, DeclWithIssue);
343*700637cbSDimitry Andric     }
344*700637cbSDimitry Andric   }
345*700637cbSDimitry Andric 
shouldSkipVarDecl(const VarDecl * V) const346*700637cbSDimitry Andric   bool shouldSkipVarDecl(const VarDecl *V) const {
347*700637cbSDimitry Andric     assert(V);
348*700637cbSDimitry Andric     if (isa<ImplicitParamDecl>(V))
349*700637cbSDimitry Andric       return true;
350*700637cbSDimitry Andric     return BR->getSourceManager().isInSystemHeader(V->getLocation());
351*700637cbSDimitry Andric   }
352*700637cbSDimitry Andric 
reportBug(const VarDecl * V,const Expr * Value,const Decl * DeclWithIssue) const353*700637cbSDimitry Andric   void reportBug(const VarDecl *V, const Expr *Value,
354*700637cbSDimitry Andric                  const Decl *DeclWithIssue) const {
355*700637cbSDimitry Andric     assert(V);
356*700637cbSDimitry Andric     SmallString<100> Buf;
357*700637cbSDimitry Andric     llvm::raw_svector_ostream Os(Buf);
358*700637cbSDimitry Andric 
359*700637cbSDimitry Andric     if (isa<ParmVarDecl>(V)) {
360*700637cbSDimitry Andric       Os << "Assignment to an " << ptrKind() << " parameter ";
361*700637cbSDimitry Andric       printQuotedQualifiedName(Os, V);
362*700637cbSDimitry Andric       Os << " is unsafe.";
363*700637cbSDimitry Andric 
364*700637cbSDimitry Andric       PathDiagnosticLocation BSLoc(Value->getExprLoc(), BR->getSourceManager());
365*700637cbSDimitry Andric       auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
366*700637cbSDimitry Andric       Report->addRange(Value->getSourceRange());
367*700637cbSDimitry Andric       BR->emitReport(std::move(Report));
368*700637cbSDimitry Andric     } else {
369*700637cbSDimitry Andric       if (V->hasLocalStorage())
370*700637cbSDimitry Andric         Os << "Local variable ";
371*700637cbSDimitry Andric       else if (V->isStaticLocal())
372*700637cbSDimitry Andric         Os << "Static local variable ";
373*700637cbSDimitry Andric       else if (V->hasGlobalStorage())
374*700637cbSDimitry Andric         Os << "Global variable ";
375*700637cbSDimitry Andric       else
376*700637cbSDimitry Andric         Os << "Variable ";
377*700637cbSDimitry Andric       printQuotedQualifiedName(Os, V);
378*700637cbSDimitry Andric       Os << " is " << ptrKind() << " and unsafe.";
379*700637cbSDimitry Andric 
380*700637cbSDimitry Andric       PathDiagnosticLocation BSLoc(V->getLocation(), BR->getSourceManager());
381*700637cbSDimitry Andric       auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
382*700637cbSDimitry Andric       Report->addRange(V->getSourceRange());
383*700637cbSDimitry Andric       Report->setDeclWithIssue(DeclWithIssue);
384*700637cbSDimitry Andric       BR->emitReport(std::move(Report));
385*700637cbSDimitry Andric     }
386*700637cbSDimitry Andric   }
387*700637cbSDimitry Andric };
388*700637cbSDimitry Andric 
389*700637cbSDimitry Andric class UncountedLocalVarsChecker final : public RawPtrRefLocalVarsChecker {
390*700637cbSDimitry Andric public:
UncountedLocalVarsChecker()391*700637cbSDimitry Andric   UncountedLocalVarsChecker()
392*700637cbSDimitry Andric       : RawPtrRefLocalVarsChecker("Uncounted raw pointer or reference not "
393*700637cbSDimitry Andric                                   "provably backed by ref-counted variable") {}
isUnsafePtr(const QualType T) const394*700637cbSDimitry Andric   std::optional<bool> isUnsafePtr(const QualType T) const final {
395*700637cbSDimitry Andric     return isUncountedPtr(T);
396*700637cbSDimitry Andric   }
isSafePtr(const CXXRecordDecl * Record) const397*700637cbSDimitry Andric   bool isSafePtr(const CXXRecordDecl *Record) const final {
398*700637cbSDimitry Andric     return isRefCounted(Record) || isCheckedPtr(Record);
399*700637cbSDimitry Andric   }
isSafePtrType(const QualType type) const400*700637cbSDimitry Andric   bool isSafePtrType(const QualType type) const final {
401*700637cbSDimitry Andric     return isRefOrCheckedPtrType(type);
402*700637cbSDimitry Andric   }
ptrKind() const403*700637cbSDimitry Andric   const char *ptrKind() const final { return "uncounted"; }
404*700637cbSDimitry Andric };
405*700637cbSDimitry Andric 
406*700637cbSDimitry Andric class UncheckedLocalVarsChecker final : public RawPtrRefLocalVarsChecker {
407*700637cbSDimitry Andric public:
UncheckedLocalVarsChecker()408*700637cbSDimitry Andric   UncheckedLocalVarsChecker()
409*700637cbSDimitry Andric       : RawPtrRefLocalVarsChecker("Unchecked raw pointer or reference not "
410*700637cbSDimitry Andric                                   "provably backed by checked variable") {}
isUnsafePtr(const QualType T) const411*700637cbSDimitry Andric   std::optional<bool> isUnsafePtr(const QualType T) const final {
412*700637cbSDimitry Andric     return isUncheckedPtr(T);
413*700637cbSDimitry Andric   }
isSafePtr(const CXXRecordDecl * Record) const414*700637cbSDimitry Andric   bool isSafePtr(const CXXRecordDecl *Record) const final {
415*700637cbSDimitry Andric     return isRefCounted(Record) || isCheckedPtr(Record);
416*700637cbSDimitry Andric   }
isSafePtrType(const QualType type) const417*700637cbSDimitry Andric   bool isSafePtrType(const QualType type) const final {
418*700637cbSDimitry Andric     return isRefOrCheckedPtrType(type);
419*700637cbSDimitry Andric   }
isSafeExpr(const Expr * E) const420*700637cbSDimitry Andric   bool isSafeExpr(const Expr *E) const final {
421*700637cbSDimitry Andric     return isExprToGetCheckedPtrCapableMember(E);
422*700637cbSDimitry Andric   }
ptrKind() const423*700637cbSDimitry Andric   const char *ptrKind() const final { return "unchecked"; }
424*700637cbSDimitry Andric };
425*700637cbSDimitry Andric 
426*700637cbSDimitry Andric class UnretainedLocalVarsChecker final : public RawPtrRefLocalVarsChecker {
427*700637cbSDimitry Andric public:
UnretainedLocalVarsChecker()428*700637cbSDimitry Andric   UnretainedLocalVarsChecker()
429*700637cbSDimitry Andric       : RawPtrRefLocalVarsChecker("Unretained raw pointer or reference not "
430*700637cbSDimitry Andric                                   "provably backed by a RetainPtr") {
431*700637cbSDimitry Andric     RTC = RetainTypeChecker();
432*700637cbSDimitry Andric   }
isUnsafePtr(const QualType T) const433*700637cbSDimitry Andric   std::optional<bool> isUnsafePtr(const QualType T) const final {
434*700637cbSDimitry Andric     return RTC->isUnretained(T);
435*700637cbSDimitry Andric   }
isSafePtr(const CXXRecordDecl * Record) const436*700637cbSDimitry Andric   bool isSafePtr(const CXXRecordDecl *Record) const final {
437*700637cbSDimitry Andric     return isRetainPtr(Record);
438*700637cbSDimitry Andric   }
isSafePtrType(const QualType type) const439*700637cbSDimitry Andric   bool isSafePtrType(const QualType type) const final {
440*700637cbSDimitry Andric     return isRetainPtrType(type);
441*700637cbSDimitry Andric   }
isSafeExpr(const Expr * E) const442*700637cbSDimitry Andric   bool isSafeExpr(const Expr *E) const final {
443*700637cbSDimitry Andric     return ento::cocoa::isCocoaObjectRef(E->getType()) &&
444*700637cbSDimitry Andric            isa<ObjCMessageExpr>(E);
445*700637cbSDimitry Andric   }
ptrKind() const446*700637cbSDimitry Andric   const char *ptrKind() const final { return "unretained"; }
447*700637cbSDimitry Andric };
448*700637cbSDimitry Andric 
449*700637cbSDimitry Andric } // namespace
450*700637cbSDimitry Andric 
registerUncountedLocalVarsChecker(CheckerManager & Mgr)451*700637cbSDimitry Andric void ento::registerUncountedLocalVarsChecker(CheckerManager &Mgr) {
452*700637cbSDimitry Andric   Mgr.registerChecker<UncountedLocalVarsChecker>();
453*700637cbSDimitry Andric }
454*700637cbSDimitry Andric 
shouldRegisterUncountedLocalVarsChecker(const CheckerManager &)455*700637cbSDimitry Andric bool ento::shouldRegisterUncountedLocalVarsChecker(const CheckerManager &) {
456*700637cbSDimitry Andric   return true;
457*700637cbSDimitry Andric }
458*700637cbSDimitry Andric 
registerUncheckedLocalVarsChecker(CheckerManager & Mgr)459*700637cbSDimitry Andric void ento::registerUncheckedLocalVarsChecker(CheckerManager &Mgr) {
460*700637cbSDimitry Andric   Mgr.registerChecker<UncheckedLocalVarsChecker>();
461*700637cbSDimitry Andric }
462*700637cbSDimitry Andric 
shouldRegisterUncheckedLocalVarsChecker(const CheckerManager &)463*700637cbSDimitry Andric bool ento::shouldRegisterUncheckedLocalVarsChecker(const CheckerManager &) {
464*700637cbSDimitry Andric   return true;
465*700637cbSDimitry Andric }
466*700637cbSDimitry Andric 
registerUnretainedLocalVarsChecker(CheckerManager & Mgr)467*700637cbSDimitry Andric void ento::registerUnretainedLocalVarsChecker(CheckerManager &Mgr) {
468*700637cbSDimitry Andric   Mgr.registerChecker<UnretainedLocalVarsChecker>();
469*700637cbSDimitry Andric }
470*700637cbSDimitry Andric 
shouldRegisterUnretainedLocalVarsChecker(const CheckerManager &)471*700637cbSDimitry Andric bool ento::shouldRegisterUnretainedLocalVarsChecker(const CheckerManager &) {
472*700637cbSDimitry Andric   return true;
473*700637cbSDimitry Andric }
474