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