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