1e8d8bef9SDimitry Andric //=======- UncountedLocalVarsChecker.cpp -------------------------*- C++ -*-==//
2e8d8bef9SDimitry Andric //
3e8d8bef9SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e8d8bef9SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5e8d8bef9SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e8d8bef9SDimitry Andric //
7e8d8bef9SDimitry Andric //===----------------------------------------------------------------------===//
8e8d8bef9SDimitry Andric 
9e8d8bef9SDimitry Andric #include "ASTUtils.h"
10e8d8bef9SDimitry Andric #include "DiagOutputUtils.h"
11e8d8bef9SDimitry Andric #include "PtrTypesSemantics.h"
12e8d8bef9SDimitry Andric #include "clang/AST/CXXInheritance.h"
13e8d8bef9SDimitry Andric #include "clang/AST/Decl.h"
14e8d8bef9SDimitry Andric #include "clang/AST/DeclCXX.h"
15e8d8bef9SDimitry Andric #include "clang/AST/ParentMapContext.h"
16e8d8bef9SDimitry Andric #include "clang/AST/RecursiveASTVisitor.h"
17e8d8bef9SDimitry Andric #include "clang/Basic/SourceLocation.h"
18e8d8bef9SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
19e8d8bef9SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
20e8d8bef9SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
21e8d8bef9SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
22bdd1243dSDimitry Andric #include <optional>
23e8d8bef9SDimitry Andric 
24e8d8bef9SDimitry Andric using namespace clang;
25e8d8bef9SDimitry Andric using namespace ento;
26e8d8bef9SDimitry Andric 
27e8d8bef9SDimitry Andric namespace {
28e8d8bef9SDimitry Andric 
29e8d8bef9SDimitry Andric // FIXME: should be defined by anotations in the future
30e8d8bef9SDimitry Andric bool isRefcountedStringsHack(const VarDecl *V) {
31e8d8bef9SDimitry Andric   assert(V);
32e8d8bef9SDimitry Andric   auto safeClass = [](const std::string &className) {
33e8d8bef9SDimitry Andric     return className == "String" || className == "AtomString" ||
34e8d8bef9SDimitry Andric            className == "UniquedString" || className == "Identifier";
35e8d8bef9SDimitry Andric   };
36e8d8bef9SDimitry Andric   QualType QT = V->getType();
37e8d8bef9SDimitry Andric   auto *T = QT.getTypePtr();
38e8d8bef9SDimitry Andric   if (auto *CXXRD = T->getAsCXXRecordDecl()) {
39e8d8bef9SDimitry Andric     if (safeClass(safeGetName(CXXRD)))
40e8d8bef9SDimitry Andric       return true;
41e8d8bef9SDimitry Andric   }
42e8d8bef9SDimitry Andric   if (T->isPointerType() || T->isReferenceType()) {
43e8d8bef9SDimitry Andric     if (auto *CXXRD = T->getPointeeCXXRecordDecl()) {
44e8d8bef9SDimitry Andric       if (safeClass(safeGetName(CXXRD)))
45e8d8bef9SDimitry Andric         return true;
46e8d8bef9SDimitry Andric     }
47e8d8bef9SDimitry Andric   }
48e8d8bef9SDimitry Andric   return false;
49e8d8bef9SDimitry Andric }
50e8d8bef9SDimitry Andric 
51e8d8bef9SDimitry Andric bool isGuardedScopeEmbeddedInGuardianScope(const VarDecl *Guarded,
52e8d8bef9SDimitry Andric                                            const VarDecl *MaybeGuardian) {
53e8d8bef9SDimitry Andric   assert(Guarded);
54e8d8bef9SDimitry Andric   assert(MaybeGuardian);
55e8d8bef9SDimitry Andric 
56e8d8bef9SDimitry Andric   if (!MaybeGuardian->isLocalVarDecl())
57e8d8bef9SDimitry Andric     return false;
58e8d8bef9SDimitry Andric 
59e8d8bef9SDimitry Andric   const CompoundStmt *guardiansClosestCompStmtAncestor = nullptr;
60e8d8bef9SDimitry Andric 
61e8d8bef9SDimitry Andric   ASTContext &ctx = MaybeGuardian->getASTContext();
62e8d8bef9SDimitry Andric 
63e8d8bef9SDimitry Andric   for (DynTypedNodeList guardianAncestors = ctx.getParents(*MaybeGuardian);
64e8d8bef9SDimitry Andric        !guardianAncestors.empty();
65e8d8bef9SDimitry Andric        guardianAncestors = ctx.getParents(
66e8d8bef9SDimitry Andric            *guardianAncestors
67e8d8bef9SDimitry Andric                 .begin()) // FIXME - should we handle all of the parents?
68e8d8bef9SDimitry Andric   ) {
69e8d8bef9SDimitry Andric     for (auto &guardianAncestor : guardianAncestors) {
70e8d8bef9SDimitry Andric       if (auto *CStmtParentAncestor = guardianAncestor.get<CompoundStmt>()) {
71e8d8bef9SDimitry Andric         guardiansClosestCompStmtAncestor = CStmtParentAncestor;
72e8d8bef9SDimitry Andric         break;
73e8d8bef9SDimitry Andric       }
74e8d8bef9SDimitry Andric     }
75e8d8bef9SDimitry Andric     if (guardiansClosestCompStmtAncestor)
76e8d8bef9SDimitry Andric       break;
77e8d8bef9SDimitry Andric   }
78e8d8bef9SDimitry Andric 
79e8d8bef9SDimitry Andric   if (!guardiansClosestCompStmtAncestor)
80e8d8bef9SDimitry Andric     return false;
81e8d8bef9SDimitry Andric 
82e8d8bef9SDimitry Andric   // We need to skip the first CompoundStmt to avoid situation when guardian is
83e8d8bef9SDimitry Andric   // defined in the same scope as guarded variable.
84e8d8bef9SDimitry Andric   bool HaveSkippedFirstCompoundStmt = false;
85e8d8bef9SDimitry Andric   for (DynTypedNodeList guardedVarAncestors = ctx.getParents(*Guarded);
86e8d8bef9SDimitry Andric        !guardedVarAncestors.empty();
87e8d8bef9SDimitry Andric        guardedVarAncestors = ctx.getParents(
88e8d8bef9SDimitry Andric            *guardedVarAncestors
89e8d8bef9SDimitry Andric                 .begin()) // FIXME - should we handle all of the parents?
90e8d8bef9SDimitry Andric   ) {
91e8d8bef9SDimitry Andric     for (auto &guardedVarAncestor : guardedVarAncestors) {
92e8d8bef9SDimitry Andric       if (auto *CStmtAncestor = guardedVarAncestor.get<CompoundStmt>()) {
93e8d8bef9SDimitry Andric         if (!HaveSkippedFirstCompoundStmt) {
94e8d8bef9SDimitry Andric           HaveSkippedFirstCompoundStmt = true;
95e8d8bef9SDimitry Andric           continue;
96e8d8bef9SDimitry Andric         }
97e8d8bef9SDimitry Andric         if (CStmtAncestor == guardiansClosestCompStmtAncestor)
98e8d8bef9SDimitry Andric           return true;
99e8d8bef9SDimitry Andric       }
100e8d8bef9SDimitry Andric     }
101e8d8bef9SDimitry Andric   }
102e8d8bef9SDimitry Andric 
103e8d8bef9SDimitry Andric   return false;
104e8d8bef9SDimitry Andric }
105e8d8bef9SDimitry Andric 
106e8d8bef9SDimitry Andric class UncountedLocalVarsChecker
107e8d8bef9SDimitry Andric     : public Checker<check::ASTDecl<TranslationUnitDecl>> {
108e8d8bef9SDimitry Andric   BugType Bug{this,
109e8d8bef9SDimitry Andric               "Uncounted raw pointer or reference not provably backed by "
110e8d8bef9SDimitry Andric               "ref-counted variable",
111e8d8bef9SDimitry Andric               "WebKit coding guidelines"};
112e8d8bef9SDimitry Andric   mutable BugReporter *BR;
113e8d8bef9SDimitry Andric 
114e8d8bef9SDimitry Andric public:
115e8d8bef9SDimitry Andric   void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
116e8d8bef9SDimitry Andric                     BugReporter &BRArg) const {
117e8d8bef9SDimitry Andric     BR = &BRArg;
118e8d8bef9SDimitry Andric 
119e8d8bef9SDimitry Andric     // The calls to checkAST* from AnalysisConsumer don't
120e8d8bef9SDimitry Andric     // visit template instantiations or lambda classes. We
121e8d8bef9SDimitry Andric     // want to visit those, so we make our own RecursiveASTVisitor.
122e8d8bef9SDimitry Andric     struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
123e8d8bef9SDimitry Andric       const UncountedLocalVarsChecker *Checker;
124*0fca6ea1SDimitry Andric 
125*0fca6ea1SDimitry Andric       TrivialFunctionAnalysis TFA;
126*0fca6ea1SDimitry Andric 
127*0fca6ea1SDimitry Andric       using Base = RecursiveASTVisitor<LocalVisitor>;
128*0fca6ea1SDimitry Andric 
129e8d8bef9SDimitry Andric       explicit LocalVisitor(const UncountedLocalVarsChecker *Checker)
130e8d8bef9SDimitry Andric           : Checker(Checker) {
131e8d8bef9SDimitry Andric         assert(Checker);
132e8d8bef9SDimitry Andric       }
133e8d8bef9SDimitry Andric 
134e8d8bef9SDimitry Andric       bool shouldVisitTemplateInstantiations() const { return true; }
135e8d8bef9SDimitry Andric       bool shouldVisitImplicitCode() const { return false; }
136e8d8bef9SDimitry Andric 
137e8d8bef9SDimitry Andric       bool VisitVarDecl(VarDecl *V) {
138*0fca6ea1SDimitry Andric         auto *Init = V->getInit();
139*0fca6ea1SDimitry Andric         if (Init && V->isLocalVarDecl())
140*0fca6ea1SDimitry Andric           Checker->visitVarDecl(V, Init);
141*0fca6ea1SDimitry Andric         return true;
142*0fca6ea1SDimitry Andric       }
143*0fca6ea1SDimitry Andric 
144*0fca6ea1SDimitry Andric       bool VisitBinaryOperator(const BinaryOperator *BO) {
145*0fca6ea1SDimitry Andric         if (BO->isAssignmentOp()) {
146*0fca6ea1SDimitry Andric           if (auto *VarRef = dyn_cast<DeclRefExpr>(BO->getLHS())) {
147*0fca6ea1SDimitry Andric             if (auto *V = dyn_cast<VarDecl>(VarRef->getDecl()))
148*0fca6ea1SDimitry Andric               Checker->visitVarDecl(V, BO->getRHS());
149*0fca6ea1SDimitry Andric           }
150*0fca6ea1SDimitry Andric         }
151*0fca6ea1SDimitry Andric         return true;
152*0fca6ea1SDimitry Andric       }
153*0fca6ea1SDimitry Andric 
154*0fca6ea1SDimitry Andric       bool TraverseIfStmt(IfStmt *IS) {
155*0fca6ea1SDimitry Andric         if (!TFA.isTrivial(IS))
156*0fca6ea1SDimitry Andric           return Base::TraverseIfStmt(IS);
157*0fca6ea1SDimitry Andric         return true;
158*0fca6ea1SDimitry Andric       }
159*0fca6ea1SDimitry Andric 
160*0fca6ea1SDimitry Andric       bool TraverseForStmt(ForStmt *FS) {
161*0fca6ea1SDimitry Andric         if (!TFA.isTrivial(FS))
162*0fca6ea1SDimitry Andric           return Base::TraverseForStmt(FS);
163*0fca6ea1SDimitry Andric         return true;
164*0fca6ea1SDimitry Andric       }
165*0fca6ea1SDimitry Andric 
166*0fca6ea1SDimitry Andric       bool TraverseCXXForRangeStmt(CXXForRangeStmt *FRS) {
167*0fca6ea1SDimitry Andric         if (!TFA.isTrivial(FRS))
168*0fca6ea1SDimitry Andric           return Base::TraverseCXXForRangeStmt(FRS);
169*0fca6ea1SDimitry Andric         return true;
170*0fca6ea1SDimitry Andric       }
171*0fca6ea1SDimitry Andric 
172*0fca6ea1SDimitry Andric       bool TraverseWhileStmt(WhileStmt *WS) {
173*0fca6ea1SDimitry Andric         if (!TFA.isTrivial(WS))
174*0fca6ea1SDimitry Andric           return Base::TraverseWhileStmt(WS);
175*0fca6ea1SDimitry Andric         return true;
176*0fca6ea1SDimitry Andric       }
177*0fca6ea1SDimitry Andric 
178*0fca6ea1SDimitry Andric       bool TraverseCompoundStmt(CompoundStmt *CS) {
179*0fca6ea1SDimitry Andric         if (!TFA.isTrivial(CS))
180*0fca6ea1SDimitry Andric           return Base::TraverseCompoundStmt(CS);
181e8d8bef9SDimitry Andric         return true;
182e8d8bef9SDimitry Andric       }
183e8d8bef9SDimitry Andric     };
184e8d8bef9SDimitry Andric 
185e8d8bef9SDimitry Andric     LocalVisitor visitor(this);
186e8d8bef9SDimitry Andric     visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
187e8d8bef9SDimitry Andric   }
188e8d8bef9SDimitry Andric 
189*0fca6ea1SDimitry Andric   void visitVarDecl(const VarDecl *V, const Expr *Value) const {
190e8d8bef9SDimitry Andric     if (shouldSkipVarDecl(V))
191e8d8bef9SDimitry Andric       return;
192e8d8bef9SDimitry Andric 
193e8d8bef9SDimitry Andric     const auto *ArgType = V->getType().getTypePtr();
194e8d8bef9SDimitry Andric     if (!ArgType)
195e8d8bef9SDimitry Andric       return;
196e8d8bef9SDimitry Andric 
197bdd1243dSDimitry Andric     std::optional<bool> IsUncountedPtr = isUncountedPtr(ArgType);
198e8d8bef9SDimitry Andric     if (IsUncountedPtr && *IsUncountedPtr) {
199*0fca6ea1SDimitry Andric       if (tryToFindPtrOrigin(
200*0fca6ea1SDimitry Andric               Value, /*StopAtFirstRefCountedObj=*/false,
201*0fca6ea1SDimitry Andric               [&](const clang::Expr *InitArgOrigin, bool IsSafe) {
202e8d8bef9SDimitry Andric                 if (!InitArgOrigin)
203*0fca6ea1SDimitry Andric                   return true;
204e8d8bef9SDimitry Andric 
205e8d8bef9SDimitry Andric                 if (isa<CXXThisExpr>(InitArgOrigin))
206*0fca6ea1SDimitry Andric                   return true;
207*0fca6ea1SDimitry Andric 
208*0fca6ea1SDimitry Andric                 if (isa<CXXNullPtrLiteralExpr>(InitArgOrigin))
209*0fca6ea1SDimitry Andric                   return true;
210*0fca6ea1SDimitry Andric 
211*0fca6ea1SDimitry Andric                 if (isa<IntegerLiteral>(InitArgOrigin))
212*0fca6ea1SDimitry Andric                   return true;
213e8d8bef9SDimitry Andric 
214e8d8bef9SDimitry Andric                 if (auto *Ref = llvm::dyn_cast<DeclRefExpr>(InitArgOrigin)) {
215e8d8bef9SDimitry Andric                   if (auto *MaybeGuardian =
216e8d8bef9SDimitry Andric                           dyn_cast_or_null<VarDecl>(Ref->getFoundDecl())) {
217e8d8bef9SDimitry Andric                     const auto *MaybeGuardianArgType =
218e8d8bef9SDimitry Andric                         MaybeGuardian->getType().getTypePtr();
219*0fca6ea1SDimitry Andric                     if (MaybeGuardianArgType) {
220e8d8bef9SDimitry Andric                       const CXXRecordDecl *const MaybeGuardianArgCXXRecord =
221e8d8bef9SDimitry Andric                           MaybeGuardianArgType->getAsCXXRecordDecl();
222*0fca6ea1SDimitry Andric                       if (MaybeGuardianArgCXXRecord) {
223e8d8bef9SDimitry Andric                         if (MaybeGuardian->isLocalVarDecl() &&
224e8d8bef9SDimitry Andric                             (isRefCounted(MaybeGuardianArgCXXRecord) ||
225e8d8bef9SDimitry Andric                              isRefcountedStringsHack(MaybeGuardian)) &&
226*0fca6ea1SDimitry Andric                             isGuardedScopeEmbeddedInGuardianScope(
227*0fca6ea1SDimitry Andric                                 V, MaybeGuardian))
228*0fca6ea1SDimitry Andric                           return true;
229*0fca6ea1SDimitry Andric                       }
230e8d8bef9SDimitry Andric                     }
231e8d8bef9SDimitry Andric 
232*0fca6ea1SDimitry Andric                     // Parameters are guaranteed to be safe for the duration of
233*0fca6ea1SDimitry Andric                     // the call by another checker.
234e8d8bef9SDimitry Andric                     if (isa<ParmVarDecl>(MaybeGuardian))
235*0fca6ea1SDimitry Andric                       return true;
236e8d8bef9SDimitry Andric                   }
237e8d8bef9SDimitry Andric                 }
238e8d8bef9SDimitry Andric 
239*0fca6ea1SDimitry Andric                 return false;
240*0fca6ea1SDimitry Andric               }))
241*0fca6ea1SDimitry Andric         return;
242*0fca6ea1SDimitry Andric 
243*0fca6ea1SDimitry Andric       reportBug(V, Value);
244e8d8bef9SDimitry Andric     }
245e8d8bef9SDimitry Andric   }
246e8d8bef9SDimitry Andric 
247e8d8bef9SDimitry Andric   bool shouldSkipVarDecl(const VarDecl *V) const {
248e8d8bef9SDimitry Andric     assert(V);
249*0fca6ea1SDimitry Andric     return BR->getSourceManager().isInSystemHeader(V->getLocation());
250e8d8bef9SDimitry Andric   }
251e8d8bef9SDimitry Andric 
252*0fca6ea1SDimitry Andric   void reportBug(const VarDecl *V, const Expr *Value) const {
253e8d8bef9SDimitry Andric     assert(V);
254e8d8bef9SDimitry Andric     SmallString<100> Buf;
255e8d8bef9SDimitry Andric     llvm::raw_svector_ostream Os(Buf);
256e8d8bef9SDimitry Andric 
257*0fca6ea1SDimitry Andric     if (dyn_cast<ParmVarDecl>(V)) {
258*0fca6ea1SDimitry Andric       Os << "Assignment to an uncounted parameter ";
259*0fca6ea1SDimitry Andric       printQuotedQualifiedName(Os, V);
260*0fca6ea1SDimitry Andric       Os << " is unsafe.";
261*0fca6ea1SDimitry Andric 
262*0fca6ea1SDimitry Andric       PathDiagnosticLocation BSLoc(Value->getExprLoc(), BR->getSourceManager());
263*0fca6ea1SDimitry Andric       auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
264*0fca6ea1SDimitry Andric       Report->addRange(Value->getSourceRange());
265*0fca6ea1SDimitry Andric       BR->emitReport(std::move(Report));
266*0fca6ea1SDimitry Andric     } else {
267*0fca6ea1SDimitry Andric       if (V->hasLocalStorage())
268e8d8bef9SDimitry Andric         Os << "Local variable ";
269*0fca6ea1SDimitry Andric       else if (V->isStaticLocal())
270*0fca6ea1SDimitry Andric         Os << "Static local variable ";
271*0fca6ea1SDimitry Andric       else if (V->hasGlobalStorage())
272*0fca6ea1SDimitry Andric         Os << "Global variable ";
273*0fca6ea1SDimitry Andric       else
274*0fca6ea1SDimitry Andric         Os << "Variable ";
275e8d8bef9SDimitry Andric       printQuotedQualifiedName(Os, V);
276e8d8bef9SDimitry Andric       Os << " is uncounted and unsafe.";
277e8d8bef9SDimitry Andric 
278e8d8bef9SDimitry Andric       PathDiagnosticLocation BSLoc(V->getLocation(), BR->getSourceManager());
279e8d8bef9SDimitry Andric       auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
280e8d8bef9SDimitry Andric       Report->addRange(V->getSourceRange());
281e8d8bef9SDimitry Andric       BR->emitReport(std::move(Report));
282e8d8bef9SDimitry Andric     }
283*0fca6ea1SDimitry Andric   }
284e8d8bef9SDimitry Andric };
285e8d8bef9SDimitry Andric } // namespace
286e8d8bef9SDimitry Andric 
287e8d8bef9SDimitry Andric void ento::registerUncountedLocalVarsChecker(CheckerManager &Mgr) {
288e8d8bef9SDimitry Andric   Mgr.registerChecker<UncountedLocalVarsChecker>();
289e8d8bef9SDimitry Andric }
290e8d8bef9SDimitry Andric 
291e8d8bef9SDimitry Andric bool ento::shouldRegisterUncountedLocalVarsChecker(const CheckerManager &) {
292e8d8bef9SDimitry Andric   return true;
293e8d8bef9SDimitry Andric }
294