text: //== CStringSyntaxChecker.cpp - CoreFoundation containers API *- C++ -*-==//
  //
  //                     The LLVM Compiler Infrastructure
  //
  // This file is distributed under the University of Illinois Open Source
  // License. See LICENSE.TXT for details.
  //
  //===----------------------------------------------------------------------===//
  //
  // An AST checker that looks for common pitfalls when using C string APIs.
  //  - Identifies erroneous patterns in the last argument to strncat - the number
  //    of bytes to copy.
  //
  //===----------------------------------------------------------------------===//
  #include "ClangSACheckers.h"
  #include "clang/AST/Expr.h"
  #include "clang/AST/OperationKinds.h"
  #include "clang/AST/StmtVisitor.h"
  #include "clang/Analysis/AnalysisContext.h"
  #include "clang/Basic/TargetInfo.h"
  #include "clang/Basic/TypeTraits.h"
  #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
  #include "clang/StaticAnalyzer/Core/Checker.h"
  #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
  #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
  #include "llvm/ADT/SmallString.h"
  #include "llvm/Support/raw_ostream.h"
  
  using namespace clang;
  using namespace ento;
  
  namespace {
  class WalkAST: public StmtVisitor<WalkAST> {
    BugReporter &BR;
    AnalysisDeclContext* AC;
  
    /// Check if two expressions refer to the same declaration.
    inline bool sameDecl(const Expr *A1, const Expr *A2) {
      if (const DeclRefExpr *D1 = dyn_cast<DeclRefExpr>(A1->IgnoreParenCasts()))
        if (const DeclRefExpr *D2 = dyn_cast<DeclRefExpr>(A2->IgnoreParenCasts()))
          return D1->getDecl() == D2->getDecl();
      return false;
    }
  
    /// Check if the expression E is a sizeof(WithArg).
    inline bool isSizeof(const Expr *E, const Expr *WithArg) {
      if (const UnaryExprOrTypeTraitExpr *UE =
      dyn_cast<UnaryExprOrTypeTraitExpr>(E))
        if (UE->getKind() == UETT_SizeOf)
          return sameDecl(UE->getArgumentExpr(), WithArg);
      return false;
    }
  
    /// Check if the expression E is a strlen(WithArg).
    inline bool isStrlen(const Expr *E, const Expr *WithArg) {
      if (const CallExpr *CE = dyn_cast<CallExpr>(E)) {
        const FunctionDecl *FD = CE->getDirectCallee();
        if (!FD)
          return false;
        return (CheckerContext::isCLibraryFunction(FD, "strlen") &&
                sameDecl(CE->getArg(0), WithArg));
      }
      return false;
    }
  
    /// Check if the expression is an integer literal with value 1.
    inline bool isOne(const Expr *E) {
      if (const IntegerLiteral *IL = dyn_cast<IntegerLiteral>(E))
        return (IL->getValue().isIntN(1));
      return false;
    }
  
    inline StringRef getPrintableName(const Expr *E) {
      if (const DeclRefExpr *D = dyn_cast<DeclRefExpr>(E->IgnoreParenCasts()))
        return D->getDecl()->getName();
      return StringRef();
    }
  
    /// Identify erroneous patterns in the last argument to strncat - the number
    /// of bytes to copy.
    bool containsBadStrncatPattern(const CallExpr *CE);
  
  public:
    WalkAST(BugReporter &br, AnalysisDeclContext* ac) :
        BR(br), AC(ac) {
    }
  
    // Statement visitor methods.
    void VisitChildren(Stmt *S);
    void VisitStmt(Stmt *S) {
      VisitChildren(S);
    }
    void VisitCallExpr(CallExpr *CE);
  };
  } // end anonymous namespace
  
  // The correct size argument should look like following:
  //   strncat(dst, src, sizeof(dst) - strlen(dest) - 1);
  // We look for the following anti-patterns:
  //   - strncat(dst, src, sizeof(dst) - strlen(dst));
  //   - strncat(dst, src, sizeof(dst) - 1);
  //   - strncat(dst, src, sizeof(dst));
  bool WalkAST::containsBadStrncatPattern(const CallExpr *CE) {
    if (CE->getNumArgs() != 3)
      return false;
    const Expr *DstArg = CE->getArg(0);
    const Expr *SrcArg = CE->getArg(1);
    const Expr *LenArg = CE->getArg(2);
  
    // Identify wrong size expressions, which are commonly used instead.
    if (const BinaryOperator *BE =
                dyn_cast<BinaryOperator>(LenArg->IgnoreParenCasts())) {
      // - sizeof(dst) - strlen(dst)
      if (BE->getOpcode() == BO_Sub) {
        const Expr *L = BE->getLHS();
        const Expr *R = BE->getRHS();
        if (isSizeof(L, DstArg) && isStrlen(R, DstArg))
          return true;
  
        // - sizeof(dst) - 1
        if (isSizeof(L, DstArg) && isOne(R->IgnoreParenCasts()))
          return true;
      }
    }
    // - sizeof(dst)
    if (isSizeof(LenArg, DstArg))
      return true;
  
    // - sizeof(src)
    if (isSizeof(LenArg, SrcArg))
      return true;
    return false;
  }
  
  void WalkAST::VisitCallExpr(CallExpr *CE) {
    const FunctionDecl *FD = CE->getDirectCallee();
    if (!FD)
      return;
  
    if (CheckerContext::isCLibraryFunction(FD, "strncat")) {
      if (containsBadStrncatPattern(CE)) {
        const Expr *DstArg = CE->getArg(0);
        const Expr *LenArg = CE->getArg(2);
        PathDiagnosticLocation Loc =
          PathDiagnosticLocation::createBegin(LenArg, BR.getSourceManager(), AC);
  
        StringRef DstName = getPrintableName(DstArg);
  
        SmallString<256> S;
        llvm::raw_svector_ostream os(S);
        os << "Potential buffer overflow. ";
        if (!DstName.empty()) {
          os << "Replace with 'sizeof(" << DstName << ") "
                "- strlen(" << DstName <<") - 1'";
          os << " or u";
        } else
          os << "U";
        os << "se a safer 'strlcat' API";
  
        BR.EmitBasicReport(FD, "Anti-pattern in the argument", "C String API",
                           os.str(), Loc, LenArg->getSourceRange());
      }
    }
  
    // Recurse and check children.
    VisitChildren(CE);
  }
  
  void WalkAST::VisitChildren(Stmt *S) {
    for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I != E;
        ++I)
      if (Stmt *child = *I)
        Visit(child);
  }
  
  namespace {
  class CStringSyntaxChecker: public Checker<check::ASTCodeBody> {
  public:
  
    void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr,
        BugReporter &BR) const {
      WalkAST walker(BR, Mgr.getAnalysisDeclContext(D));
      walker.Visit(D->getBody());
    }
  };
  }
  
  void ento::registerCStringSyntaxChecker(CheckerManager &mgr) {
    mgr.registerChecker<CStringSyntaxChecker>();
  }
  
  