brintos

brintos / llvm-project-archived public Read only

0
0
Text · 21.5 KiB · f60d193 Raw
619 lines · cpp
1//=======- UncountedLambdaCapturesChecker.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/DynamicRecursiveASTVisitor.h"13#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"14#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"15#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"16#include "clang/StaticAnalyzer/Core/Checker.h"17#include <optional>18 19using namespace clang;20using namespace ento;21 22namespace {23class RawPtrRefLambdaCapturesChecker24    : public Checker<check::ASTDecl<TranslationUnitDecl>> {25private:26  BugType Bug;27  mutable BugReporter *BR = nullptr;28  TrivialFunctionAnalysis TFA;29 30protected:31  mutable std::optional<RetainTypeChecker> RTC;32 33public:34  RawPtrRefLambdaCapturesChecker(const char *description)35      : Bug(this, description, "WebKit coding guidelines") {}36 37  virtual std::optional<bool> isUnsafePtr(QualType) const = 0;38  virtual bool isPtrType(const std::string &) const = 0;39  virtual const char *ptrKind(QualType QT) const = 0;40 41  void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,42                    BugReporter &BRArg) const {43    BR = &BRArg;44 45    // The calls to checkAST* from AnalysisConsumer don't46    // visit template instantiations or lambda classes. We47    // want to visit those, so we make our own RecursiveASTVisitor.48    struct LocalVisitor : DynamicRecursiveASTVisitor {49      const RawPtrRefLambdaCapturesChecker *Checker;50      llvm::DenseSet<const DeclRefExpr *> DeclRefExprsToIgnore;51      llvm::DenseSet<const LambdaExpr *> LambdasToIgnore;52      llvm::DenseSet<const ValueDecl *> ProtectedThisDecls;53      llvm::DenseSet<const CallExpr *> CallToIgnore;54      llvm::DenseSet<const CXXConstructExpr *> ConstructToIgnore;55      llvm::DenseMap<const VarDecl *, const LambdaExpr *> LambdaOwnerMap;56 57      QualType ClsType;58 59      explicit LocalVisitor(const RawPtrRefLambdaCapturesChecker *Checker)60          : Checker(Checker) {61        assert(Checker);62        ShouldVisitTemplateInstantiations = true;63        ShouldVisitImplicitCode = false;64      }65 66      bool TraverseCXXMethodDecl(CXXMethodDecl *CXXMD) override {67        llvm::SaveAndRestore SavedDecl(ClsType);68        if (CXXMD->isInstance())69          ClsType = CXXMD->getThisType();70        return DynamicRecursiveASTVisitor::TraverseCXXMethodDecl(CXXMD);71      }72 73      bool TraverseObjCMethodDecl(ObjCMethodDecl *OCMD) override {74        llvm::SaveAndRestore SavedDecl(ClsType);75        if (OCMD && OCMD->isInstanceMethod()) {76          if (auto *ImplParamDecl = OCMD->getSelfDecl())77            ClsType = ImplParamDecl->getType();78        }79        return DynamicRecursiveASTVisitor::TraverseObjCMethodDecl(OCMD);80      }81 82      bool VisitTypedefDecl(TypedefDecl *TD) override {83        if (Checker->RTC)84          Checker->RTC->visitTypedef(TD);85        return true;86      }87 88      bool shouldCheckThis() {89        auto result =90            !ClsType.isNull() ? Checker->isUnsafePtr(ClsType) : std::nullopt;91        return result && *result;92      }93 94      bool VisitLambdaExpr(LambdaExpr *L) override {95        if (LambdasToIgnore.contains(L))96          return true;97        Checker->visitLambdaExpr(L, shouldCheckThis() && !hasProtectedThis(L),98                                 ClsType);99        return true;100      }101 102      bool VisitVarDecl(VarDecl *VD) override {103        auto *Init = VD->getInit();104        if (!Init)105          return true;106        if (auto *L = dyn_cast_or_null<LambdaExpr>(Init->IgnoreParenCasts())) {107          LambdasToIgnore.insert(L); // Evaluate lambdas in VisitDeclRefExpr.108          return true;109        }110        if (!VD->hasLocalStorage())111          return true;112        if (auto *E = dyn_cast<ExprWithCleanups>(Init))113          Init = E->getSubExpr();114        if (auto *E = dyn_cast<CXXBindTemporaryExpr>(Init))115          Init = E->getSubExpr();116        if (auto *CE = dyn_cast<CallExpr>(Init)) {117          if (auto *Callee = CE->getDirectCallee()) {118            auto FnName = safeGetName(Callee);119            unsigned ArgCnt = CE->getNumArgs();120            if (FnName == "makeScopeExit" && ArgCnt == 1) {121              auto *Arg = CE->getArg(0);122              if (auto *E = dyn_cast<MaterializeTemporaryExpr>(Arg))123                Arg = E->getSubExpr();124              if (auto *L = dyn_cast<LambdaExpr>(Arg)) {125                LambdaOwnerMap.insert(std::make_pair(VD, L));126                CallToIgnore.insert(CE);127                LambdasToIgnore.insert(L);128              }129            } else if (FnName == "makeVisitor") {130              for (unsigned ArgIndex = 0; ArgIndex < ArgCnt; ++ArgIndex) {131                auto *Arg = CE->getArg(ArgIndex);132                if (auto *E = dyn_cast<MaterializeTemporaryExpr>(Arg))133                  Arg = E->getSubExpr();134                if (auto *L = dyn_cast<LambdaExpr>(Arg)) {135                  LambdaOwnerMap.insert(std::make_pair(VD, L));136                  CallToIgnore.insert(CE);137                  LambdasToIgnore.insert(L);138                }139              }140            }141          }142        } else if (auto *CE = dyn_cast<CXXConstructExpr>(Init)) {143          if (auto *Ctor = CE->getConstructor()) {144            if (auto *Cls = Ctor->getParent()) {145              auto FnName = safeGetName(Cls);146              unsigned ArgCnt = CE->getNumArgs();147              if (FnName == "ScopeExit" && ArgCnt == 1) {148                auto *Arg = CE->getArg(0);149                if (auto *E = dyn_cast<MaterializeTemporaryExpr>(Arg))150                  Arg = E->getSubExpr();151                if (auto *L = dyn_cast<LambdaExpr>(Arg)) {152                  LambdaOwnerMap.insert(std::make_pair(VD, L));153                  ConstructToIgnore.insert(CE);154                  LambdasToIgnore.insert(L);155                }156              }157            }158          }159        }160        return true;161      }162 163      bool VisitDeclRefExpr(DeclRefExpr *DRE) override {164        if (DeclRefExprsToIgnore.contains(DRE))165          return true;166        auto *VD = dyn_cast_or_null<VarDecl>(DRE->getDecl());167        if (!VD)168          return true;169        if (auto It = LambdaOwnerMap.find(VD); It != LambdaOwnerMap.end()) {170          auto *L = It->second;171          Checker->visitLambdaExpr(L, shouldCheckThis() && !hasProtectedThis(L),172                                   ClsType);173          return true;174        }175        auto *Init = VD->getInit();176        if (!Init)177          return true;178        auto *L = dyn_cast_or_null<LambdaExpr>(Init->IgnoreParenCasts());179        if (!L)180          return true;181        LambdasToIgnore.insert(L);182        Checker->visitLambdaExpr(L, shouldCheckThis() && !hasProtectedThis(L),183                                 ClsType);184        return true;185      }186 187      bool shouldTreatAllArgAsNoEscape(FunctionDecl *FDecl) {188        std::string PreviousName = safeGetName(FDecl);189        for (auto *Decl = FDecl->getParent(); Decl; Decl = Decl->getParent()) {190          if (!isa<NamespaceDecl>(Decl) && !isa<CXXRecordDecl>(Decl))191            return false;192          auto Name = safeGetName(Decl);193          // WTF::switchOn(T, F... f) is a variadic template function and194          // couldn't be annotated with NOESCAPE. We hard code it here to195          // workaround that.196          if (Name == "WTF" && PreviousName == "switchOn")197            return true;198          // Treat every argument of functions in std::ranges as noescape.199          if (Name == "std" && PreviousName == "ranges")200            return true;201          PreviousName = Name;202        }203        return false;204      }205 206      bool VisitCXXConstructExpr(CXXConstructExpr *CE) override {207        if (ConstructToIgnore.contains(CE))208          return true;209        if (auto *Callee = CE->getConstructor()) {210          unsigned ArgIndex = 0;211          for (auto *Param : Callee->parameters()) {212            if (ArgIndex >= CE->getNumArgs())213              return true;214            auto *Arg = CE->getArg(ArgIndex)->IgnoreParenCasts();215            if (auto *L = findLambdaInArg(Arg)) {216              LambdasToIgnore.insert(L);217              if (!Param->hasAttr<NoEscapeAttr>())218                Checker->visitLambdaExpr(219                    L, shouldCheckThis() && !hasProtectedThis(L), ClsType);220            }221            ++ArgIndex;222          }223        }224        return true;225      }226 227      bool VisitCallExpr(CallExpr *CE) override {228        if (CallToIgnore.contains(CE))229          return true;230        checkCalleeLambda(CE);231        if (auto *Callee = CE->getDirectCallee()) {232          if (isVisitFunction(CE, Callee))233            return true;234          checkParameters(CE, Callee);235        } else if (auto *CalleeE = CE->getCallee()) {236          if (auto *DRE = dyn_cast<DeclRefExpr>(CalleeE->IgnoreParenCasts())) {237            if (auto *Callee = dyn_cast_or_null<FunctionDecl>(DRE->getDecl()))238              checkParameters(CE, Callee);239          }240        }241        return true;242      }243 244      bool isVisitFunction(CallExpr *CallExpr, FunctionDecl *FnDecl) {245        bool IsVisitFn = safeGetName(FnDecl) == "visit";246        if (!IsVisitFn)247          return false;248        bool ArgCnt = CallExpr->getNumArgs();249        if (!ArgCnt)250          return false;251        auto *Ns = FnDecl->getParent();252        if (!Ns)253          return false;254        auto NsName = safeGetName(Ns);255        if (NsName != "WTF" && NsName != "std")256          return false;257        auto *Arg = CallExpr->getArg(0);258        if (!Arg)259          return false;260        auto *DRE = dyn_cast<DeclRefExpr>(Arg->IgnoreParenCasts());261        if (!DRE)262          return false;263        auto *VD = dyn_cast<VarDecl>(DRE->getDecl());264        if (!VD)265          return false;266        if (!LambdaOwnerMap.contains(VD))267          return false;268        DeclRefExprsToIgnore.insert(DRE);269        return true;270      }271 272      void checkParameters(CallExpr *CE, FunctionDecl *Callee) {273        unsigned ArgIndex = isa<CXXOperatorCallExpr>(CE);274        bool TreatAllArgsAsNoEscape = shouldTreatAllArgAsNoEscape(Callee);275        for (auto *Param : Callee->parameters()) {276          if (ArgIndex >= CE->getNumArgs())277            return;278          auto *Arg = CE->getArg(ArgIndex)->IgnoreParenCasts();279          if (auto *L = findLambdaInArg(Arg)) {280            LambdasToIgnore.insert(L);281            if (!Param->hasAttr<NoEscapeAttr>() && !TreatAllArgsAsNoEscape)282              Checker->visitLambdaExpr(283                  L, shouldCheckThis() && !hasProtectedThis(L), ClsType);284          }285          ++ArgIndex;286        }287      }288 289      LambdaExpr *findLambdaInArg(Expr *E) {290        if (auto *Lambda = dyn_cast_or_null<LambdaExpr>(E))291          return Lambda;292        auto *TempExpr = dyn_cast_or_null<CXXBindTemporaryExpr>(E);293        if (!TempExpr)294          return nullptr;295        E = TempExpr->getSubExpr()->IgnoreParenCasts();296        if (!E)297          return nullptr;298        if (auto *Lambda = dyn_cast<LambdaExpr>(E))299          return Lambda;300        auto *CE = dyn_cast_or_null<CXXConstructExpr>(E);301        if (!CE || !CE->getNumArgs())302          return nullptr;303        auto *CtorArg = CE->getArg(0)->IgnoreParenCasts();304        if (!CtorArg)305          return nullptr;306        auto *InnerCE = dyn_cast_or_null<CXXConstructExpr>(CtorArg);307        if (InnerCE && InnerCE->getNumArgs())308          CtorArg = InnerCE->getArg(0)->IgnoreParenCasts();309        auto updateIgnoreList = [&] {310          ConstructToIgnore.insert(CE);311          if (InnerCE)312            ConstructToIgnore.insert(InnerCE);313        };314        if (auto *Lambda = dyn_cast<LambdaExpr>(CtorArg)) {315          updateIgnoreList();316          return Lambda;317        }318        if (auto *TempExpr = dyn_cast<CXXBindTemporaryExpr>(CtorArg)) {319          E = TempExpr->getSubExpr()->IgnoreParenCasts();320          if (auto *Lambda = dyn_cast<LambdaExpr>(E)) {321            updateIgnoreList();322            return Lambda;323          }324        }325        auto *DRE = dyn_cast<DeclRefExpr>(CtorArg);326        if (!DRE)327          return nullptr;328        auto *VD = dyn_cast_or_null<VarDecl>(DRE->getDecl());329        if (!VD)330          return nullptr;331        auto *Init = VD->getInit();332        if (!Init)333          return nullptr;334        if (auto *Lambda = dyn_cast<LambdaExpr>(Init)) {335          DeclRefExprsToIgnore.insert(DRE);336          updateIgnoreList();337          return Lambda;338        }339        return nullptr;340      }341 342      void checkCalleeLambda(CallExpr *CE) {343        auto *Callee = CE->getCallee();344        if (!Callee)345          return;346        auto *DRE = dyn_cast<DeclRefExpr>(Callee->IgnoreParenCasts());347        if (!DRE)348          return;349        auto *MD = dyn_cast_or_null<CXXMethodDecl>(DRE->getDecl());350        if (!MD || CE->getNumArgs() < 1)351          return;352        auto *Arg = CE->getArg(0)->IgnoreParenCasts();353        if (auto *L = dyn_cast_or_null<LambdaExpr>(Arg)) {354          LambdasToIgnore.insert(L); // Calling a lambda upon creation is safe.355          return;356        }357        auto *ArgRef = dyn_cast<DeclRefExpr>(Arg);358        if (!ArgRef)359          return;360        auto *VD = dyn_cast_or_null<VarDecl>(ArgRef->getDecl());361        if (!VD)362          return;363        auto *Init = VD->getInit();364        if (!Init)365          return;366        auto *L = dyn_cast_or_null<LambdaExpr>(Init->IgnoreParenCasts());367        if (!L)368          return;369        DeclRefExprsToIgnore.insert(ArgRef);370        LambdasToIgnore.insert(L);371      }372 373      bool hasProtectedThis(const LambdaExpr *L) {374        for (const LambdaCapture &OtherCapture : L->captures()) {375          if (!OtherCapture.capturesVariable())376            continue;377          if (auto *ValueDecl = OtherCapture.getCapturedVar()) {378            if (declProtectsThis(ValueDecl)) {379              ProtectedThisDecls.insert(ValueDecl);380              return true;381            }382          }383        }384        return false;385      }386 387      bool declProtectsThis(const ValueDecl *ValueDecl) const {388        auto *VD = dyn_cast<VarDecl>(ValueDecl);389        if (!VD)390          return false;391        auto *Init = VD->getInit();392        if (!Init)393          return false;394        const Expr *Arg = Init->IgnoreParenCasts();395        do {396          if (auto *BTE = dyn_cast<CXXBindTemporaryExpr>(Arg))397            Arg = BTE->getSubExpr()->IgnoreParenCasts();398          if (auto *CE = dyn_cast<CXXConstructExpr>(Arg)) {399            auto *Ctor = CE->getConstructor();400            if (!Ctor)401              return false;402            auto clsName = safeGetName(Ctor->getParent());403            if (Checker->isPtrType(clsName) && CE->getNumArgs()) {404              Arg = CE->getArg(0)->IgnoreParenCasts();405              continue;406            }407            if (auto *Type = ClsType.getTypePtrOrNull()) {408              if (auto *CXXR = Type->getPointeeCXXRecordDecl()) {409                if (CXXR == Ctor->getParent() && Ctor->isMoveConstructor() &&410                    CE->getNumArgs() == 1) {411                  Arg = CE->getArg(0)->IgnoreParenCasts();412                  continue;413                }414              }415            }416            return false;417          }418          if (auto *CE = dyn_cast<CallExpr>(Arg)) {419            if (CE->isCallToStdMove() && CE->getNumArgs() == 1) {420              Arg = CE->getArg(0)->IgnoreParenCasts();421              continue;422            }423            if (auto *Callee = CE->getDirectCallee()) {424              if (isCtorOfSafePtr(Callee) && CE->getNumArgs() == 1) {425                Arg = CE->getArg(0)->IgnoreParenCasts();426                continue;427              }428            }429          }430          if (auto *OpCE = dyn_cast<CXXOperatorCallExpr>(Arg)) {431            auto OpCode = OpCE->getOperator();432            if (OpCode == OO_Star || OpCode == OO_Amp) {433              auto *Callee = OpCE->getDirectCallee();434              if (!Callee)435                return false;436              auto clsName = safeGetName(Callee->getParent());437              if (!Checker->isPtrType(clsName) || !OpCE->getNumArgs())438                return false;439              Arg = OpCE->getArg(0)->IgnoreParenCasts();440              continue;441            }442          }443          if (auto *UO = dyn_cast<UnaryOperator>(Arg)) {444            auto OpCode = UO->getOpcode();445            if (OpCode == UO_Deref || OpCode == UO_AddrOf) {446              Arg = UO->getSubExpr()->IgnoreParenCasts();447              continue;448            }449          }450          break;451        } while (Arg);452        if (auto *DRE = dyn_cast<DeclRefExpr>(Arg)) {453          auto *Decl = DRE->getDecl();454          if (auto *ImplicitParam = dyn_cast<ImplicitParamDecl>(Decl)) {455            auto kind = ImplicitParam->getParameterKind();456            return kind == ImplicitParamKind::ObjCSelf ||457                   kind == ImplicitParamKind::CXXThis;458          }459          return ProtectedThisDecls.contains(Decl);460        }461        return isa<CXXThisExpr>(Arg);462      }463    };464 465    LocalVisitor visitor(this);466    if (RTC)467      RTC->visitTranslationUnitDecl(TUD);468    visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));469  }470 471  void visitLambdaExpr(const LambdaExpr *L, bool shouldCheckThis,472                       const QualType T,473                       bool ignoreParamVarDecl = false) const {474    if (TFA.isTrivial(L->getBody()))475      return;476    for (const LambdaCapture &C : L->captures()) {477      if (C.capturesVariable()) {478        ValueDecl *CapturedVar = C.getCapturedVar();479        if (ignoreParamVarDecl && isa<ParmVarDecl>(CapturedVar))480          continue;481        if (auto *ImplicitParam = dyn_cast<ImplicitParamDecl>(CapturedVar)) {482          auto kind = ImplicitParam->getParameterKind();483          if ((kind == ImplicitParamKind::ObjCSelf ||484               kind == ImplicitParamKind::CXXThis) &&485              !shouldCheckThis)486            continue;487        }488        QualType CapturedVarQualType = CapturedVar->getType();489        auto IsUncountedPtr = isUnsafePtr(CapturedVar->getType());490        if (C.getCaptureKind() == LCK_ByCopy &&491            CapturedVarQualType->isReferenceType())492          continue;493        if (IsUncountedPtr && *IsUncountedPtr)494          reportBug(C, CapturedVar, CapturedVarQualType, L);495      } else if (C.capturesThis() && shouldCheckThis) {496        if (ignoreParamVarDecl) // this is always a parameter to this function.497          continue;498        reportBugOnThisPtr(C, T);499      }500    }501  }502 503  void reportBug(const LambdaCapture &Capture, ValueDecl *CapturedVar,504                 const QualType T, const LambdaExpr *L) const {505    assert(CapturedVar);506 507    auto Location = Capture.getLocation();508    if (isa<ImplicitParamDecl>(CapturedVar) && !Location.isValid())509      Location = L->getBeginLoc();510 511    SmallString<100> Buf;512    llvm::raw_svector_ostream Os(Buf);513 514    if (Capture.isExplicit()) {515      Os << "Captured ";516    } else {517      Os << "Implicitly captured ";518    }519    if (isa<PointerType>(T) || isa<ObjCObjectPointerType>(T)) {520      Os << "raw-pointer ";521    } else {522      Os << "reference ";523    }524 525    printQuotedQualifiedName(Os, CapturedVar);526    Os << " to " << ptrKind(T) << " type is unsafe.";527 528    PathDiagnosticLocation BSLoc(Location, BR->getSourceManager());529    auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);530    BR->emitReport(std::move(Report));531  }532 533  void reportBugOnThisPtr(const LambdaCapture &Capture,534                          const QualType T) const {535    SmallString<100> Buf;536    llvm::raw_svector_ostream Os(Buf);537 538    if (Capture.isExplicit()) {539      Os << "Captured ";540    } else {541      Os << "Implicitly captured ";542    }543 544    Os << "raw-pointer 'this' to " << ptrKind(T) << " type is unsafe.";545 546    PathDiagnosticLocation BSLoc(Capture.getLocation(), BR->getSourceManager());547    auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);548    BR->emitReport(std::move(Report));549  }550};551 552class UncountedLambdaCapturesChecker : public RawPtrRefLambdaCapturesChecker {553public:554  UncountedLambdaCapturesChecker()555      : RawPtrRefLambdaCapturesChecker("Lambda capture of uncounted or "556                                       "unchecked variable") {}557 558  std::optional<bool> isUnsafePtr(QualType QT) const final {559    auto result1 = isUncountedPtr(QT);560    auto result2 = isUncheckedPtr(QT);561    if (result1 && *result1)562      return true;563    if (result2 && *result2)564      return true;565    if (result1)566      return *result1;567    return result2;568  }569 570  virtual bool isPtrType(const std::string &Name) const final {571    return isRefType(Name) || isCheckedPtr(Name);572  }573 574  const char *ptrKind(QualType QT) const final {575    if (isUncounted(QT))576      return "uncounted";577    return "unchecked";578  }579};580 581class UnretainedLambdaCapturesChecker : public RawPtrRefLambdaCapturesChecker {582public:583  UnretainedLambdaCapturesChecker()584      : RawPtrRefLambdaCapturesChecker("Lambda capture of unretained "585                                       "variables") {586    RTC = RetainTypeChecker();587  }588 589  std::optional<bool> isUnsafePtr(QualType QT) const final {590    return RTC->isUnretained(QT);591  }592 593  virtual bool isPtrType(const std::string &Name) const final {594    return isRetainPtrOrOSPtr(Name);595  }596 597  const char *ptrKind(QualType QT) const final { return "unretained"; }598};599 600} // namespace601 602void ento::registerUncountedLambdaCapturesChecker(CheckerManager &Mgr) {603  Mgr.registerChecker<UncountedLambdaCapturesChecker>();604}605 606bool ento::shouldRegisterUncountedLambdaCapturesChecker(607    const CheckerManager &mgr) {608  return true;609}610 611void ento::registerUnretainedLambdaCapturesChecker(CheckerManager &Mgr) {612  Mgr.registerChecker<UnretainedLambdaCapturesChecker>();613}614 615bool ento::shouldRegisterUnretainedLambdaCapturesChecker(616    const CheckerManager &mgr) {617  return true;618}619