18a64689eSJan Korous //=======- UncountedLocalVarsChecker.cpp -------------------------*- C++ -*-==//
28a64689eSJan Korous //
38a64689eSJan Korous // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
48a64689eSJan Korous // See https://llvm.org/LICENSE.txt for license information.
58a64689eSJan Korous // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
68a64689eSJan Korous //
78a64689eSJan Korous //===----------------------------------------------------------------------===//
88a64689eSJan Korous 
98a64689eSJan Korous #include "ASTUtils.h"
108a64689eSJan Korous #include "DiagOutputUtils.h"
118a64689eSJan Korous #include "PtrTypesSemantics.h"
128a64689eSJan Korous #include "clang/AST/CXXInheritance.h"
138a64689eSJan Korous #include "clang/AST/Decl.h"
148a64689eSJan Korous #include "clang/AST/DeclCXX.h"
158a64689eSJan Korous #include "clang/AST/ParentMapContext.h"
168a64689eSJan Korous #include "clang/AST/RecursiveASTVisitor.h"
178a64689eSJan Korous #include "clang/Basic/SourceLocation.h"
188a64689eSJan Korous #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
198a64689eSJan Korous #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
208a64689eSJan Korous #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
218a64689eSJan Korous #include "clang/StaticAnalyzer/Core/Checker.h"
228a64689eSJan Korous #include "llvm/ADT/DenseSet.h"
238a64689eSJan Korous 
248a64689eSJan Korous using namespace clang;
258a64689eSJan Korous using namespace ento;
268a64689eSJan Korous 
278a64689eSJan Korous namespace {
288a64689eSJan Korous 
298a64689eSJan Korous // for ( int a = ...) ... true
308a64689eSJan Korous // for ( int a : ...) ... true
318a64689eSJan Korous // if ( int* a = ) ... true
328a64689eSJan Korous // anything else ... false
isDeclaredInForOrIf(const VarDecl * Var)338a64689eSJan Korous bool isDeclaredInForOrIf(const VarDecl *Var) {
348a64689eSJan Korous   assert(Var);
358a64689eSJan Korous   auto &ASTCtx = Var->getASTContext();
368a64689eSJan Korous   auto parent = ASTCtx.getParents(*Var);
378a64689eSJan Korous 
388a64689eSJan Korous   if (parent.size() == 1) {
398a64689eSJan Korous     if (auto *DS = parent.begin()->get<DeclStmt>()) {
408a64689eSJan Korous       DynTypedNodeList grandParent = ASTCtx.getParents(*DS);
418a64689eSJan Korous       if (grandParent.size() == 1) {
428a64689eSJan Korous         return grandParent.begin()->get<ForStmt>() ||
438a64689eSJan Korous                grandParent.begin()->get<IfStmt>() ||
448a64689eSJan Korous                grandParent.begin()->get<CXXForRangeStmt>();
458a64689eSJan Korous       }
468a64689eSJan Korous     }
478a64689eSJan Korous   }
488a64689eSJan Korous   return false;
498a64689eSJan Korous }
508a64689eSJan Korous 
518a64689eSJan Korous // FIXME: should be defined by anotations in the future
isRefcountedStringsHack(const VarDecl * V)528a64689eSJan Korous bool isRefcountedStringsHack(const VarDecl *V) {
538a64689eSJan Korous   assert(V);
548a64689eSJan Korous   auto safeClass = [](const std::string &className) {
558a64689eSJan Korous     return className == "String" || className == "AtomString" ||
568a64689eSJan Korous            className == "UniquedString" || className == "Identifier";
578a64689eSJan Korous   };
588a64689eSJan Korous   QualType QT = V->getType();
598a64689eSJan Korous   auto *T = QT.getTypePtr();
608a64689eSJan Korous   if (auto *CXXRD = T->getAsCXXRecordDecl()) {
618a64689eSJan Korous     if (safeClass(safeGetName(CXXRD)))
628a64689eSJan Korous       return true;
638a64689eSJan Korous   }
648a64689eSJan Korous   if (T->isPointerType() || T->isReferenceType()) {
658a64689eSJan Korous     if (auto *CXXRD = T->getPointeeCXXRecordDecl()) {
668a64689eSJan Korous       if (safeClass(safeGetName(CXXRD)))
678a64689eSJan Korous         return true;
688a64689eSJan Korous     }
698a64689eSJan Korous   }
708a64689eSJan Korous   return false;
718a64689eSJan Korous }
728a64689eSJan Korous 
isGuardedScopeEmbeddedInGuardianScope(const VarDecl * Guarded,const VarDecl * MaybeGuardian)738a64689eSJan Korous bool isGuardedScopeEmbeddedInGuardianScope(const VarDecl *Guarded,
748a64689eSJan Korous                                            const VarDecl *MaybeGuardian) {
758a64689eSJan Korous   assert(Guarded);
768a64689eSJan Korous   assert(MaybeGuardian);
778a64689eSJan Korous 
788a64689eSJan Korous   if (!MaybeGuardian->isLocalVarDecl())
798a64689eSJan Korous     return false;
808a64689eSJan Korous 
818a64689eSJan Korous   const CompoundStmt *guardiansClosestCompStmtAncestor = nullptr;
828a64689eSJan Korous 
838a64689eSJan Korous   ASTContext &ctx = MaybeGuardian->getASTContext();
848a64689eSJan Korous 
858a64689eSJan Korous   for (DynTypedNodeList guardianAncestors = ctx.getParents(*MaybeGuardian);
868a64689eSJan Korous        !guardianAncestors.empty();
878a64689eSJan Korous        guardianAncestors = ctx.getParents(
888a64689eSJan Korous            *guardianAncestors
898a64689eSJan Korous                 .begin()) // FIXME - should we handle all of the parents?
908a64689eSJan Korous   ) {
918a64689eSJan Korous     for (auto &guardianAncestor : guardianAncestors) {
928a64689eSJan Korous       if (auto *CStmtParentAncestor = guardianAncestor.get<CompoundStmt>()) {
938a64689eSJan Korous         guardiansClosestCompStmtAncestor = CStmtParentAncestor;
948a64689eSJan Korous         break;
958a64689eSJan Korous       }
968a64689eSJan Korous     }
978a64689eSJan Korous     if (guardiansClosestCompStmtAncestor)
988a64689eSJan Korous       break;
998a64689eSJan Korous   }
1008a64689eSJan Korous 
1018a64689eSJan Korous   if (!guardiansClosestCompStmtAncestor)
1028a64689eSJan Korous     return false;
1038a64689eSJan Korous 
1048a64689eSJan Korous   // We need to skip the first CompoundStmt to avoid situation when guardian is
1058a64689eSJan Korous   // defined in the same scope as guarded variable.
1068a64689eSJan Korous   bool HaveSkippedFirstCompoundStmt = false;
1078a64689eSJan Korous   for (DynTypedNodeList guardedVarAncestors = ctx.getParents(*Guarded);
1088a64689eSJan Korous        !guardedVarAncestors.empty();
1098a64689eSJan Korous        guardedVarAncestors = ctx.getParents(
1108a64689eSJan Korous            *guardedVarAncestors
1118a64689eSJan Korous                 .begin()) // FIXME - should we handle all of the parents?
1128a64689eSJan Korous   ) {
1138a64689eSJan Korous     for (auto &guardedVarAncestor : guardedVarAncestors) {
1148a64689eSJan Korous       if (auto *CStmtAncestor = guardedVarAncestor.get<CompoundStmt>()) {
1158a64689eSJan Korous         if (!HaveSkippedFirstCompoundStmt) {
1168a64689eSJan Korous           HaveSkippedFirstCompoundStmt = true;
1178a64689eSJan Korous           continue;
1188a64689eSJan Korous         }
1198a64689eSJan Korous         if (CStmtAncestor == guardiansClosestCompStmtAncestor)
1208a64689eSJan Korous           return true;
1218a64689eSJan Korous       }
1228a64689eSJan Korous     }
1238a64689eSJan Korous   }
1248a64689eSJan Korous 
1258a64689eSJan Korous   return false;
1268a64689eSJan Korous }
1278a64689eSJan Korous 
1288a64689eSJan Korous class UncountedLocalVarsChecker
1298a64689eSJan Korous     : public Checker<check::ASTDecl<TranslationUnitDecl>> {
1308a64689eSJan Korous   BugType Bug{this,
1318a64689eSJan Korous               "Uncounted raw pointer or reference not provably backed by "
1328a64689eSJan Korous               "ref-counted variable",
1338a64689eSJan Korous               "WebKit coding guidelines"};
1348a64689eSJan Korous   mutable BugReporter *BR;
1358a64689eSJan Korous 
1368a64689eSJan Korous public:
checkASTDecl(const TranslationUnitDecl * TUD,AnalysisManager & MGR,BugReporter & BRArg) const1378a64689eSJan Korous   void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
1388a64689eSJan Korous                     BugReporter &BRArg) const {
1398a64689eSJan Korous     BR = &BRArg;
1408a64689eSJan Korous 
1418a64689eSJan Korous     // The calls to checkAST* from AnalysisConsumer don't
1428a64689eSJan Korous     // visit template instantiations or lambda classes. We
1438a64689eSJan Korous     // want to visit those, so we make our own RecursiveASTVisitor.
1448a64689eSJan Korous     struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
1458a64689eSJan Korous       const UncountedLocalVarsChecker *Checker;
1468a64689eSJan Korous       explicit LocalVisitor(const UncountedLocalVarsChecker *Checker)
1478a64689eSJan Korous           : Checker(Checker) {
1488a64689eSJan Korous         assert(Checker);
1498a64689eSJan Korous       }
1508a64689eSJan Korous 
1518a64689eSJan Korous       bool shouldVisitTemplateInstantiations() const { return true; }
1528a64689eSJan Korous       bool shouldVisitImplicitCode() const { return false; }
1538a64689eSJan Korous 
1548a64689eSJan Korous       bool VisitVarDecl(VarDecl *V) {
1558a64689eSJan Korous         Checker->visitVarDecl(V);
1568a64689eSJan Korous         return true;
1578a64689eSJan Korous       }
1588a64689eSJan Korous     };
1598a64689eSJan Korous 
1608a64689eSJan Korous     LocalVisitor visitor(this);
1618a64689eSJan Korous     visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
1628a64689eSJan Korous   }
1638a64689eSJan Korous 
visitVarDecl(const VarDecl * V) const1648a64689eSJan Korous   void visitVarDecl(const VarDecl *V) const {
1658a64689eSJan Korous     if (shouldSkipVarDecl(V))
1668a64689eSJan Korous       return;
1678a64689eSJan Korous 
1688a64689eSJan Korous     const auto *ArgType = V->getType().getTypePtr();
1698a64689eSJan Korous     if (!ArgType)
1708a64689eSJan Korous       return;
1718a64689eSJan Korous 
172*47e68514SJan Korous     Optional<bool> IsUncountedPtr = isUncountedPtr(ArgType);
173*47e68514SJan Korous     if (IsUncountedPtr && *IsUncountedPtr) {
1748a64689eSJan Korous       const Expr *const InitExpr = V->getInit();
1758a64689eSJan Korous       if (!InitExpr)
1768a64689eSJan Korous         return; // FIXME: later on we might warn on uninitialized vars too
1778a64689eSJan Korous 
1788a64689eSJan Korous       const clang::Expr *const InitArgOrigin =
1798a64689eSJan Korous           tryToFindPtrOrigin(InitExpr, /*StopAtFirstRefCountedObj=*/false)
1808a64689eSJan Korous               .first;
1818a64689eSJan Korous       if (!InitArgOrigin)
1828a64689eSJan Korous         return;
1838a64689eSJan Korous 
1848a64689eSJan Korous       if (isa<CXXThisExpr>(InitArgOrigin))
1858a64689eSJan Korous         return;
1868a64689eSJan Korous 
1878a64689eSJan Korous       if (auto *Ref = llvm::dyn_cast<DeclRefExpr>(InitArgOrigin)) {
1888a64689eSJan Korous         if (auto *MaybeGuardian =
1898a64689eSJan Korous                 dyn_cast_or_null<VarDecl>(Ref->getFoundDecl())) {
1908a64689eSJan Korous           const auto *MaybeGuardianArgType =
1918a64689eSJan Korous               MaybeGuardian->getType().getTypePtr();
1928a64689eSJan Korous           if (!MaybeGuardianArgType)
1938a64689eSJan Korous             return;
1948a64689eSJan Korous           const CXXRecordDecl *const MaybeGuardianArgCXXRecord =
1958a64689eSJan Korous               MaybeGuardianArgType->getAsCXXRecordDecl();
1968a64689eSJan Korous           if (!MaybeGuardianArgCXXRecord)
1978a64689eSJan Korous             return;
1988a64689eSJan Korous 
1998a64689eSJan Korous           if (MaybeGuardian->isLocalVarDecl() &&
2008a64689eSJan Korous               (isRefCounted(MaybeGuardianArgCXXRecord) ||
2018a64689eSJan Korous                isRefcountedStringsHack(MaybeGuardian)) &&
2028a64689eSJan Korous               isGuardedScopeEmbeddedInGuardianScope(V, MaybeGuardian)) {
2038a64689eSJan Korous             return;
2048a64689eSJan Korous           }
2058a64689eSJan Korous 
2068a64689eSJan Korous           // Parameters are guaranteed to be safe for the duration of the call
2078a64689eSJan Korous           // by another checker.
2088a64689eSJan Korous           if (isa<ParmVarDecl>(MaybeGuardian))
2098a64689eSJan Korous             return;
2108a64689eSJan Korous         }
2118a64689eSJan Korous       }
2128a64689eSJan Korous 
2138a64689eSJan Korous       reportBug(V);
2148a64689eSJan Korous     }
2158a64689eSJan Korous   }
2168a64689eSJan Korous 
shouldSkipVarDecl(const VarDecl * V) const2178a64689eSJan Korous   bool shouldSkipVarDecl(const VarDecl *V) const {
2188a64689eSJan Korous     assert(V);
2198a64689eSJan Korous     if (!V->isLocalVarDecl())
2208a64689eSJan Korous       return true;
2218a64689eSJan Korous 
2228a64689eSJan Korous     if (isDeclaredInForOrIf(V))
2238a64689eSJan Korous       return true;
2248a64689eSJan Korous 
2258a64689eSJan Korous     return false;
2268a64689eSJan Korous   }
2278a64689eSJan Korous 
reportBug(const VarDecl * V) const2288a64689eSJan Korous   void reportBug(const VarDecl *V) const {
2298a64689eSJan Korous     assert(V);
2308a64689eSJan Korous     SmallString<100> Buf;
2318a64689eSJan Korous     llvm::raw_svector_ostream Os(Buf);
2328a64689eSJan Korous 
2338a64689eSJan Korous     Os << "Local variable ";
2348a64689eSJan Korous     printQuotedQualifiedName(Os, V);
2358a64689eSJan Korous     Os << " is uncounted and unsafe.";
2368a64689eSJan Korous 
2378a64689eSJan Korous     PathDiagnosticLocation BSLoc(V->getLocation(), BR->getSourceManager());
2388a64689eSJan Korous     auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
2398a64689eSJan Korous     Report->addRange(V->getSourceRange());
2408a64689eSJan Korous     BR->emitReport(std::move(Report));
2418a64689eSJan Korous   }
2428a64689eSJan Korous };
2438a64689eSJan Korous } // namespace
2448a64689eSJan Korous 
registerUncountedLocalVarsChecker(CheckerManager & Mgr)2458a64689eSJan Korous void ento::registerUncountedLocalVarsChecker(CheckerManager &Mgr) {
2468a64689eSJan Korous   Mgr.registerChecker<UncountedLocalVarsChecker>();
2478a64689eSJan Korous }
2488a64689eSJan Korous 
shouldRegisterUncountedLocalVarsChecker(const CheckerManager &)2498a64689eSJan Korous bool ento::shouldRegisterUncountedLocalVarsChecker(const CheckerManager &) {
2508a64689eSJan Korous   return true;
2518a64689eSJan Korous }
252