brintos

brintos / llvm-project-archived public Read only

0
0
Text · 13.9 KiB · c6421f8 Raw
427 lines · cpp
1//=======- RefCntblBaseVirtualDtor.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/DynamicRecursiveASTVisitor.h"14#include "clang/AST/StmtVisitor.h"15#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"16#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"17#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"18#include "clang/StaticAnalyzer/Core/Checker.h"19#include "llvm/ADT/DenseSet.h"20#include "llvm/ADT/SetVector.h"21#include <optional>22 23using namespace clang;24using namespace ento;25 26namespace {27 28class DerefFuncDeleteExprVisitor29    : public ConstStmtVisitor<DerefFuncDeleteExprVisitor, bool> {30  // Returns true if any of child statements return true.31  bool VisitChildren(const Stmt *S) {32    for (const Stmt *Child : S->children()) {33      if (Child && Visit(Child))34        return true;35    }36    return false;37  }38 39  bool VisitBody(const Stmt *Body) {40    if (!Body)41      return false;42 43    auto [It, IsNew] = VisitedBody.insert(Body);44    if (!IsNew) // This body is recursive45      return false;46 47    return Visit(Body);48  }49 50public:51  DerefFuncDeleteExprVisitor(const TemplateArgumentList &ArgList,52                             const CXXRecordDecl *ClassDecl)53      : ArgList(&ArgList), ClassDecl(ClassDecl) {}54 55  DerefFuncDeleteExprVisitor(const CXXRecordDecl *ClassDecl)56      : ClassDecl(ClassDecl) {}57 58  std::optional<bool> HasSpecializedDelete(CXXMethodDecl *Decl) {59    if (auto *Body = Decl->getBody())60      return VisitBody(Body);61    if (Decl->getTemplateInstantiationPattern())62      return std::nullopt; // Indeterminate. There was no concrete instance.63    return false;64  }65 66  bool VisitCallExpr(const CallExpr *CE) {67    const Decl *D = CE->getCalleeDecl();68    if (D && D->hasBody())69      return VisitBody(D->getBody());70    else {71      auto name = safeGetName(D);72      if (name == "ensureOnMainThread" || name == "ensureOnMainRunLoop") {73        for (unsigned i = 0; i < CE->getNumArgs(); ++i) {74          auto *Arg = CE->getArg(i);75          if (VisitLambdaArgument(Arg))76            return true;77        }78      }79    }80    return false;81  }82 83  bool VisitLambdaArgument(const Expr *E) {84    E = E->IgnoreParenCasts();85    if (auto *TempE = dyn_cast<CXXBindTemporaryExpr>(E))86      E = TempE->getSubExpr();87    E = E->IgnoreParenCasts();88    if (auto *Ref = dyn_cast<DeclRefExpr>(E)) {89      if (auto *VD = dyn_cast_or_null<VarDecl>(Ref->getDecl()))90        return VisitLambdaArgument(VD->getInit());91      return false;92    }93    if (auto *Lambda = dyn_cast<LambdaExpr>(E)) {94      if (VisitBody(Lambda->getBody()))95        return true;96    }97    if (auto *ConstructE = dyn_cast<CXXConstructExpr>(E)) {98      for (unsigned i = 0; i < ConstructE->getNumArgs(); ++i) {99        if (VisitLambdaArgument(ConstructE->getArg(i)))100          return true;101      }102    }103    return false;104  }105 106  bool VisitCXXDeleteExpr(const CXXDeleteExpr *E) {107    auto *Arg = E->getArgument();108    while (Arg) {109      if (auto *Paren = dyn_cast<ParenExpr>(Arg))110        Arg = Paren->getSubExpr();111      else if (auto *Cast = dyn_cast<CastExpr>(Arg)) {112        Arg = Cast->getSubExpr();113        auto CastType = Cast->getType();114        if (auto *PtrType = dyn_cast<PointerType>(CastType)) {115          auto PointeeType = PtrType->getPointeeType();116          if (auto *ParmType = dyn_cast<TemplateTypeParmType>(PointeeType)) {117            if (ArgList) {118              auto ParmIndex = ParmType->getIndex();119              auto Type = ArgList->get(ParmIndex).getAsType();120              if (Type->getAsCXXRecordDecl() == ClassDecl)121                return true;122            }123          } else if (auto *RD = dyn_cast<RecordType>(PointeeType)) {124            if (declaresSameEntity(RD->getDecl(), ClassDecl))125              return true;126          } else if (auto *ST =127                         dyn_cast<SubstTemplateTypeParmType>(PointeeType)) {128            auto Type = ST->getReplacementType();129            if (auto *RD = dyn_cast<RecordType>(Type)) {130              if (declaresSameEntity(RD->getDecl(), ClassDecl))131                return true;132            }133          }134        }135      } else136        break;137    }138    return false;139  }140 141  bool VisitStmt(const Stmt *S) { return VisitChildren(S); }142 143  // Return false since the contents of lambda isn't necessarily executed.144  // If it is executed, VisitCallExpr above will visit its body.145  bool VisitLambdaExpr(const LambdaExpr *) { return false; }146 147private:148  const TemplateArgumentList *ArgList{nullptr};149  const CXXRecordDecl *ClassDecl;150  llvm::DenseSet<const Stmt *> VisitedBody;151};152 153class RefCntblBaseVirtualDtorChecker154    : public Checker<check::ASTDecl<TranslationUnitDecl>> {155private:156  BugType Bug;157  mutable BugReporter *BR;158 159public:160  RefCntblBaseVirtualDtorChecker()161      : Bug(this,162            "Reference-countable base class doesn't have virtual destructor",163            "WebKit coding guidelines") {}164 165  void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,166                    BugReporter &BRArg) const {167    BR = &BRArg;168 169    // The calls to checkAST* from AnalysisConsumer don't170    // visit template instantiations or lambda classes. We171    // want to visit those, so we make our own RecursiveASTVisitor.172    struct LocalVisitor : DynamicRecursiveASTVisitor {173      const RefCntblBaseVirtualDtorChecker *Checker;174      explicit LocalVisitor(const RefCntblBaseVirtualDtorChecker *Checker)175          : Checker(Checker) {176        assert(Checker);177        ShouldVisitTemplateInstantiations = true;178        ShouldVisitImplicitCode = false;179      }180 181      bool VisitCXXRecordDecl(CXXRecordDecl *RD) override {182        if (!RD->hasDefinition())183          return true;184 185        Decls.insert(RD);186 187        for (auto &Base : RD->bases()) {188          const auto AccSpec = Base.getAccessSpecifier();189          if (AccSpec == AS_protected || AccSpec == AS_private ||190              (AccSpec == AS_none && RD->isClass()))191            continue;192 193          QualType T = Base.getType();194          if (T.isNull())195            continue;196 197          const CXXRecordDecl *C = T->getAsCXXRecordDecl();198          if (!C)199            continue;200 201          bool isExempt = T.getAsString() == "NoVirtualDestructorBase" &&202                          safeGetName(C->getParent()) == "WTF";203          if (isExempt || ExemptDecls.contains(C)) {204            ExemptDecls.insert(RD);205            continue;206          }207 208          if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(C)) {209            for (auto &Arg : CTSD->getTemplateArgs().asArray()) {210              if (Arg.getKind() != TemplateArgument::Type)211                continue;212              auto TemplT = Arg.getAsType();213              if (TemplT.isNull())214                continue;215 216              bool IsCRTP = TemplT->getAsCXXRecordDecl() == RD;217              if (!IsCRTP)218                continue;219              CRTPs.insert(C);220            }221          }222        }223 224        return true;225      }226 227      llvm::SetVector<const CXXRecordDecl *> Decls;228      llvm::DenseSet<const CXXRecordDecl *> CRTPs;229      llvm::DenseSet<const CXXRecordDecl *> ExemptDecls;230    };231 232    LocalVisitor visitor(this);233    visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));234    for (auto *RD : visitor.Decls) {235      if (visitor.CRTPs.contains(RD) || visitor.ExemptDecls.contains(RD))236        continue;237      visitCXXRecordDecl(RD);238    }239  }240 241  void visitCXXRecordDecl(const CXXRecordDecl *RD) const {242    if (shouldSkipDecl(RD))243      return;244 245    for (auto &Base : RD->bases()) {246      const auto AccSpec = Base.getAccessSpecifier();247      if (AccSpec == AS_protected || AccSpec == AS_private ||248          (AccSpec == AS_none && RD->isClass()))249        continue;250 251      auto hasRefInBase = clang::hasPublicMethodInBase(&Base, "ref");252      auto hasDerefInBase = clang::hasPublicMethodInBase(&Base, "deref");253 254      bool hasRef = hasRefInBase && *hasRefInBase != nullptr;255      bool hasDeref = hasDerefInBase && *hasDerefInBase != nullptr;256 257      QualType T = Base.getType();258      if (T.isNull())259        continue;260 261      const CXXRecordDecl *C = T->getAsCXXRecordDecl();262      if (!C)263        continue;264 265      bool AnyInconclusiveBase = false;266      const auto hasPublicRefInBase =267          [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) {268            auto hasRefInBase = clang::hasPublicMethodInBase(Base, "ref");269            if (!hasRefInBase) {270              AnyInconclusiveBase = true;271              return false;272            }273            return (*hasRefInBase) != nullptr;274          };275      const auto hasPublicDerefInBase =276          [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) {277            auto hasDerefInBase = clang::hasPublicMethodInBase(Base, "deref");278            if (!hasDerefInBase) {279              AnyInconclusiveBase = true;280              return false;281            }282            return (*hasDerefInBase) != nullptr;283          };284      CXXBasePaths Paths;285      Paths.setOrigin(C);286      hasRef = hasRef || C->lookupInBases(hasPublicRefInBase, Paths,287                                          /*LookupInDependent =*/true);288      hasDeref = hasDeref || C->lookupInBases(hasPublicDerefInBase, Paths,289                                              /*LookupInDependent =*/true);290      if (AnyInconclusiveBase || !hasRef || !hasDeref)291        continue;292 293      auto HasSpecializedDelete = isClassWithSpecializedDelete(C, RD);294      if (!HasSpecializedDelete || *HasSpecializedDelete)295        continue;296      if (C->lookupInBases(297              [&](const CXXBaseSpecifier *Base, CXXBasePath &) {298                auto *T = Base->getType().getTypePtrOrNull();299                if (!T)300                  return false;301                auto *R = T->getAsCXXRecordDecl();302                if (!R)303                  return false;304                auto Result = isClassWithSpecializedDelete(R, RD);305                if (!Result)306                  AnyInconclusiveBase = true;307                return Result && *Result;308              },309              Paths, /*LookupInDependent =*/true))310        continue;311      if (AnyInconclusiveBase)312        continue;313 314      const auto *Dtor = C->getDestructor();315      if (!Dtor || !Dtor->isVirtual()) {316        auto *ProblematicBaseSpecifier = &Base;317        auto *ProblematicBaseClass = C;318        reportBug(RD, ProblematicBaseSpecifier, ProblematicBaseClass);319      }320    }321  }322 323  bool shouldSkipDecl(const CXXRecordDecl *RD) const {324    if (!RD->isThisDeclarationADefinition())325      return true;326 327    if (RD->isImplicit())328      return true;329 330    if (RD->isLambda())331      return true;332 333    // If the construct doesn't have a source file, then it's not something334    // we want to diagnose.335    const auto RDLocation = RD->getLocation();336    if (!RDLocation.isValid())337      return true;338 339    const auto Kind = RD->getTagKind();340    if (Kind != TagTypeKind::Struct && Kind != TagTypeKind::Class)341      return true;342 343    // Ignore CXXRecords that come from system headers.344    if (BR->getSourceManager().getFileCharacteristic(RDLocation) !=345        SrcMgr::C_User)346      return true;347 348    return false;349  }350 351  static bool isRefCountedClass(const CXXRecordDecl *D) {352    if (!D->getTemplateInstantiationPattern())353      return false;354    auto *NsDecl = D->getParent();355    if (!NsDecl || !isa<NamespaceDecl>(NsDecl))356      return false;357    auto NamespaceName = safeGetName(NsDecl);358    auto ClsNameStr = safeGetName(D);359    StringRef ClsName = ClsNameStr; // FIXME: Make safeGetName return StringRef.360    return NamespaceName == "WTF" &&361           (ClsName.ends_with("RefCounted") ||362            ClsName == "ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr");363  }364 365  static std::optional<bool>366  isClassWithSpecializedDelete(const CXXRecordDecl *C,367                               const CXXRecordDecl *DerivedClass) {368    if (auto *ClsTmplSpDecl = dyn_cast<ClassTemplateSpecializationDecl>(C)) {369      for (auto *MethodDecl : C->methods()) {370        if (safeGetName(MethodDecl) == "deref") {371          DerefFuncDeleteExprVisitor Visitor(ClsTmplSpDecl->getTemplateArgs(),372                                             DerivedClass);373          auto Result = Visitor.HasSpecializedDelete(MethodDecl);374          if (!Result || *Result)375            return Result;376        }377      }378      return false;379    }380    for (auto *MethodDecl : C->methods()) {381      if (safeGetName(MethodDecl) == "deref") {382        DerefFuncDeleteExprVisitor Visitor(DerivedClass);383        auto Result = Visitor.HasSpecializedDelete(MethodDecl);384        if (!Result || *Result)385          return Result;386      }387    }388    return false;389  }390 391  void reportBug(const CXXRecordDecl *DerivedClass,392                 const CXXBaseSpecifier *BaseSpec,393                 const CXXRecordDecl *ProblematicBaseClass) const {394    assert(DerivedClass);395    assert(BaseSpec);396    assert(ProblematicBaseClass);397 398    SmallString<100> Buf;399    llvm::raw_svector_ostream Os(Buf);400 401    Os << (ProblematicBaseClass->isClass() ? "Class" : "Struct") << " ";402    printQuotedQualifiedName(Os, ProblematicBaseClass);403 404    Os << " is used as a base of "405       << (DerivedClass->isClass() ? "class" : "struct") << " ";406    printQuotedQualifiedName(Os, DerivedClass);407 408    Os << " but doesn't have virtual destructor";409 410    PathDiagnosticLocation BSLoc(BaseSpec->getSourceRange().getBegin(),411                                 BR->getSourceManager());412    auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);413    Report->addRange(BaseSpec->getSourceRange());414    BR->emitReport(std::move(Report));415  }416};417} // namespace418 419void ento::registerRefCntblBaseVirtualDtorChecker(CheckerManager &Mgr) {420  Mgr.registerChecker<RefCntblBaseVirtualDtorChecker>();421}422 423bool ento::shouldRegisterRefCntblBaseVirtualDtorChecker(424    const CheckerManager &mgr) {425  return true;426}427