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"
22e8d8bef9SDimitry Andric #include "llvm/ADT/DenseSet.h"
23*bdd1243dSDimitry Andric #include <optional>
24e8d8bef9SDimitry Andric 
25e8d8bef9SDimitry Andric using namespace clang;
26e8d8bef9SDimitry Andric using namespace ento;
27e8d8bef9SDimitry Andric 
28e8d8bef9SDimitry Andric namespace {
29e8d8bef9SDimitry Andric 
30e8d8bef9SDimitry Andric // for ( int a = ...) ... true
31e8d8bef9SDimitry Andric // for ( int a : ...) ... true
32e8d8bef9SDimitry Andric // if ( int* a = ) ... true
33e8d8bef9SDimitry Andric // anything else ... false
34e8d8bef9SDimitry Andric bool isDeclaredInForOrIf(const VarDecl *Var) {
35e8d8bef9SDimitry Andric   assert(Var);
36e8d8bef9SDimitry Andric   auto &ASTCtx = Var->getASTContext();
37e8d8bef9SDimitry Andric   auto parent = ASTCtx.getParents(*Var);
38e8d8bef9SDimitry Andric 
39e8d8bef9SDimitry Andric   if (parent.size() == 1) {
40e8d8bef9SDimitry Andric     if (auto *DS = parent.begin()->get<DeclStmt>()) {
41e8d8bef9SDimitry Andric       DynTypedNodeList grandParent = ASTCtx.getParents(*DS);
42e8d8bef9SDimitry Andric       if (grandParent.size() == 1) {
43e8d8bef9SDimitry Andric         return grandParent.begin()->get<ForStmt>() ||
44e8d8bef9SDimitry Andric                grandParent.begin()->get<IfStmt>() ||
45e8d8bef9SDimitry Andric                grandParent.begin()->get<CXXForRangeStmt>();
46e8d8bef9SDimitry Andric       }
47e8d8bef9SDimitry Andric     }
48e8d8bef9SDimitry Andric   }
49e8d8bef9SDimitry Andric   return false;
50e8d8bef9SDimitry Andric }
51e8d8bef9SDimitry Andric 
52e8d8bef9SDimitry Andric // FIXME: should be defined by anotations in the future
53e8d8bef9SDimitry Andric bool isRefcountedStringsHack(const VarDecl *V) {
54e8d8bef9SDimitry Andric   assert(V);
55e8d8bef9SDimitry Andric   auto safeClass = [](const std::string &className) {
56e8d8bef9SDimitry Andric     return className == "String" || className == "AtomString" ||
57e8d8bef9SDimitry Andric            className == "UniquedString" || className == "Identifier";
58e8d8bef9SDimitry Andric   };
59e8d8bef9SDimitry Andric   QualType QT = V->getType();
60e8d8bef9SDimitry Andric   auto *T = QT.getTypePtr();
61e8d8bef9SDimitry Andric   if (auto *CXXRD = T->getAsCXXRecordDecl()) {
62e8d8bef9SDimitry Andric     if (safeClass(safeGetName(CXXRD)))
63e8d8bef9SDimitry Andric       return true;
64e8d8bef9SDimitry Andric   }
65e8d8bef9SDimitry Andric   if (T->isPointerType() || T->isReferenceType()) {
66e8d8bef9SDimitry Andric     if (auto *CXXRD = T->getPointeeCXXRecordDecl()) {
67e8d8bef9SDimitry Andric       if (safeClass(safeGetName(CXXRD)))
68e8d8bef9SDimitry Andric         return true;
69e8d8bef9SDimitry Andric     }
70e8d8bef9SDimitry Andric   }
71e8d8bef9SDimitry Andric   return false;
72e8d8bef9SDimitry Andric }
73e8d8bef9SDimitry Andric 
74e8d8bef9SDimitry Andric bool isGuardedScopeEmbeddedInGuardianScope(const VarDecl *Guarded,
75e8d8bef9SDimitry Andric                                            const VarDecl *MaybeGuardian) {
76e8d8bef9SDimitry Andric   assert(Guarded);
77e8d8bef9SDimitry Andric   assert(MaybeGuardian);
78e8d8bef9SDimitry Andric 
79e8d8bef9SDimitry Andric   if (!MaybeGuardian->isLocalVarDecl())
80e8d8bef9SDimitry Andric     return false;
81e8d8bef9SDimitry Andric 
82e8d8bef9SDimitry Andric   const CompoundStmt *guardiansClosestCompStmtAncestor = nullptr;
83e8d8bef9SDimitry Andric 
84e8d8bef9SDimitry Andric   ASTContext &ctx = MaybeGuardian->getASTContext();
85e8d8bef9SDimitry Andric 
86e8d8bef9SDimitry Andric   for (DynTypedNodeList guardianAncestors = ctx.getParents(*MaybeGuardian);
87e8d8bef9SDimitry Andric        !guardianAncestors.empty();
88e8d8bef9SDimitry Andric        guardianAncestors = ctx.getParents(
89e8d8bef9SDimitry Andric            *guardianAncestors
90e8d8bef9SDimitry Andric                 .begin()) // FIXME - should we handle all of the parents?
91e8d8bef9SDimitry Andric   ) {
92e8d8bef9SDimitry Andric     for (auto &guardianAncestor : guardianAncestors) {
93e8d8bef9SDimitry Andric       if (auto *CStmtParentAncestor = guardianAncestor.get<CompoundStmt>()) {
94e8d8bef9SDimitry Andric         guardiansClosestCompStmtAncestor = CStmtParentAncestor;
95e8d8bef9SDimitry Andric         break;
96e8d8bef9SDimitry Andric       }
97e8d8bef9SDimitry Andric     }
98e8d8bef9SDimitry Andric     if (guardiansClosestCompStmtAncestor)
99e8d8bef9SDimitry Andric       break;
100e8d8bef9SDimitry Andric   }
101e8d8bef9SDimitry Andric 
102e8d8bef9SDimitry Andric   if (!guardiansClosestCompStmtAncestor)
103e8d8bef9SDimitry Andric     return false;
104e8d8bef9SDimitry Andric 
105e8d8bef9SDimitry Andric   // We need to skip the first CompoundStmt to avoid situation when guardian is
106e8d8bef9SDimitry Andric   // defined in the same scope as guarded variable.
107e8d8bef9SDimitry Andric   bool HaveSkippedFirstCompoundStmt = false;
108e8d8bef9SDimitry Andric   for (DynTypedNodeList guardedVarAncestors = ctx.getParents(*Guarded);
109e8d8bef9SDimitry Andric        !guardedVarAncestors.empty();
110e8d8bef9SDimitry Andric        guardedVarAncestors = ctx.getParents(
111e8d8bef9SDimitry Andric            *guardedVarAncestors
112e8d8bef9SDimitry Andric                 .begin()) // FIXME - should we handle all of the parents?
113e8d8bef9SDimitry Andric   ) {
114e8d8bef9SDimitry Andric     for (auto &guardedVarAncestor : guardedVarAncestors) {
115e8d8bef9SDimitry Andric       if (auto *CStmtAncestor = guardedVarAncestor.get<CompoundStmt>()) {
116e8d8bef9SDimitry Andric         if (!HaveSkippedFirstCompoundStmt) {
117e8d8bef9SDimitry Andric           HaveSkippedFirstCompoundStmt = true;
118e8d8bef9SDimitry Andric           continue;
119e8d8bef9SDimitry Andric         }
120e8d8bef9SDimitry Andric         if (CStmtAncestor == guardiansClosestCompStmtAncestor)
121e8d8bef9SDimitry Andric           return true;
122e8d8bef9SDimitry Andric       }
123e8d8bef9SDimitry Andric     }
124e8d8bef9SDimitry Andric   }
125e8d8bef9SDimitry Andric 
126e8d8bef9SDimitry Andric   return false;
127e8d8bef9SDimitry Andric }
128e8d8bef9SDimitry Andric 
129e8d8bef9SDimitry Andric class UncountedLocalVarsChecker
130e8d8bef9SDimitry Andric     : public Checker<check::ASTDecl<TranslationUnitDecl>> {
131e8d8bef9SDimitry Andric   BugType Bug{this,
132e8d8bef9SDimitry Andric               "Uncounted raw pointer or reference not provably backed by "
133e8d8bef9SDimitry Andric               "ref-counted variable",
134e8d8bef9SDimitry Andric               "WebKit coding guidelines"};
135e8d8bef9SDimitry Andric   mutable BugReporter *BR;
136e8d8bef9SDimitry Andric 
137e8d8bef9SDimitry Andric public:
138e8d8bef9SDimitry Andric   void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
139e8d8bef9SDimitry Andric                     BugReporter &BRArg) const {
140e8d8bef9SDimitry Andric     BR = &BRArg;
141e8d8bef9SDimitry Andric 
142e8d8bef9SDimitry Andric     // The calls to checkAST* from AnalysisConsumer don't
143e8d8bef9SDimitry Andric     // visit template instantiations or lambda classes. We
144e8d8bef9SDimitry Andric     // want to visit those, so we make our own RecursiveASTVisitor.
145e8d8bef9SDimitry Andric     struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
146e8d8bef9SDimitry Andric       const UncountedLocalVarsChecker *Checker;
147e8d8bef9SDimitry Andric       explicit LocalVisitor(const UncountedLocalVarsChecker *Checker)
148e8d8bef9SDimitry Andric           : Checker(Checker) {
149e8d8bef9SDimitry Andric         assert(Checker);
150e8d8bef9SDimitry Andric       }
151e8d8bef9SDimitry Andric 
152e8d8bef9SDimitry Andric       bool shouldVisitTemplateInstantiations() const { return true; }
153e8d8bef9SDimitry Andric       bool shouldVisitImplicitCode() const { return false; }
154e8d8bef9SDimitry Andric 
155e8d8bef9SDimitry Andric       bool VisitVarDecl(VarDecl *V) {
156e8d8bef9SDimitry Andric         Checker->visitVarDecl(V);
157e8d8bef9SDimitry Andric         return true;
158e8d8bef9SDimitry Andric       }
159e8d8bef9SDimitry Andric     };
160e8d8bef9SDimitry Andric 
161e8d8bef9SDimitry Andric     LocalVisitor visitor(this);
162e8d8bef9SDimitry Andric     visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
163e8d8bef9SDimitry Andric   }
164e8d8bef9SDimitry Andric 
165e8d8bef9SDimitry Andric   void visitVarDecl(const VarDecl *V) const {
166e8d8bef9SDimitry Andric     if (shouldSkipVarDecl(V))
167e8d8bef9SDimitry Andric       return;
168e8d8bef9SDimitry Andric 
169e8d8bef9SDimitry Andric     const auto *ArgType = V->getType().getTypePtr();
170e8d8bef9SDimitry Andric     if (!ArgType)
171e8d8bef9SDimitry Andric       return;
172e8d8bef9SDimitry Andric 
173*bdd1243dSDimitry Andric     std::optional<bool> IsUncountedPtr = isUncountedPtr(ArgType);
174e8d8bef9SDimitry Andric     if (IsUncountedPtr && *IsUncountedPtr) {
175e8d8bef9SDimitry Andric       const Expr *const InitExpr = V->getInit();
176e8d8bef9SDimitry Andric       if (!InitExpr)
177e8d8bef9SDimitry Andric         return; // FIXME: later on we might warn on uninitialized vars too
178e8d8bef9SDimitry Andric 
179e8d8bef9SDimitry Andric       const clang::Expr *const InitArgOrigin =
180e8d8bef9SDimitry Andric           tryToFindPtrOrigin(InitExpr, /*StopAtFirstRefCountedObj=*/false)
181e8d8bef9SDimitry Andric               .first;
182e8d8bef9SDimitry Andric       if (!InitArgOrigin)
183e8d8bef9SDimitry Andric         return;
184e8d8bef9SDimitry Andric 
185e8d8bef9SDimitry Andric       if (isa<CXXThisExpr>(InitArgOrigin))
186e8d8bef9SDimitry Andric         return;
187e8d8bef9SDimitry Andric 
188e8d8bef9SDimitry Andric       if (auto *Ref = llvm::dyn_cast<DeclRefExpr>(InitArgOrigin)) {
189e8d8bef9SDimitry Andric         if (auto *MaybeGuardian =
190e8d8bef9SDimitry Andric                 dyn_cast_or_null<VarDecl>(Ref->getFoundDecl())) {
191e8d8bef9SDimitry Andric           const auto *MaybeGuardianArgType =
192e8d8bef9SDimitry Andric               MaybeGuardian->getType().getTypePtr();
193e8d8bef9SDimitry Andric           if (!MaybeGuardianArgType)
194e8d8bef9SDimitry Andric             return;
195e8d8bef9SDimitry Andric           const CXXRecordDecl *const MaybeGuardianArgCXXRecord =
196e8d8bef9SDimitry Andric               MaybeGuardianArgType->getAsCXXRecordDecl();
197e8d8bef9SDimitry Andric           if (!MaybeGuardianArgCXXRecord)
198e8d8bef9SDimitry Andric             return;
199e8d8bef9SDimitry Andric 
200e8d8bef9SDimitry Andric           if (MaybeGuardian->isLocalVarDecl() &&
201e8d8bef9SDimitry Andric               (isRefCounted(MaybeGuardianArgCXXRecord) ||
202e8d8bef9SDimitry Andric                isRefcountedStringsHack(MaybeGuardian)) &&
203e8d8bef9SDimitry Andric               isGuardedScopeEmbeddedInGuardianScope(V, MaybeGuardian)) {
204e8d8bef9SDimitry Andric             return;
205e8d8bef9SDimitry Andric           }
206e8d8bef9SDimitry Andric 
207e8d8bef9SDimitry Andric           // Parameters are guaranteed to be safe for the duration of the call
208e8d8bef9SDimitry Andric           // by another checker.
209e8d8bef9SDimitry Andric           if (isa<ParmVarDecl>(MaybeGuardian))
210e8d8bef9SDimitry Andric             return;
211e8d8bef9SDimitry Andric         }
212e8d8bef9SDimitry Andric       }
213e8d8bef9SDimitry Andric 
214e8d8bef9SDimitry Andric       reportBug(V);
215e8d8bef9SDimitry Andric     }
216e8d8bef9SDimitry Andric   }
217e8d8bef9SDimitry Andric 
218e8d8bef9SDimitry Andric   bool shouldSkipVarDecl(const VarDecl *V) const {
219e8d8bef9SDimitry Andric     assert(V);
220e8d8bef9SDimitry Andric     if (!V->isLocalVarDecl())
221e8d8bef9SDimitry Andric       return true;
222e8d8bef9SDimitry Andric 
223e8d8bef9SDimitry Andric     if (isDeclaredInForOrIf(V))
224e8d8bef9SDimitry Andric       return true;
225e8d8bef9SDimitry Andric 
226e8d8bef9SDimitry Andric     return false;
227e8d8bef9SDimitry Andric   }
228e8d8bef9SDimitry Andric 
229e8d8bef9SDimitry Andric   void reportBug(const VarDecl *V) const {
230e8d8bef9SDimitry Andric     assert(V);
231e8d8bef9SDimitry Andric     SmallString<100> Buf;
232e8d8bef9SDimitry Andric     llvm::raw_svector_ostream Os(Buf);
233e8d8bef9SDimitry Andric 
234e8d8bef9SDimitry Andric     Os << "Local variable ";
235e8d8bef9SDimitry Andric     printQuotedQualifiedName(Os, V);
236e8d8bef9SDimitry Andric     Os << " is uncounted and unsafe.";
237e8d8bef9SDimitry Andric 
238e8d8bef9SDimitry Andric     PathDiagnosticLocation BSLoc(V->getLocation(), BR->getSourceManager());
239e8d8bef9SDimitry Andric     auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
240e8d8bef9SDimitry Andric     Report->addRange(V->getSourceRange());
241e8d8bef9SDimitry Andric     BR->emitReport(std::move(Report));
242e8d8bef9SDimitry Andric   }
243e8d8bef9SDimitry Andric };
244e8d8bef9SDimitry Andric } // namespace
245e8d8bef9SDimitry Andric 
246e8d8bef9SDimitry Andric void ento::registerUncountedLocalVarsChecker(CheckerManager &Mgr) {
247e8d8bef9SDimitry Andric   Mgr.registerChecker<UncountedLocalVarsChecker>();
248e8d8bef9SDimitry Andric }
249e8d8bef9SDimitry Andric 
250e8d8bef9SDimitry Andric bool ento::shouldRegisterUncountedLocalVarsChecker(const CheckerManager &) {
251e8d8bef9SDimitry Andric   return true;
252e8d8bef9SDimitry Andric }
253