brintos

brintos / llvm-project-archived public Read only

0
0
Text · 15.9 KiB · c13df47 Raw
480 lines · cpp
1//=======- UncountedLocalVarsChecker.cpp -------------------------*- C++ -*-==//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "ASTUtils.h"10#include "DiagOutputUtils.h"11#include "PtrTypesSemantics.h"12#include "clang/AST/CXXInheritance.h"13#include "clang/AST/Decl.h"14#include "clang/AST/DeclCXX.h"15#include "clang/AST/DynamicRecursiveASTVisitor.h"16#include "clang/AST/ParentMapContext.h"17#include "clang/Analysis/DomainSpecific/CocoaConventions.h"18#include "clang/Basic/SourceLocation.h"19#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"20#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"21#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"22#include "clang/StaticAnalyzer/Core/Checker.h"23#include <optional>24 25using namespace clang;26using namespace ento;27 28namespace {29 30// FIXME: should be defined by anotations in the future31bool isRefcountedStringsHack(const VarDecl *V) {32  assert(V);33  auto safeClass = [](const std::string &className) {34    return className == "String" || className == "AtomString" ||35           className == "UniquedString" || className == "Identifier";36  };37  QualType QT = V->getType();38  auto *T = QT.getTypePtr();39  if (auto *CXXRD = T->getAsCXXRecordDecl()) {40    if (safeClass(safeGetName(CXXRD)))41      return true;42  }43  if (T->isPointerType() || T->isReferenceType()) {44    if (auto *CXXRD = T->getPointeeCXXRecordDecl()) {45      if (safeClass(safeGetName(CXXRD)))46        return true;47    }48  }49  return false;50}51 52struct GuardianVisitor : DynamicRecursiveASTVisitor {53  const VarDecl *Guardian{nullptr};54 55  explicit GuardianVisitor(const VarDecl *Guardian) : Guardian(Guardian) {56    assert(Guardian);57  }58 59  bool VisitBinaryOperator(BinaryOperator *BO) override {60    if (BO->isAssignmentOp()) {61      if (auto *VarRef = dyn_cast<DeclRefExpr>(BO->getLHS())) {62        if (VarRef->getDecl() == Guardian)63          return false;64      }65    }66    return true;67  }68 69  bool VisitCXXConstructExpr(CXXConstructExpr *CE) override {70    if (auto *Ctor = CE->getConstructor()) {71      if (Ctor->isMoveConstructor() && CE->getNumArgs() == 1) {72        auto *Arg = CE->getArg(0)->IgnoreParenCasts();73        if (auto *VarRef = dyn_cast<DeclRefExpr>(Arg)) {74          if (VarRef->getDecl() == Guardian)75            return false;76        }77      }78    }79    return true;80  }81 82  bool VisitCXXMemberCallExpr(CXXMemberCallExpr *MCE) override {83    auto MethodName = safeGetName(MCE->getMethodDecl());84    if (MethodName == "swap" || MethodName == "leakRef" ||85        MethodName == "releaseNonNull" || MethodName == "clear") {86      auto *ThisArg = MCE->getImplicitObjectArgument()->IgnoreParenCasts();87      if (auto *VarRef = dyn_cast<DeclRefExpr>(ThisArg)) {88        if (VarRef->getDecl() == Guardian)89          return false;90      }91    }92    return true;93  }94 95  bool VisitCXXOperatorCallExpr(CXXOperatorCallExpr *OCE) override {96    if (OCE->isAssignmentOp()) {97      assert(OCE->getNumArgs() == 2);98      auto *ThisArg = OCE->getArg(0)->IgnoreParenCasts();99      if (auto *VarRef = dyn_cast<DeclRefExpr>(ThisArg)) {100        if (VarRef->getDecl() == Guardian)101          return false;102      }103    }104    return true;105  }106};107 108bool isGuardedScopeEmbeddedInGuardianScope(const VarDecl *Guarded,109                                           const VarDecl *MaybeGuardian) {110  assert(Guarded);111  assert(MaybeGuardian);112 113  if (!MaybeGuardian->isLocalVarDecl())114    return false;115 116  const CompoundStmt *guardiansClosestCompStmtAncestor = nullptr;117 118  ASTContext &ctx = MaybeGuardian->getASTContext();119 120  for (DynTypedNodeList guardianAncestors = ctx.getParents(*MaybeGuardian);121       !guardianAncestors.empty();122       guardianAncestors = ctx.getParents(123           *guardianAncestors124                .begin()) // FIXME - should we handle all of the parents?125  ) {126    for (auto &guardianAncestor : guardianAncestors) {127      if (auto *CStmtParentAncestor = guardianAncestor.get<CompoundStmt>()) {128        guardiansClosestCompStmtAncestor = CStmtParentAncestor;129        break;130      }131    }132    if (guardiansClosestCompStmtAncestor)133      break;134  }135 136  if (!guardiansClosestCompStmtAncestor)137    return false;138 139  // We need to skip the first CompoundStmt to avoid situation when guardian is140  // defined in the same scope as guarded variable.141  const CompoundStmt *FirstCompondStmt = nullptr;142  for (DynTypedNodeList guardedVarAncestors = ctx.getParents(*Guarded);143       !guardedVarAncestors.empty();144       guardedVarAncestors = ctx.getParents(145           *guardedVarAncestors146                .begin()) // FIXME - should we handle all of the parents?147  ) {148    for (auto &guardedVarAncestor : guardedVarAncestors) {149      if (auto *CStmtAncestor = guardedVarAncestor.get<CompoundStmt>()) {150        if (!FirstCompondStmt) {151          FirstCompondStmt = CStmtAncestor;152          continue;153        }154        if (CStmtAncestor == guardiansClosestCompStmtAncestor) {155          GuardianVisitor guardianVisitor(MaybeGuardian);156          auto *GuardedScope = const_cast<CompoundStmt *>(FirstCompondStmt);157          return guardianVisitor.TraverseCompoundStmt(GuardedScope);158        }159      }160    }161  }162 163  return false;164}165 166class RawPtrRefLocalVarsChecker167    : public Checker<check::ASTDecl<TranslationUnitDecl>> {168  BugType Bug;169  EnsureFunctionAnalysis EFA;170 171protected:172  mutable BugReporter *BR;173  mutable std::optional<RetainTypeChecker> RTC;174 175public:176  RawPtrRefLocalVarsChecker(const char *description)177      : Bug(this, description, "WebKit coding guidelines") {}178 179  virtual std::optional<bool> isUnsafePtr(const QualType T) const = 0;180  virtual bool isSafePtr(const CXXRecordDecl *) const = 0;181  virtual bool isSafePtrType(const QualType) const = 0;182  virtual bool isSafeExpr(const Expr *) const { return false; }183  virtual bool isSafeDecl(const Decl *) const { return false; }184  virtual const char *ptrKind() const = 0;185 186  void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,187                    BugReporter &BRArg) const {188    BR = &BRArg;189 190    // The calls to checkAST* from AnalysisConsumer don't191    // visit template instantiations or lambda classes. We192    // want to visit those, so we make our own RecursiveASTVisitor.193    struct LocalVisitor : DynamicRecursiveASTVisitor {194      const RawPtrRefLocalVarsChecker *Checker;195      Decl *DeclWithIssue{nullptr};196 197      TrivialFunctionAnalysis TFA;198 199      explicit LocalVisitor(const RawPtrRefLocalVarsChecker *Checker)200          : Checker(Checker) {201        assert(Checker);202        ShouldVisitTemplateInstantiations = true;203        ShouldVisitImplicitCode = false;204      }205 206      bool TraverseDecl(Decl *D) override {207        llvm::SaveAndRestore SavedDecl(DeclWithIssue);208        if (D && (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)))209          DeclWithIssue = D;210        return DynamicRecursiveASTVisitor::TraverseDecl(D);211      }212 213      bool VisitTypedefDecl(TypedefDecl *TD) override {214        if (Checker->RTC)215          Checker->RTC->visitTypedef(TD);216        return true;217      }218 219      bool VisitVarDecl(VarDecl *V) override {220        auto *Init = V->getInit();221        if (Init && V->isLocalVarDecl())222          Checker->visitVarDecl(V, Init, DeclWithIssue);223        return true;224      }225 226      bool VisitBinaryOperator(BinaryOperator *BO) override {227        if (BO->isAssignmentOp()) {228          if (auto *VarRef = dyn_cast<DeclRefExpr>(BO->getLHS())) {229            if (auto *V = dyn_cast<VarDecl>(VarRef->getDecl()))230              Checker->visitVarDecl(V, BO->getRHS(), DeclWithIssue);231          }232        }233        return true;234      }235 236      bool TraverseIfStmt(IfStmt *IS) override {237        if (!TFA.isTrivial(IS))238          return DynamicRecursiveASTVisitor::TraverseIfStmt(IS);239        return true;240      }241 242      bool TraverseForStmt(ForStmt *FS) override {243        if (!TFA.isTrivial(FS))244          return DynamicRecursiveASTVisitor::TraverseForStmt(FS);245        return true;246      }247 248      bool TraverseCXXForRangeStmt(CXXForRangeStmt *FRS) override {249        if (!TFA.isTrivial(FRS))250          return DynamicRecursiveASTVisitor::TraverseCXXForRangeStmt(FRS);251        return true;252      }253 254      bool TraverseWhileStmt(WhileStmt *WS) override {255        if (!TFA.isTrivial(WS))256          return DynamicRecursiveASTVisitor::TraverseWhileStmt(WS);257        return true;258      }259 260      bool TraverseCompoundStmt(CompoundStmt *CS) override {261        if (!TFA.isTrivial(CS))262          return DynamicRecursiveASTVisitor::TraverseCompoundStmt(CS);263        return true;264      }265 266      bool TraverseClassTemplateDecl(ClassTemplateDecl *Decl) override {267        if (isSmartPtrClass(safeGetName(Decl)))268          return true;269        return DynamicRecursiveASTVisitor::TraverseClassTemplateDecl(Decl);270      }271    };272 273    LocalVisitor visitor(this);274    if (RTC)275      RTC->visitTranslationUnitDecl(TUD);276    visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));277  }278 279  void visitVarDecl(const VarDecl *V, const Expr *Value,280                    const Decl *DeclWithIssue) const {281    if (shouldSkipVarDecl(V))282      return;283 284    std::optional<bool> IsUncountedPtr = isUnsafePtr(V->getType());285    if (IsUncountedPtr && *IsUncountedPtr) {286      if (tryToFindPtrOrigin(287              Value, /*StopAtFirstRefCountedObj=*/false,288              [&](const clang::CXXRecordDecl *Record) {289                return isSafePtr(Record);290              },291              [&](const clang::QualType Type) { return isSafePtrType(Type); },292              [&](const clang::Decl *D) { return isSafeDecl(D); },293              [&](const clang::Expr *InitArgOrigin, bool IsSafe) {294                if (!InitArgOrigin || IsSafe)295                  return true;296 297                if (isa<CXXThisExpr>(InitArgOrigin))298                  return true;299 300                if (isNullPtr(InitArgOrigin))301                  return true;302 303                if (isa<IntegerLiteral>(InitArgOrigin))304                  return true;305 306                if (isConstOwnerPtrMemberExpr(InitArgOrigin))307                  return true;308 309                if (EFA.isACallToEnsureFn(InitArgOrigin))310                  return true;311 312                if (isSafeExpr(InitArgOrigin))313                  return true;314 315                if (auto *Ref = llvm::dyn_cast<DeclRefExpr>(InitArgOrigin)) {316                  if (auto *MaybeGuardian =317                          dyn_cast_or_null<VarDecl>(Ref->getFoundDecl())) {318                    const auto *MaybeGuardianArgType =319                        MaybeGuardian->getType().getTypePtr();320                    if (MaybeGuardianArgType) {321                      const CXXRecordDecl *const MaybeGuardianArgCXXRecord =322                          MaybeGuardianArgType->getAsCXXRecordDecl();323                      if (MaybeGuardianArgCXXRecord) {324                        if (MaybeGuardian->isLocalVarDecl() &&325                            (isSafePtr(MaybeGuardianArgCXXRecord) ||326                             isRefcountedStringsHack(MaybeGuardian)) &&327                            isGuardedScopeEmbeddedInGuardianScope(328                                V, MaybeGuardian))329                          return true;330                      }331                    }332 333                    // Parameters are guaranteed to be safe for the duration of334                    // the call by another checker.335                    if (isa<ParmVarDecl>(MaybeGuardian))336                      return true;337                  }338                }339 340                return false;341              }))342        return;343 344      reportBug(V, Value, DeclWithIssue);345    }346  }347 348  bool shouldSkipVarDecl(const VarDecl *V) const {349    assert(V);350    if (isa<ImplicitParamDecl>(V))351      return true;352    return BR->getSourceManager().isInSystemHeader(V->getLocation());353  }354 355  void reportBug(const VarDecl *V, const Expr *Value,356                 const Decl *DeclWithIssue) const {357    assert(V);358    SmallString<100> Buf;359    llvm::raw_svector_ostream Os(Buf);360 361    if (isa<ParmVarDecl>(V)) {362      Os << "Assignment to an " << ptrKind() << " parameter ";363      printQuotedQualifiedName(Os, V);364      Os << " is unsafe.";365 366      PathDiagnosticLocation BSLoc(Value->getExprLoc(), BR->getSourceManager());367      auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);368      Report->addRange(Value->getSourceRange());369      BR->emitReport(std::move(Report));370    } else {371      if (V->hasLocalStorage())372        Os << "Local variable ";373      else if (V->isStaticLocal())374        Os << "Static local variable ";375      else if (V->hasGlobalStorage())376        Os << "Global variable ";377      else378        Os << "Variable ";379      printQuotedQualifiedName(Os, V);380      Os << " is " << ptrKind() << " and unsafe.";381 382      PathDiagnosticLocation BSLoc(V->getLocation(), BR->getSourceManager());383      auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);384      Report->addRange(V->getSourceRange());385      Report->setDeclWithIssue(DeclWithIssue);386      BR->emitReport(std::move(Report));387    }388  }389};390 391class UncountedLocalVarsChecker final : public RawPtrRefLocalVarsChecker {392public:393  UncountedLocalVarsChecker()394      : RawPtrRefLocalVarsChecker("Uncounted raw pointer or reference not "395                                  "provably backed by ref-counted variable") {}396  std::optional<bool> isUnsafePtr(const QualType T) const final {397    return isUncountedPtr(T);398  }399  bool isSafePtr(const CXXRecordDecl *Record) const final {400    return isRefCounted(Record) || isCheckedPtr(Record);401  }402  bool isSafePtrType(const QualType type) const final {403    return isRefOrCheckedPtrType(type);404  }405  const char *ptrKind() const final { return "uncounted"; }406};407 408class UncheckedLocalVarsChecker final : public RawPtrRefLocalVarsChecker {409public:410  UncheckedLocalVarsChecker()411      : RawPtrRefLocalVarsChecker("Unchecked raw pointer or reference not "412                                  "provably backed by checked variable") {}413  std::optional<bool> isUnsafePtr(const QualType T) const final {414    return isUncheckedPtr(T);415  }416  bool isSafePtr(const CXXRecordDecl *Record) const final {417    return isRefCounted(Record) || isCheckedPtr(Record);418  }419  bool isSafePtrType(const QualType type) const final {420    return isRefOrCheckedPtrType(type);421  }422  bool isSafeExpr(const Expr *E) const final {423    return isExprToGetCheckedPtrCapableMember(E);424  }425  const char *ptrKind() const final { return "unchecked"; }426};427 428class UnretainedLocalVarsChecker final : public RawPtrRefLocalVarsChecker {429public:430  UnretainedLocalVarsChecker()431      : RawPtrRefLocalVarsChecker("Unretained raw pointer or reference not "432                                  "provably backed by a RetainPtr") {433    RTC = RetainTypeChecker();434  }435  std::optional<bool> isUnsafePtr(const QualType T) const final {436    return RTC->isUnretained(T);437  }438  bool isSafePtr(const CXXRecordDecl *Record) const final {439    return isRetainPtrOrOSPtr(Record);440  }441  bool isSafePtrType(const QualType type) const final {442    return isRetainPtrOrOSPtrType(type);443  }444  bool isSafeExpr(const Expr *E) const final {445    return ento::cocoa::isCocoaObjectRef(E->getType()) &&446           isa<ObjCMessageExpr>(E);447  }448  bool isSafeDecl(const Decl *D) const final {449    // Treat NS/CF globals in system header as immortal.450    return BR->getSourceManager().isInSystemHeader(D->getLocation());451  }452  const char *ptrKind() const final { return "unretained"; }453};454 455} // namespace456 457void ento::registerUncountedLocalVarsChecker(CheckerManager &Mgr) {458  Mgr.registerChecker<UncountedLocalVarsChecker>();459}460 461bool ento::shouldRegisterUncountedLocalVarsChecker(const CheckerManager &) {462  return true;463}464 465void ento::registerUncheckedLocalVarsChecker(CheckerManager &Mgr) {466  Mgr.registerChecker<UncheckedLocalVarsChecker>();467}468 469bool ento::shouldRegisterUncheckedLocalVarsChecker(const CheckerManager &) {470  return true;471}472 473void ento::registerUnretainedLocalVarsChecker(CheckerManager &Mgr) {474  Mgr.registerChecker<UnretainedLocalVarsChecker>();475}476 477bool ento::shouldRegisterUnretainedLocalVarsChecker(const CheckerManager &) {478  return true;479}480