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