brintos

brintos / llvm-project-archived public Read only

0
0
Text · 17.3 KiB · 791e709 Raw
517 lines · cpp
1//=======- RawPtrRefCallArgsChecker.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/Decl.h"13#include "clang/AST/DeclCXX.h"14#include "clang/AST/DynamicRecursiveASTVisitor.h"15#include "clang/Analysis/DomainSpecific/CocoaConventions.h"16#include "clang/Basic/SourceLocation.h"17#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"18#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"19#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"20#include "clang/StaticAnalyzer/Core/Checker.h"21#include "llvm/Support/SaveAndRestore.h"22#include <optional>23 24using namespace clang;25using namespace ento;26 27namespace {28 29class RawPtrRefCallArgsChecker30    : public Checker<check::ASTDecl<TranslationUnitDecl>> {31  BugType Bug;32 33  TrivialFunctionAnalysis TFA;34  EnsureFunctionAnalysis EFA;35 36protected:37  mutable BugReporter *BR;38  mutable std::optional<RetainTypeChecker> RTC;39 40public:41  RawPtrRefCallArgsChecker(const char *description)42      : Bug(this, description, "WebKit coding guidelines") {}43 44  virtual std::optional<bool> isUnsafeType(QualType) const = 0;45  virtual std::optional<bool> isUnsafePtr(QualType) const = 0;46  virtual bool isSafePtr(const CXXRecordDecl *Record) const = 0;47  virtual bool isSafePtrType(const QualType type) const = 0;48  virtual bool isSafeExpr(const Expr *) const { return false; }49  virtual bool isSafeDecl(const Decl *) const { return false; }50  virtual const char *ptrKind() const = 0;51 52  void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,53                    BugReporter &BRArg) const {54    BR = &BRArg;55 56    // The calls to checkAST* from AnalysisConsumer don't57    // visit template instantiations or lambda classes. We58    // want to visit those, so we make our own RecursiveASTVisitor.59    struct LocalVisitor : DynamicRecursiveASTVisitor {60      const RawPtrRefCallArgsChecker *Checker;61      Decl *DeclWithIssue{nullptr};62 63      explicit LocalVisitor(const RawPtrRefCallArgsChecker *Checker)64          : Checker(Checker) {65        assert(Checker);66        ShouldVisitTemplateInstantiations = true;67        ShouldVisitImplicitCode = false;68      }69 70      bool TraverseClassTemplateDecl(ClassTemplateDecl *Decl) override {71        if (isSmartPtrClass(safeGetName(Decl)))72          return true;73        return DynamicRecursiveASTVisitor::TraverseClassTemplateDecl(Decl);74      }75 76      bool TraverseDecl(Decl *D) override {77        llvm::SaveAndRestore SavedDecl(DeclWithIssue);78        if (D && (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)))79          DeclWithIssue = D;80        return DynamicRecursiveASTVisitor::TraverseDecl(D);81      }82 83      bool VisitCallExpr(CallExpr *CE) override {84        Checker->visitCallExpr(CE, DeclWithIssue);85        return true;86      }87 88      bool VisitTypedefDecl(TypedefDecl *TD) override {89        if (Checker->RTC)90          Checker->RTC->visitTypedef(TD);91        return true;92      }93 94      bool VisitObjCMessageExpr(ObjCMessageExpr *ObjCMsgExpr) override {95        Checker->visitObjCMessageExpr(ObjCMsgExpr, DeclWithIssue);96        return true;97      }98    };99 100    LocalVisitor visitor(this);101    if (RTC)102      RTC->visitTranslationUnitDecl(TUD);103    visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));104  }105 106  void visitCallExpr(const CallExpr *CE, const Decl *D) const {107    if (shouldSkipCall(CE))108      return;109 110    if (auto *F = CE->getDirectCallee()) {111      // Skip the first argument for overloaded member operators (e. g. lambda112      // or std::function call operator).113      unsigned ArgIdx =114          isa<CXXOperatorCallExpr>(CE) && isa_and_nonnull<CXXMethodDecl>(F);115 116      if (auto *MemberCallExpr = dyn_cast<CXXMemberCallExpr>(CE)) {117        if (auto *MD = MemberCallExpr->getMethodDecl()) {118          auto name = safeGetName(MD);119          if (name == "ref" || name == "deref")120            return;121          if (name == "incrementCheckedPtrCount" ||122              name == "decrementCheckedPtrCount")123            return;124        }125        auto *E = MemberCallExpr->getImplicitObjectArgument();126        QualType ArgType = MemberCallExpr->getObjectType().getCanonicalType();127        std::optional<bool> IsUnsafe = isUnsafeType(ArgType);128        if (IsUnsafe && *IsUnsafe && !isPtrOriginSafe(E))129          reportBugOnThis(E, D);130      }131 132      for (auto P = F->param_begin();133           // FIXME: Also check variadic function parameters.134           // FIXME: Also check default function arguments. Probably a different135           // checker. In case there are default arguments the call can have136           // fewer arguments than the callee has parameters.137           P < F->param_end() && ArgIdx < CE->getNumArgs(); ++P, ++ArgIdx) {138        // TODO: attributes.139        // if ((*P)->hasAttr<SafeRefCntblRawPtrAttr>())140        //  continue;141 142        QualType ArgType = (*P)->getType();143        // FIXME: more complex types (arrays, references to raw pointers, etc)144        std::optional<bool> IsUncounted = isUnsafePtr(ArgType);145        if (!IsUncounted || !(*IsUncounted))146          continue;147 148        const auto *Arg = CE->getArg(ArgIdx);149 150        if (auto *defaultArg = dyn_cast<CXXDefaultArgExpr>(Arg))151          Arg = defaultArg->getExpr();152 153        if (isPtrOriginSafe(Arg))154          continue;155 156        reportBug(Arg, *P, D);157      }158      for (; ArgIdx < CE->getNumArgs(); ++ArgIdx) {159        const auto *Arg = CE->getArg(ArgIdx);160        auto ArgType = Arg->getType();161        std::optional<bool> IsUncounted = isUnsafePtr(ArgType);162        if (!IsUncounted || !(*IsUncounted))163          continue;164 165        if (auto *defaultArg = dyn_cast<CXXDefaultArgExpr>(Arg))166          Arg = defaultArg->getExpr();167 168        if (isPtrOriginSafe(Arg))169          continue;170 171        reportBug(Arg, nullptr, D);172      }173    }174  }175 176  void visitObjCMessageExpr(const ObjCMessageExpr *E, const Decl *D) const {177    if (BR->getSourceManager().isInSystemHeader(E->getExprLoc()))178      return;179 180    auto Selector = E->getSelector();181    if (auto *Receiver = E->getInstanceReceiver()) {182      std::optional<bool> IsUnsafe = isUnsafePtr(E->getReceiverType());183      if (IsUnsafe && *IsUnsafe && !isPtrOriginSafe(Receiver)) {184        if (auto *InnerMsg = dyn_cast<ObjCMessageExpr>(Receiver)) {185          auto InnerSelector = InnerMsg->getSelector();186          if (InnerSelector.getNameForSlot(0) == "alloc" &&187              Selector.getNameForSlot(0).starts_with("init"))188            return;189        }190        reportBugOnReceiver(Receiver, D);191      }192    }193 194    auto *MethodDecl = E->getMethodDecl();195    if (!MethodDecl)196      return;197 198    auto ArgCount = E->getNumArgs();199    for (unsigned i = 0; i < ArgCount; ++i) {200      auto *Arg = E->getArg(i);201      bool hasParam = i < MethodDecl->param_size();202      auto *Param = hasParam ? MethodDecl->getParamDecl(i) : nullptr;203      auto ArgType = Arg->getType();204      std::optional<bool> IsUnsafe = isUnsafePtr(ArgType);205      if (!IsUnsafe || !(*IsUnsafe))206        continue;207      if (isPtrOriginSafe(Arg))208        continue;209      reportBug(Arg, Param, D);210    }211  }212 213  bool isPtrOriginSafe(const Expr *Arg) const {214    return tryToFindPtrOrigin(215        Arg, /*StopAtFirstRefCountedObj=*/true,216        [&](const clang::CXXRecordDecl *Record) { return isSafePtr(Record); },217        [&](const clang::QualType T) { return isSafePtrType(T); },218        [&](const clang::Decl *D) { return isSafeDecl(D); },219        [&](const clang::Expr *ArgOrigin, bool IsSafe) {220          if (IsSafe)221            return true;222          if (isNullPtr(ArgOrigin))223            return true;224          if (isa<IntegerLiteral>(ArgOrigin)) {225            // FIXME: Check the value.226            // foo(123)227            return true;228          }229          if (isa<CXXBoolLiteralExpr>(ArgOrigin))230            return true;231          if (isa<ObjCStringLiteral>(ArgOrigin))232            return true;233          if (isASafeCallArg(ArgOrigin))234            return true;235          if (EFA.isACallToEnsureFn(ArgOrigin))236            return true;237          if (isSafeExpr(ArgOrigin))238            return true;239          return false;240        });241  }242 243  bool shouldSkipCall(const CallExpr *CE) const {244    const auto *Callee = CE->getDirectCallee();245 246    if (BR->getSourceManager().isInSystemHeader(CE->getExprLoc()))247      return true;248 249    if (Callee && TFA.isTrivial(Callee) && !Callee->isVirtualAsWritten())250      return true;251 252    if (isTrivialBuiltinFunction(Callee))253      return true;254 255    if (CE->getNumArgs() == 0)256      return false;257 258    // If an assignment is problematic we should warn about the sole existence259    // of object on LHS.260    if (auto *MemberOp = dyn_cast<CXXOperatorCallExpr>(CE)) {261      // Note: assignemnt to built-in type isn't derived from CallExpr.262      if (MemberOp->getOperator() ==263          OO_Equal) { // Ignore assignment to Ref/RefPtr.264        auto *callee = MemberOp->getDirectCallee();265        if (auto *calleeDecl = dyn_cast<CXXMethodDecl>(callee)) {266          if (const CXXRecordDecl *classDecl = calleeDecl->getParent()) {267            if (isSafePtr(classDecl))268              return true;269          }270        }271      }272      if (MemberOp->isAssignmentOp())273        return false;274    }275 276    if (!Callee)277      return false;278 279    if (isMethodOnWTFContainerType(Callee))280      return true;281 282    auto overloadedOperatorType = Callee->getOverloadedOperator();283    if (overloadedOperatorType == OO_EqualEqual ||284        overloadedOperatorType == OO_ExclaimEqual ||285        overloadedOperatorType == OO_LessEqual ||286        overloadedOperatorType == OO_GreaterEqual ||287        overloadedOperatorType == OO_Spaceship ||288        overloadedOperatorType == OO_AmpAmp ||289        overloadedOperatorType == OO_PipePipe)290      return true;291 292    if (isCtorOfSafePtr(Callee) || isPtrConversion(Callee))293      return true;294 295    auto name = safeGetName(Callee);296    if (name == "adoptRef" || name == "getPtr" || name == "WeakPtr" ||297        name == "is" || name == "equal" || name == "hash" || name == "isType" ||298        // FIXME: Most/all of these should be implemented via attributes.299        name == "CFEqual" || name == "equalIgnoringASCIICase" ||300        name == "equalIgnoringASCIICaseCommon" ||301        name == "equalIgnoringNullity" || name == "toString")302      return true;303 304    return false;305  }306 307  bool isMethodOnWTFContainerType(const FunctionDecl *Decl) const {308    if (!isa<CXXMethodDecl>(Decl))309      return false;310    auto *ClassDecl = Decl->getParent();311    if (!ClassDecl || !isa<CXXRecordDecl>(ClassDecl))312      return false;313 314    auto *NsDecl = ClassDecl->getParent();315    if (!NsDecl || !isa<NamespaceDecl>(NsDecl))316      return false;317 318    auto MethodName = safeGetName(Decl);319    auto ClsNameStr = safeGetName(ClassDecl);320    StringRef ClsName = ClsNameStr; // FIXME: Make safeGetName return StringRef.321    auto NamespaceName = safeGetName(NsDecl);322    // FIXME: These should be implemented via attributes.323    return NamespaceName == "WTF" &&324           (MethodName == "find" || MethodName == "findIf" ||325            MethodName == "reverseFind" || MethodName == "reverseFindIf" ||326            MethodName == "findIgnoringASCIICase" || MethodName == "get" ||327            MethodName == "inlineGet" || MethodName == "contains" ||328            MethodName == "containsIf" ||329            MethodName == "containsIgnoringASCIICase" ||330            MethodName == "startsWith" || MethodName == "endsWith" ||331            MethodName == "startsWithIgnoringASCIICase" ||332            MethodName == "endsWithIgnoringASCIICase" ||333            MethodName == "substring") &&334           (ClsName.ends_with("Vector") || ClsName.ends_with("Set") ||335            ClsName.ends_with("Map") || ClsName == "StringImpl" ||336            ClsName.ends_with("String"));337  }338 339  void reportBug(const Expr *CallArg, const ParmVarDecl *Param,340                 const Decl *DeclWithIssue) const {341    assert(CallArg);342 343    SmallString<100> Buf;344    llvm::raw_svector_ostream Os(Buf);345 346    const std::string paramName = safeGetName(Param);347    Os << "Call argument";348    if (!paramName.empty()) {349      Os << " for parameter ";350      printQuotedQualifiedName(Os, Param);351    }352    Os << " is " << ptrKind() << " and unsafe.";353 354    bool usesDefaultArgValue = isa<CXXDefaultArgExpr>(CallArg) && Param;355    const SourceLocation SrcLocToReport =356        usesDefaultArgValue ? Param->getDefaultArg()->getExprLoc()357                            : CallArg->getSourceRange().getBegin();358 359    PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());360    auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);361    Report->addRange(CallArg->getSourceRange());362    Report->setDeclWithIssue(DeclWithIssue);363    BR->emitReport(std::move(Report));364  }365 366  void reportBugOnThis(const Expr *CallArg, const Decl *DeclWithIssue) const {367    assert(CallArg);368 369    const SourceLocation SrcLocToReport = CallArg->getSourceRange().getBegin();370 371    SmallString<100> Buf;372    llvm::raw_svector_ostream Os(Buf);373    Os << "Call argument for 'this' parameter is " << ptrKind();374    Os << " and unsafe.";375 376    PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());377    auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);378    Report->addRange(CallArg->getSourceRange());379    Report->setDeclWithIssue(DeclWithIssue);380    BR->emitReport(std::move(Report));381  }382 383  void reportBugOnReceiver(const Expr *CallArg,384                           const Decl *DeclWithIssue) const {385    assert(CallArg);386 387    const SourceLocation SrcLocToReport = CallArg->getSourceRange().getBegin();388 389    SmallString<100> Buf;390    llvm::raw_svector_ostream Os(Buf);391    Os << "Receiver is " << ptrKind() << " and unsafe.";392 393    PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());394    auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);395    Report->addRange(CallArg->getSourceRange());396    Report->setDeclWithIssue(DeclWithIssue);397    BR->emitReport(std::move(Report));398  }399};400 401class UncountedCallArgsChecker final : public RawPtrRefCallArgsChecker {402public:403  UncountedCallArgsChecker()404      : RawPtrRefCallArgsChecker("Uncounted call argument for a raw "405                                 "pointer/reference parameter") {}406 407  std::optional<bool> isUnsafeType(QualType QT) const final {408    return isUncounted(QT);409  }410 411  std::optional<bool> isUnsafePtr(QualType QT) const final {412    return isUncountedPtr(QT.getCanonicalType());413  }414 415  bool isSafePtr(const CXXRecordDecl *Record) const final {416    return isRefCounted(Record) || isCheckedPtr(Record);417  }418 419  bool isSafePtrType(const QualType type) const final {420    return isRefOrCheckedPtrType(type);421  }422 423  const char *ptrKind() const final { return "uncounted"; }424};425 426class UncheckedCallArgsChecker final : public RawPtrRefCallArgsChecker {427public:428  UncheckedCallArgsChecker()429      : RawPtrRefCallArgsChecker("Unchecked call argument for a raw "430                                 "pointer/reference parameter") {}431 432  std::optional<bool> isUnsafeType(QualType QT) const final {433    return isUnchecked(QT);434  }435 436  std::optional<bool> isUnsafePtr(QualType QT) const final {437    return isUncheckedPtr(QT.getCanonicalType());438  }439 440  bool isSafePtr(const CXXRecordDecl *Record) const final {441    return isRefCounted(Record) || isCheckedPtr(Record);442  }443 444  bool isSafePtrType(const QualType type) const final {445    return isRefOrCheckedPtrType(type);446  }447 448  bool isSafeExpr(const Expr *E) const final {449    return isExprToGetCheckedPtrCapableMember(E);450  }451 452  const char *ptrKind() const final { return "unchecked"; }453};454 455class UnretainedCallArgsChecker final : public RawPtrRefCallArgsChecker {456public:457  UnretainedCallArgsChecker()458      : RawPtrRefCallArgsChecker("Unretained call argument for a raw "459                                 "pointer/reference parameter") {460    RTC = RetainTypeChecker();461  }462 463  std::optional<bool> isUnsafeType(QualType QT) const final {464    return RTC->isUnretained(QT);465  }466 467  std::optional<bool> isUnsafePtr(QualType QT) const final {468    return RTC->isUnretained(QT);469  }470 471  bool isSafePtr(const CXXRecordDecl *Record) const final {472    return isRetainPtrOrOSPtr(Record);473  }474 475  bool isSafePtrType(const QualType type) const final {476    return isRetainPtrOrOSPtrType(type);477  }478 479  bool isSafeExpr(const Expr *E) const final {480    return ento::cocoa::isCocoaObjectRef(E->getType()) &&481           isa<ObjCMessageExpr>(E);482  }483 484  bool isSafeDecl(const Decl *D) const final {485    // Treat NS/CF globals in system header as immortal.486    return BR->getSourceManager().isInSystemHeader(D->getLocation());487  }488 489  const char *ptrKind() const final { return "unretained"; }490};491 492} // namespace493 494void ento::registerUncountedCallArgsChecker(CheckerManager &Mgr) {495  Mgr.registerChecker<UncountedCallArgsChecker>();496}497 498bool ento::shouldRegisterUncountedCallArgsChecker(const CheckerManager &) {499  return true;500}501 502void ento::registerUncheckedCallArgsChecker(CheckerManager &Mgr) {503  Mgr.registerChecker<UncheckedCallArgsChecker>();504}505 506bool ento::shouldRegisterUncheckedCallArgsChecker(const CheckerManager &) {507  return true;508}509 510void ento::registerUnretainedCallArgsChecker(CheckerManager &Mgr) {511  Mgr.registerChecker<UnretainedCallArgsChecker>();512}513 514bool ento::shouldRegisterUnretainedCallArgsChecker(const CheckerManager &) {515  return true;516}517