diff options
Diffstat (limited to 'contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLocalVarsChecker.cpp')
-rw-r--r-- | contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLocalVarsChecker.cpp | 293 |
1 files changed, 293 insertions, 0 deletions
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLocalVarsChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLocalVarsChecker.cpp new file mode 100644 index 000000000000..274da0baf2ce --- /dev/null +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLocalVarsChecker.cpp @@ -0,0 +1,293 @@ +//=======- UncountedLocalVarsChecker.cpp -------------------------*- C++ -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ASTUtils.h" +#include "DiagOutputUtils.h" +#include "PtrTypesSemantics.h" +#include "clang/AST/CXXInheritance.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/ParentMapContext.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include <optional> + +using namespace clang; +using namespace ento; + +namespace { + +// FIXME: should be defined by anotations in the future +bool isRefcountedStringsHack(const VarDecl *V) { + assert(V); + auto safeClass = [](const std::string &className) { + return className == "String" || className == "AtomString" || + className == "UniquedString" || className == "Identifier"; + }; + QualType QT = V->getType(); + auto *T = QT.getTypePtr(); + if (auto *CXXRD = T->getAsCXXRecordDecl()) { + if (safeClass(safeGetName(CXXRD))) + return true; + } + if (T->isPointerType() || T->isReferenceType()) { + if (auto *CXXRD = T->getPointeeCXXRecordDecl()) { + if (safeClass(safeGetName(CXXRD))) + return true; + } + } + return false; +} + +bool isGuardedScopeEmbeddedInGuardianScope(const VarDecl *Guarded, + const VarDecl *MaybeGuardian) { + assert(Guarded); + assert(MaybeGuardian); + + if (!MaybeGuardian->isLocalVarDecl()) + return false; + + const CompoundStmt *guardiansClosestCompStmtAncestor = nullptr; + + ASTContext &ctx = MaybeGuardian->getASTContext(); + + for (DynTypedNodeList guardianAncestors = ctx.getParents(*MaybeGuardian); + !guardianAncestors.empty(); + guardianAncestors = ctx.getParents( + *guardianAncestors + .begin()) // FIXME - should we handle all of the parents? + ) { + for (auto &guardianAncestor : guardianAncestors) { + if (auto *CStmtParentAncestor = guardianAncestor.get<CompoundStmt>()) { + guardiansClosestCompStmtAncestor = CStmtParentAncestor; + break; + } + } + if (guardiansClosestCompStmtAncestor) + break; + } + + if (!guardiansClosestCompStmtAncestor) + return false; + + // We need to skip the first CompoundStmt to avoid situation when guardian is + // defined in the same scope as guarded variable. + bool HaveSkippedFirstCompoundStmt = false; + for (DynTypedNodeList guardedVarAncestors = ctx.getParents(*Guarded); + !guardedVarAncestors.empty(); + guardedVarAncestors = ctx.getParents( + *guardedVarAncestors + .begin()) // FIXME - should we handle all of the parents? + ) { + for (auto &guardedVarAncestor : guardedVarAncestors) { + if (auto *CStmtAncestor = guardedVarAncestor.get<CompoundStmt>()) { + if (!HaveSkippedFirstCompoundStmt) { + HaveSkippedFirstCompoundStmt = true; + continue; + } + if (CStmtAncestor == guardiansClosestCompStmtAncestor) + return true; + } + } + } + + return false; +} + +class UncountedLocalVarsChecker + : public Checker<check::ASTDecl<TranslationUnitDecl>> { + BugType Bug{this, + "Uncounted raw pointer or reference not provably backed by " + "ref-counted variable", + "WebKit coding guidelines"}; + mutable BugReporter *BR; + +public: + void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR, + BugReporter &BRArg) const { + BR = &BRArg; + + // The calls to checkAST* from AnalysisConsumer don't + // visit template instantiations or lambda classes. We + // want to visit those, so we make our own RecursiveASTVisitor. + struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> { + const UncountedLocalVarsChecker *Checker; + + TrivialFunctionAnalysis TFA; + + using Base = RecursiveASTVisitor<LocalVisitor>; + + explicit LocalVisitor(const UncountedLocalVarsChecker *Checker) + : Checker(Checker) { + assert(Checker); + } + + bool shouldVisitTemplateInstantiations() const { return true; } + bool shouldVisitImplicitCode() const { return false; } + + bool VisitVarDecl(VarDecl *V) { + auto *Init = V->getInit(); + if (Init && V->isLocalVarDecl()) + Checker->visitVarDecl(V, Init); + return true; + } + + bool VisitBinaryOperator(const BinaryOperator *BO) { + if (BO->isAssignmentOp()) { + if (auto *VarRef = dyn_cast<DeclRefExpr>(BO->getLHS())) { + if (auto *V = dyn_cast<VarDecl>(VarRef->getDecl())) + Checker->visitVarDecl(V, BO->getRHS()); + } + } + return true; + } + + bool TraverseIfStmt(IfStmt *IS) { + if (!TFA.isTrivial(IS)) + return Base::TraverseIfStmt(IS); + return true; + } + + bool TraverseForStmt(ForStmt *FS) { + if (!TFA.isTrivial(FS)) + return Base::TraverseForStmt(FS); + return true; + } + + bool TraverseCXXForRangeStmt(CXXForRangeStmt *FRS) { + if (!TFA.isTrivial(FRS)) + return Base::TraverseCXXForRangeStmt(FRS); + return true; + } + + bool TraverseWhileStmt(WhileStmt *WS) { + if (!TFA.isTrivial(WS)) + return Base::TraverseWhileStmt(WS); + return true; + } + + bool TraverseCompoundStmt(CompoundStmt *CS) { + if (!TFA.isTrivial(CS)) + return Base::TraverseCompoundStmt(CS); + return true; + } + }; + + LocalVisitor visitor(this); + visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD)); + } + + void visitVarDecl(const VarDecl *V, const Expr *Value) const { + if (shouldSkipVarDecl(V)) + return; + + const auto *ArgType = V->getType().getTypePtr(); + if (!ArgType) + return; + + std::optional<bool> IsUncountedPtr = isUncountedPtr(ArgType); + if (IsUncountedPtr && *IsUncountedPtr) { + if (tryToFindPtrOrigin( + Value, /*StopAtFirstRefCountedObj=*/false, + [&](const clang::Expr *InitArgOrigin, bool IsSafe) { + if (!InitArgOrigin) + return true; + + if (isa<CXXThisExpr>(InitArgOrigin)) + return true; + + if (isa<CXXNullPtrLiteralExpr>(InitArgOrigin)) + return true; + + if (isa<IntegerLiteral>(InitArgOrigin)) + return true; + + if (auto *Ref = llvm::dyn_cast<DeclRefExpr>(InitArgOrigin)) { + if (auto *MaybeGuardian = + dyn_cast_or_null<VarDecl>(Ref->getFoundDecl())) { + const auto *MaybeGuardianArgType = + MaybeGuardian->getType().getTypePtr(); + if (MaybeGuardianArgType) { + const CXXRecordDecl *const MaybeGuardianArgCXXRecord = + MaybeGuardianArgType->getAsCXXRecordDecl(); + if (MaybeGuardianArgCXXRecord) { + if (MaybeGuardian->isLocalVarDecl() && + (isRefCounted(MaybeGuardianArgCXXRecord) || + isRefcountedStringsHack(MaybeGuardian)) && + isGuardedScopeEmbeddedInGuardianScope( + V, MaybeGuardian)) + return true; + } + } + + // Parameters are guaranteed to be safe for the duration of + // the call by another checker. + if (isa<ParmVarDecl>(MaybeGuardian)) + return true; + } + } + + return false; + })) + return; + + reportBug(V, Value); + } + } + + bool shouldSkipVarDecl(const VarDecl *V) const { + assert(V); + return BR->getSourceManager().isInSystemHeader(V->getLocation()); + } + + void reportBug(const VarDecl *V, const Expr *Value) const { + assert(V); + SmallString<100> Buf; + llvm::raw_svector_ostream Os(Buf); + + if (dyn_cast<ParmVarDecl>(V)) { + Os << "Assignment to an uncounted parameter "; + printQuotedQualifiedName(Os, V); + Os << " is unsafe."; + + PathDiagnosticLocation BSLoc(Value->getExprLoc(), BR->getSourceManager()); + auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc); + Report->addRange(Value->getSourceRange()); + BR->emitReport(std::move(Report)); + } else { + if (V->hasLocalStorage()) + Os << "Local variable "; + else if (V->isStaticLocal()) + Os << "Static local variable "; + else if (V->hasGlobalStorage()) + Os << "Global variable "; + else + Os << "Variable "; + printQuotedQualifiedName(Os, V); + Os << " is uncounted and unsafe."; + + PathDiagnosticLocation BSLoc(V->getLocation(), BR->getSourceManager()); + auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc); + Report->addRange(V->getSourceRange()); + BR->emitReport(std::move(Report)); + } + } +}; +} // namespace + +void ento::registerUncountedLocalVarsChecker(CheckerManager &Mgr) { + Mgr.registerChecker<UncountedLocalVarsChecker>(); +} + +bool ento::shouldRegisterUncountedLocalVarsChecker(const CheckerManager &) { + return true; +} |