brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.9 KiB · 4da12f7 Raw
169 lines · cpp
1//===- CallDescription.cpp - function/method call matching     --*- 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/// \file This file defines a generic mechanism for matching for function and10/// method calls of C, C++, and Objective-C languages. Instances of these11/// classes are frequently used together with the CallEvent classes.12//13//===----------------------------------------------------------------------===//14 15#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"16#include "clang/AST/Decl.h"17#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"18#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"19#include <iterator>20#include <optional>21 22using namespace llvm;23using namespace clang;24 25using MaybeCount = std::optional<unsigned>;26 27// A constructor helper.28static MaybeCount readRequiredParams(MaybeCount RequiredArgs,29                                     MaybeCount RequiredParams) {30  if (RequiredParams)31    return RequiredParams;32  if (RequiredArgs)33    return RequiredArgs;34  return std::nullopt;35}36 37ento::CallDescription::CallDescription(Mode MatchAs,38                                       ArrayRef<StringRef> QualifiedName,39                                       MaybeCount RequiredArgs /*= None*/,40                                       MaybeCount RequiredParams /*= None*/)41    : RequiredArgs(RequiredArgs),42      RequiredParams(readRequiredParams(RequiredArgs, RequiredParams)),43      MatchAs(MatchAs) {44  assert(!QualifiedName.empty());45  this->QualifiedName.reserve(QualifiedName.size());46  llvm::transform(QualifiedName, std::back_inserter(this->QualifiedName),47                  [](StringRef From) { return From.str(); });48}49 50bool ento::CallDescription::matches(const CallEvent &Call) const {51  // FIXME: Add ObjC Message support.52  if (Call.getKind() == CE_ObjCMessage)53    return false;54 55  const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());56  if (!FD)57    return false;58 59  return matchesImpl(FD, Call.getNumArgs(), Call.parameters().size());60}61 62bool ento::CallDescription::matchesAsWritten(const CallExpr &CE) const {63  const auto *FD = dyn_cast_or_null<FunctionDecl>(CE.getCalleeDecl());64  if (!FD)65    return false;66 67  return matchesImpl(FD, CE.getNumArgs(), FD->param_size());68}69 70bool ento::CallDescription::matchNameOnly(const NamedDecl *ND) const {71  DeclarationName Name = ND->getDeclName();72  if (const auto *NameII = Name.getAsIdentifierInfo()) {73    if (!II)74      II = &ND->getASTContext().Idents.get(getFunctionName());75 76    return NameII == *II; // Fast case.77  }78 79  // Fallback to the slow stringification and comparison for:80  // C++ overloaded operators, constructors, destructors, etc.81  // FIXME This comparison is way SLOWER than comparing pointers.82  // At some point in the future, we should compare FunctionDecl pointers.83  return Name.getAsString() == getFunctionName();84}85 86bool ento::CallDescription::matchQualifiedNameParts(const Decl *D) const {87  const auto FindNextNamespaceOrRecord =88      [](const DeclContext *Ctx) -> const DeclContext * {89    while (Ctx && !isa<NamespaceDecl, RecordDecl>(Ctx))90      Ctx = Ctx->getParent();91    return Ctx;92  };93 94  auto QualifierPartsIt = begin_qualified_name_parts();95  const auto QualifierPartsEndIt = end_qualified_name_parts();96 97  // Match namespace and record names. Skip unrelated names if they don't98  // match.99  const DeclContext *Ctx = FindNextNamespaceOrRecord(D->getDeclContext());100  for (; Ctx && QualifierPartsIt != QualifierPartsEndIt;101       Ctx = FindNextNamespaceOrRecord(Ctx->getParent())) {102    // If not matched just continue and try matching for the next one.103    if (cast<NamedDecl>(Ctx)->getName() != *QualifierPartsIt)104      continue;105    ++QualifierPartsIt;106  }107 108  // We matched if we consumed all expected qualifier segments.109  return QualifierPartsIt == QualifierPartsEndIt;110}111 112bool ento::CallDescription::matchesImpl(const FunctionDecl *FD, size_t ArgCount,113                                        size_t ParamCount) const {114  if (!FD)115    return false;116 117  const bool isMethod = isa<CXXMethodDecl>(FD);118 119  if (MatchAs == Mode::SimpleFunc && isMethod)120    return false;121 122  if (MatchAs == Mode::CXXMethod && !isMethod)123    return false;124 125  if (MatchAs == Mode::CLibraryMaybeHardened) {126    // In addition to accepting FOO() with CLibrary rules, we also want to127    // accept calls to __FOO_chk() and __builtin___FOO_chk().128    if (CheckerContext::isCLibraryFunction(FD) &&129        CheckerContext::isHardenedVariantOf(FD, getFunctionName())) {130      // Check that the actual argument/parameter counts are greater or equal131      // to the required counts. (Setting a requirement to std::nullopt matches132      // anything, so in that case value_or ensures that the value is compared133      // with itself.)134      return (RequiredArgs.value_or(ArgCount) <= ArgCount &&135              RequiredParams.value_or(ParamCount) <= ParamCount);136    }137  }138 139  if (RequiredArgs.value_or(ArgCount) != ArgCount ||140      RequiredParams.value_or(ParamCount) != ParamCount)141    return false;142 143  if (MatchAs == Mode::CLibrary || MatchAs == Mode::CLibraryMaybeHardened)144    return CheckerContext::isCLibraryFunction(FD, getFunctionName());145 146  if (!matchNameOnly(FD))147    return false;148 149  if (!hasQualifiedNameParts())150    return true;151 152  return matchQualifiedNameParts(FD);153}154 155ento::CallDescriptionSet::CallDescriptionSet(156    std::initializer_list<CallDescription> &&List) {157  Impl.LinearMap.reserve(List.size());158  for (const CallDescription &CD : List)159    Impl.LinearMap.push_back({CD, /*unused*/ true});160}161 162bool ento::CallDescriptionSet::contains(const CallEvent &Call) const {163  return static_cast<bool>(Impl.lookup(Call));164}165 166bool ento::CallDescriptionSet::containsAsWritten(const CallExpr &CE) const {167  return static_cast<bool>(Impl.lookupAsWritten(CE));168}169