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