brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.5 KiB · 6978d81 Raw
178 lines · cpp
1//== ObjCContainersASTChecker.cpp - CoreFoundation containers API *- 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// An AST checker that looks for common pitfalls when using 'CFArray',10// 'CFDictionary', 'CFSet' APIs.11//12//===----------------------------------------------------------------------===//13#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"14#include "clang/AST/StmtVisitor.h"15#include "clang/Analysis/AnalysisDeclContext.h"16#include "clang/Basic/TargetInfo.h"17#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"18#include "clang/StaticAnalyzer/Core/Checker.h"19#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"20#include "llvm/ADT/SmallString.h"21#include "llvm/Support/raw_ostream.h"22 23using namespace clang;24using namespace ento;25 26namespace {27class WalkAST : public StmtVisitor<WalkAST> {28  BugReporter &BR;29  const CheckerBase *Checker;30  AnalysisDeclContext* AC;31  ASTContext &ASTC;32  uint64_t PtrWidth;33 34  /// Check if the type has pointer size (very conservative).35  inline bool isPointerSize(const Type *T) {36    if (!T)37      return true;38    if (T->isIncompleteType())39      return true;40    return (ASTC.getTypeSize(T) == PtrWidth);41  }42 43  /// Check if the type is a pointer/array to pointer sized values.44  inline bool hasPointerToPointerSizedType(const Expr *E) {45    QualType T = E->getType();46 47    // The type could be either a pointer or array.48    const Type *TP = T.getTypePtr();49    QualType PointeeT = TP->getPointeeType();50    if (!PointeeT.isNull()) {51      // If the type is a pointer to an array, check the size of the array52      // elements. To avoid false positives coming from assumption that the53      // values x and &x are equal when x is an array.54      if (const Type *TElem = PointeeT->getArrayElementTypeNoTypeQual())55        if (isPointerSize(TElem))56          return true;57 58      // Else, check the pointee size.59      return isPointerSize(PointeeT.getTypePtr());60    }61 62    if (const Type *TElem = TP->getArrayElementTypeNoTypeQual())63      return isPointerSize(TElem);64 65    // The type must be an array/pointer type.66 67    // This could be a null constant, which is allowed.68    return static_cast<bool>(69        E->isNullPointerConstant(ASTC, Expr::NPC_ValueDependentIsNull));70  }71 72public:73  WalkAST(BugReporter &br, const CheckerBase *checker, AnalysisDeclContext *ac)74      : BR(br), Checker(checker), AC(ac), ASTC(AC->getASTContext()),75        PtrWidth(ASTC.getTargetInfo().getPointerWidth(LangAS::Default)) {}76 77  // Statement visitor methods.78  void VisitChildren(Stmt *S);79  void VisitStmt(Stmt *S) { VisitChildren(S); }80  void VisitCallExpr(CallExpr *CE);81};82} // end anonymous namespace83 84static StringRef getCalleeName(CallExpr *CE) {85  const FunctionDecl *FD = CE->getDirectCallee();86  if (!FD)87    return StringRef();88 89  IdentifierInfo *II = FD->getIdentifier();90  if (!II)   // if no identifier, not a simple C function91    return StringRef();92 93  return II->getName();94}95 96void WalkAST::VisitCallExpr(CallExpr *CE) {97  StringRef Name = getCalleeName(CE);98  if (Name.empty())99    return;100 101  const Expr *Arg = nullptr;102  unsigned ArgNum;103 104  if (Name == "CFArrayCreate" || Name == "CFSetCreate") {105    if (CE->getNumArgs() != 4)106      return;107    ArgNum = 1;108    Arg = CE->getArg(ArgNum)->IgnoreParenCasts();109    if (hasPointerToPointerSizedType(Arg))110        return;111  } else if (Name == "CFDictionaryCreate") {112    if (CE->getNumArgs() != 6)113      return;114    // Check first argument.115    ArgNum = 1;116    Arg = CE->getArg(ArgNum)->IgnoreParenCasts();117    if (hasPointerToPointerSizedType(Arg)) {118      // Check second argument.119      ArgNum = 2;120      Arg = CE->getArg(ArgNum)->IgnoreParenCasts();121      if (hasPointerToPointerSizedType(Arg))122        // Both are good, return.123        return;124    }125  }126 127  if (Arg) {128    assert(ArgNum == 1 || ArgNum == 2);129 130    SmallString<64> BufName;131    llvm::raw_svector_ostream OsName(BufName);132    OsName << " Invalid use of '" << Name << "'" ;133 134    SmallString<256> Buf;135    llvm::raw_svector_ostream Os(Buf);136    // Use "second" and "third" since users will expect 1-based indexing137    // for parameter names when mentioned in prose.138    Os << " The " << ((ArgNum == 1) ? "second" : "third") << " argument to '"139       << Name << "' must be a C array of pointer-sized values, not '"140       << Arg->getType() << "'";141 142    PathDiagnosticLocation CELoc =143        PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);144    BR.EmitBasicReport(AC->getDecl(), Checker, OsName.str(),145                       categories::CoreFoundationObjectiveC, Os.str(), CELoc,146                       Arg->getSourceRange());147  }148 149  // Recurse and check children.150  VisitChildren(CE);151}152 153void WalkAST::VisitChildren(Stmt *S) {154  for (Stmt *Child : S->children())155    if (Child)156      Visit(Child);157}158 159namespace {160class ObjCContainersASTChecker : public Checker<check::ASTCodeBody> {161public:162 163  void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr,164                        BugReporter &BR) const {165    WalkAST walker(BR, this, Mgr.getAnalysisDeclContext(D));166    walker.Visit(D->getBody());167  }168};169}170 171void ento::registerObjCContainersASTChecker(CheckerManager &mgr) {172  mgr.registerChecker<ObjCContainersASTChecker>();173}174 175bool ento::shouldRegisterObjCContainersASTChecker(const CheckerManager &mgr) {176  return true;177}178