brintos

brintos / llvm-project-archived public Read only

0
0
Text · 39.4 KiB · 5e75c1c Raw
1118 lines · cpp
1//==- CheckSecuritySyntaxOnly.cpp - Basic security checks --------*- 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//  This file defines a set of flow-insensitive security checks.10//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/ADT/StringSwitch.h"22#include "llvm/Support/raw_ostream.h"23 24using namespace clang;25using namespace ento;26 27static bool isArc4RandomAvailable(const ASTContext &Ctx) {28  const llvm::Triple &T = Ctx.getTargetInfo().getTriple();29  return T.getVendor() == llvm::Triple::Apple ||30         T.isOSFreeBSD() ||31         T.isOSNetBSD() ||32         T.isOSOpenBSD() ||33         T.isOSDragonFly();34}35 36namespace {37struct ChecksFilter {38  bool check_bcmp = false;39  bool check_bcopy = false;40  bool check_bzero = false;41  bool check_gets = false;42  bool check_getpw = false;43  bool check_mktemp = false;44  bool check_mkstemp = false;45  bool check_strcpy = false;46  bool check_DeprecatedOrUnsafeBufferHandling = false;47  bool check_rand = false;48  bool check_vfork = false;49  bool check_FloatLoopCounter = false;50  bool check_UncheckedReturn = false;51  bool check_decodeValueOfObjCType = false;52 53  CheckerNameRef checkName_bcmp;54  CheckerNameRef checkName_bcopy;55  CheckerNameRef checkName_bzero;56  CheckerNameRef checkName_gets;57  CheckerNameRef checkName_getpw;58  CheckerNameRef checkName_mktemp;59  CheckerNameRef checkName_mkstemp;60  CheckerNameRef checkName_strcpy;61  CheckerNameRef checkName_DeprecatedOrUnsafeBufferHandling;62  CheckerNameRef checkName_rand;63  CheckerNameRef checkName_vfork;64  CheckerNameRef checkName_FloatLoopCounter;65  CheckerNameRef checkName_UncheckedReturn;66  CheckerNameRef checkName_decodeValueOfObjCType;67};68 69class WalkAST : public StmtVisitor<WalkAST> {70  BugReporter &BR;71  AnalysisDeclContext* AC;72  enum { num_setids = 6 };73  IdentifierInfo *II_setid[num_setids];74 75  const bool CheckRand;76  const ChecksFilter &filter;77 78public:79  WalkAST(BugReporter &br, AnalysisDeclContext* ac,80          const ChecksFilter &f)81  : BR(br), AC(ac), II_setid(),82    CheckRand(isArc4RandomAvailable(BR.getContext())),83    filter(f) {}84 85  // Statement visitor methods.86  void VisitCallExpr(CallExpr *CE);87  void VisitObjCMessageExpr(ObjCMessageExpr *CE);88  void VisitForStmt(ForStmt *S);89  void VisitCompoundStmt (CompoundStmt *S);90  void VisitStmt(Stmt *S) { VisitChildren(S); }91 92  void VisitChildren(Stmt *S);93 94  // Helpers.95  bool checkCall_strCommon(const CallExpr *CE, const FunctionDecl *FD);96 97  typedef void (WalkAST::*FnCheck)(const CallExpr *, const FunctionDecl *);98  typedef void (WalkAST::*MsgCheck)(const ObjCMessageExpr *);99 100  // Checker-specific methods.101  void checkLoopConditionForFloat(const ForStmt *FS);102  void checkCall_bcmp(const CallExpr *CE, const FunctionDecl *FD);103  void checkCall_bcopy(const CallExpr *CE, const FunctionDecl *FD);104  void checkCall_bzero(const CallExpr *CE, const FunctionDecl *FD);105  void checkCall_gets(const CallExpr *CE, const FunctionDecl *FD);106  void checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD);107  void checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD);108  void checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD);109  void checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD);110  void checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD);111  void checkDeprecatedOrUnsafeBufferHandling(const CallExpr *CE,112                                             const FunctionDecl *FD);113  void checkCall_rand(const CallExpr *CE, const FunctionDecl *FD);114  void checkCall_random(const CallExpr *CE, const FunctionDecl *FD);115  void checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD);116  void checkMsg_decodeValueOfObjCType(const ObjCMessageExpr *ME);117  void checkUncheckedReturnValue(CallExpr *CE);118};119} // end anonymous namespace120 121//===----------------------------------------------------------------------===//122// AST walking.123//===----------------------------------------------------------------------===//124 125void WalkAST::VisitChildren(Stmt *S) {126  for (Stmt *Child : S->children())127    if (Child)128      Visit(Child);129}130 131void WalkAST::VisitCallExpr(CallExpr *CE) {132  // Get the callee.133  const FunctionDecl *FD = CE->getDirectCallee();134 135  if (!FD)136    return;137 138  // Get the name of the callee. If it's a builtin, strip off the prefix.139  IdentifierInfo *II = FD->getIdentifier();140  if (!II)   // if no identifier, not a simple C function141    return;142  StringRef Name = II->getName();143  Name.consume_front("__builtin_");144 145  // Set the evaluation function by switching on the callee name.146  FnCheck evalFunction =147      llvm::StringSwitch<FnCheck>(Name)148          .Case("bcmp", &WalkAST::checkCall_bcmp)149          .Case("bcopy", &WalkAST::checkCall_bcopy)150          .Case("bzero", &WalkAST::checkCall_bzero)151          .Case("gets", &WalkAST::checkCall_gets)152          .Case("getpw", &WalkAST::checkCall_getpw)153          .Case("mktemp", &WalkAST::checkCall_mktemp)154          .Case("mkstemp", &WalkAST::checkCall_mkstemp)155          .Case("mkdtemp", &WalkAST::checkCall_mkstemp)156          .Case("mkstemps", &WalkAST::checkCall_mkstemp)157          .Cases({"strcpy", "__strcpy_chk"}, &WalkAST::checkCall_strcpy)158          .Cases({"strcat", "__strcat_chk"}, &WalkAST::checkCall_strcat)159          .Cases({"sprintf", "vsprintf", "scanf", "wscanf", "fscanf", "fwscanf",160                  "vscanf", "vwscanf", "vfscanf", "vfwscanf"},161                 &WalkAST::checkDeprecatedOrUnsafeBufferHandling)162          .Cases({"sscanf", "swscanf", "vsscanf", "vswscanf", "swprintf",163                  "snprintf", "vswprintf", "vsnprintf", "memcpy", "memmove"},164                 &WalkAST::checkDeprecatedOrUnsafeBufferHandling)165          .Cases({"strncpy", "strncat", "memset", "fprintf"},166                 &WalkAST::checkDeprecatedOrUnsafeBufferHandling)167          .Case("drand48", &WalkAST::checkCall_rand)168          .Case("erand48", &WalkAST::checkCall_rand)169          .Case("jrand48", &WalkAST::checkCall_rand)170          .Case("lrand48", &WalkAST::checkCall_rand)171          .Case("mrand48", &WalkAST::checkCall_rand)172          .Case("nrand48", &WalkAST::checkCall_rand)173          .Case("lcong48", &WalkAST::checkCall_rand)174          .Case("rand", &WalkAST::checkCall_rand)175          .Case("rand_r", &WalkAST::checkCall_rand)176          .Case("random", &WalkAST::checkCall_random)177          .Case("vfork", &WalkAST::checkCall_vfork)178          .Default(nullptr);179 180  // If the callee isn't defined, it is not of security concern.181  // Check and evaluate the call.182  if (evalFunction)183    (this->*evalFunction)(CE, FD);184 185  // Recurse and check children.186  VisitChildren(CE);187}188 189void WalkAST::VisitObjCMessageExpr(ObjCMessageExpr *ME) {190  MsgCheck evalFunction =191      llvm::StringSwitch<MsgCheck>(ME->getSelector().getAsString())192          .Case("decodeValueOfObjCType:at:",193                &WalkAST::checkMsg_decodeValueOfObjCType)194          .Default(nullptr);195 196  if (evalFunction)197    (this->*evalFunction)(ME);198 199  // Recurse and check children.200  VisitChildren(ME);201}202 203void WalkAST::VisitCompoundStmt(CompoundStmt *S) {204  for (Stmt *Child : S->children())205    if (Child) {206      if (CallExpr *CE = dyn_cast<CallExpr>(Child))207        checkUncheckedReturnValue(CE);208      Visit(Child);209    }210}211 212void WalkAST::VisitForStmt(ForStmt *FS) {213  checkLoopConditionForFloat(FS);214 215  // Recurse and check children.216  VisitChildren(FS);217}218 219//===----------------------------------------------------------------------===//220// Check: floating point variable used as loop counter.221// Implements: CERT security coding advisory FLP-30.222//===----------------------------------------------------------------------===//223 224// Returns either 'x' or 'y', depending on which one of them is incremented225// in 'expr', or nullptr if none of them is incremented.226static const DeclRefExpr*227getIncrementedVar(const Expr *expr, const VarDecl *x, const VarDecl *y) {228  expr = expr->IgnoreParenCasts();229 230  if (const BinaryOperator *B = dyn_cast<BinaryOperator>(expr)) {231    if (!(B->isAssignmentOp() || B->isCompoundAssignmentOp() ||232          B->getOpcode() == BO_Comma))233      return nullptr;234 235    if (const DeclRefExpr *lhs = getIncrementedVar(B->getLHS(), x, y))236      return lhs;237 238    if (const DeclRefExpr *rhs = getIncrementedVar(B->getRHS(), x, y))239      return rhs;240 241    return nullptr;242  }243 244  if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(expr)) {245    const NamedDecl *ND = DR->getDecl();246    return ND == x || ND == y ? DR : nullptr;247  }248 249  if (const UnaryOperator *U = dyn_cast<UnaryOperator>(expr))250    return U->isIncrementDecrementOp()251      ? getIncrementedVar(U->getSubExpr(), x, y) : nullptr;252 253  return nullptr;254}255 256/// CheckLoopConditionForFloat - This check looks for 'for' statements that257///  use a floating point variable as a loop counter.258///  CERT: FLP30-C, FLP30-CPP.259///260void WalkAST::checkLoopConditionForFloat(const ForStmt *FS) {261  if (!filter.check_FloatLoopCounter)262    return;263 264  // Does the loop have a condition?265  const Expr *condition = FS->getCond();266 267  if (!condition)268    return;269 270  // Does the loop have an increment?271  const Expr *increment = FS->getInc();272 273  if (!increment)274    return;275 276  // Strip away '()' and casts.277  condition = condition->IgnoreParenCasts();278  increment = increment->IgnoreParenCasts();279 280  // Is the loop condition a comparison?281  const BinaryOperator *B = dyn_cast<BinaryOperator>(condition);282 283  if (!B)284    return;285 286  // Is this a comparison?287  if (!(B->isRelationalOp() || B->isEqualityOp()))288    return;289 290  // Are we comparing variables?291  const DeclRefExpr *drLHS =292    dyn_cast<DeclRefExpr>(B->getLHS()->IgnoreParenLValueCasts());293  const DeclRefExpr *drRHS =294    dyn_cast<DeclRefExpr>(B->getRHS()->IgnoreParenLValueCasts());295 296  // Does at least one of the variables have a floating point type?297  drLHS = drLHS && drLHS->getType()->isRealFloatingType() ? drLHS : nullptr;298  drRHS = drRHS && drRHS->getType()->isRealFloatingType() ? drRHS : nullptr;299 300  if (!drLHS && !drRHS)301    return;302 303  const VarDecl *vdLHS = drLHS ? dyn_cast<VarDecl>(drLHS->getDecl()) : nullptr;304  const VarDecl *vdRHS = drRHS ? dyn_cast<VarDecl>(drRHS->getDecl()) : nullptr;305 306  if (!vdLHS && !vdRHS)307    return;308 309  // Does either variable appear in increment?310  const DeclRefExpr *drInc = getIncrementedVar(increment, vdLHS, vdRHS);311  if (!drInc)312    return;313 314  const VarDecl *vdInc = cast<VarDecl>(drInc->getDecl());315  assert(vdInc && (vdInc == vdLHS || vdInc == vdRHS));316 317  // Emit the error.  First figure out which DeclRefExpr in the condition318  // referenced the compared variable.319  const DeclRefExpr *drCond = vdLHS == vdInc ? drLHS : drRHS;320 321  SmallVector<SourceRange, 2> ranges;322  SmallString<256> sbuf;323  llvm::raw_svector_ostream os(sbuf);324 325  os << "Variable '" << drCond->getDecl()->getName()326     << "' with floating point type '" << drCond->getType()327     << "' should not be used as a loop counter";328 329  ranges.push_back(drCond->getSourceRange());330  ranges.push_back(drInc->getSourceRange());331 332  const char *bugType = "Floating point variable used as loop counter";333 334  PathDiagnosticLocation FSLoc =335    PathDiagnosticLocation::createBegin(FS, BR.getSourceManager(), AC);336  BR.EmitBasicReport(AC->getDecl(), filter.checkName_FloatLoopCounter,337                     bugType, "Security", os.str(),338                     FSLoc, ranges);339}340 341//===----------------------------------------------------------------------===//342// Check: Any use of bcmp.343// CWE-477: Use of Obsolete Functions344// bcmp was deprecated in POSIX.1-2008345//===----------------------------------------------------------------------===//346 347void WalkAST::checkCall_bcmp(const CallExpr *CE, const FunctionDecl *FD) {348  if (!filter.check_bcmp)349    return;350 351  const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();352  if (!FPT)353    return;354 355  // Verify that the function takes three arguments.356  if (FPT->getNumParams() != 3)357    return;358 359  for (int i = 0; i < 2; i++) {360    // Verify the first and second argument type is void*.361    const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>();362    if (!PT)363      return;364 365    if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy)366      return;367  }368 369  // Verify the third argument type is integer.370  if (!FPT->getParamType(2)->isIntegralOrUnscopedEnumerationType())371    return;372 373  // Issue a warning.374  PathDiagnosticLocation CELoc =375    PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);376  BR.EmitBasicReport(AC->getDecl(), filter.checkName_bcmp,377                     "Use of deprecated function in call to 'bcmp()'",378                     "Security",379                     "The bcmp() function is obsoleted by memcmp().",380                     CELoc, CE->getCallee()->getSourceRange());381}382 383//===----------------------------------------------------------------------===//384// Check: Any use of bcopy.385// CWE-477: Use of Obsolete Functions386// bcopy was deprecated in POSIX.1-2008387//===----------------------------------------------------------------------===//388 389void WalkAST::checkCall_bcopy(const CallExpr *CE, const FunctionDecl *FD) {390  if (!filter.check_bcopy)391    return;392 393  const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();394  if (!FPT)395    return;396 397  // Verify that the function takes three arguments.398  if (FPT->getNumParams() != 3)399    return;400 401  for (int i = 0; i < 2; i++) {402    // Verify the first and second argument type is void*.403    const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>();404    if (!PT)405      return;406 407    if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy)408      return;409  }410 411  // Verify the third argument type is integer.412  if (!FPT->getParamType(2)->isIntegralOrUnscopedEnumerationType())413    return;414 415  // Issue a warning.416  PathDiagnosticLocation CELoc =417    PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);418  BR.EmitBasicReport(AC->getDecl(), filter.checkName_bcopy,419                     "Use of deprecated function in call to 'bcopy()'",420                     "Security",421                     "The bcopy() function is obsoleted by memcpy() "422                     "or memmove().",423                     CELoc, CE->getCallee()->getSourceRange());424}425 426//===----------------------------------------------------------------------===//427// Check: Any use of bzero.428// CWE-477: Use of Obsolete Functions429// bzero was deprecated in POSIX.1-2008430//===----------------------------------------------------------------------===//431 432void WalkAST::checkCall_bzero(const CallExpr *CE, const FunctionDecl *FD) {433  if (!filter.check_bzero)434    return;435 436  const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();437  if (!FPT)438    return;439 440  // Verify that the function takes two arguments.441  if (FPT->getNumParams() != 2)442    return;443 444  // Verify the first argument type is void*.445  const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>();446  if (!PT)447    return;448 449  if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy)450    return;451 452  // Verify the second argument type is integer.453  if (!FPT->getParamType(1)->isIntegralOrUnscopedEnumerationType())454    return;455 456  // Issue a warning.457  PathDiagnosticLocation CELoc =458    PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);459  BR.EmitBasicReport(AC->getDecl(), filter.checkName_bzero,460                     "Use of deprecated function in call to 'bzero()'",461                     "Security",462                     "The bzero() function is obsoleted by memset().",463                     CELoc, CE->getCallee()->getSourceRange());464}465 466 467//===----------------------------------------------------------------------===//468// Check: Any use of 'gets' is insecure. Most man pages literally says this.469//470// Implements (part of): 300-BSI (buildsecurityin.us-cert.gov)471// CWE-242: Use of Inherently Dangerous Function472//===----------------------------------------------------------------------===//473 474void WalkAST::checkCall_gets(const CallExpr *CE, const FunctionDecl *FD) {475  if (!filter.check_gets)476    return;477 478  const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();479  if (!FPT)480    return;481 482  // Verify that the function takes a single argument.483  if (FPT->getNumParams() != 1)484    return;485 486  // Is the argument a 'char*'?487  const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>();488  if (!PT)489    return;490 491  if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)492    return;493 494  // Issue a warning.495  PathDiagnosticLocation CELoc =496    PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);497  BR.EmitBasicReport(AC->getDecl(), filter.checkName_gets,498                     "Potential buffer overflow in call to 'gets'",499                     "Security",500                     "Call to function 'gets' is extremely insecure as it can "501                     "always result in a buffer overflow",502                     CELoc, CE->getCallee()->getSourceRange());503}504 505//===----------------------------------------------------------------------===//506// Check: Any use of 'getpwd' is insecure.507// CWE-477: Use of Obsolete Functions508//===----------------------------------------------------------------------===//509 510void WalkAST::checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD) {511  if (!filter.check_getpw)512    return;513 514  const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();515  if (!FPT)516    return;517 518  // Verify that the function takes two arguments.519  if (FPT->getNumParams() != 2)520    return;521 522  // Verify the first argument type is integer.523  if (!FPT->getParamType(0)->isIntegralOrUnscopedEnumerationType())524    return;525 526  // Verify the second argument type is char*.527  const PointerType *PT = FPT->getParamType(1)->getAs<PointerType>();528  if (!PT)529    return;530 531  if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)532    return;533 534  // Issue a warning.535  PathDiagnosticLocation CELoc =536    PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);537  BR.EmitBasicReport(AC->getDecl(), filter.checkName_getpw,538                     "Potential buffer overflow in call to 'getpw'",539                     "Security",540                     "The getpw() function is dangerous as it may overflow the "541                     "provided buffer. It is obsoleted by getpwuid().",542                     CELoc, CE->getCallee()->getSourceRange());543}544 545//===----------------------------------------------------------------------===//546// Check: Any use of 'mktemp' is insecure.  It is obsoleted by mkstemp().547// CWE-377: Insecure Temporary File548//===----------------------------------------------------------------------===//549 550void WalkAST::checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD) {551  if (!filter.check_mktemp) {552    // Fall back to the security check of looking for enough 'X's in the553    // format string, since that is a less severe warning.554    checkCall_mkstemp(CE, FD);555    return;556  }557 558  const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();559  if(!FPT)560    return;561 562  // Verify that the function takes a single argument.563  if (FPT->getNumParams() != 1)564    return;565 566  // Verify that the argument is Pointer Type.567  const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>();568  if (!PT)569    return;570 571  // Verify that the argument is a 'char*'.572  if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)573    return;574 575  // Issue a warning.576  PathDiagnosticLocation CELoc =577    PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);578  BR.EmitBasicReport(AC->getDecl(), filter.checkName_mktemp,579                     "Potential insecure temporary file in call 'mktemp'",580                     "Security",581                     "Call to function 'mktemp' is insecure as it always "582                     "creates or uses insecure temporary file.  Use 'mkstemp' "583                     "instead",584                     CELoc, CE->getCallee()->getSourceRange());585}586 587//===----------------------------------------------------------------------===//588// Check: Use of 'mkstemp', 'mktemp', 'mkdtemp' should contain at least 6 X's.589//===----------------------------------------------------------------------===//590 591void WalkAST::checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD) {592  if (!filter.check_mkstemp)593    return;594 595  StringRef Name = FD->getIdentifier()->getName();596  std::pair<signed, signed> ArgSuffix =597    llvm::StringSwitch<std::pair<signed, signed> >(Name)598      .Case("mktemp", std::make_pair(0,-1))599      .Case("mkstemp", std::make_pair(0,-1))600      .Case("mkdtemp", std::make_pair(0,-1))601      .Case("mkstemps", std::make_pair(0,1))602      .Default(std::make_pair(-1, -1));603 604  assert(ArgSuffix.first >= 0 && "Unsupported function");605 606  // Check if the number of arguments is consistent with out expectations.607  unsigned numArgs = CE->getNumArgs();608  if ((signed) numArgs <= ArgSuffix.first)609    return;610 611  const StringLiteral *strArg =612    dyn_cast<StringLiteral>(CE->getArg((unsigned)ArgSuffix.first)613                              ->IgnoreParenImpCasts());614 615  // Currently we only handle string literals.  It is possible to do better,616  // either by looking at references to const variables, or by doing real617  // flow analysis.618  if (!strArg || strArg->getCharByteWidth() != 1)619    return;620 621  // Count the number of X's, taking into account a possible cutoff suffix.622  StringRef str = strArg->getString();623  unsigned numX = 0;624  unsigned n = str.size();625 626  // Take into account the suffix.627  unsigned suffix = 0;628  if (ArgSuffix.second >= 0) {629    const Expr *suffixEx = CE->getArg((unsigned)ArgSuffix.second);630    Expr::EvalResult EVResult;631    if (!suffixEx->EvaluateAsInt(EVResult, BR.getContext()))632      return;633    llvm::APSInt Result = EVResult.Val.getInt();634    // FIXME: Issue a warning.635    if (Result.isNegative())636      return;637    suffix = (unsigned) Result.getZExtValue();638    n = (n > suffix) ? n - suffix : 0;639  }640 641  for (unsigned i = 0; i < n; ++i)642    if (str[i] == 'X') ++numX;643 644  if (numX >= 6)645    return;646 647  // Issue a warning.648  PathDiagnosticLocation CELoc =649    PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);650  SmallString<512> buf;651  llvm::raw_svector_ostream out(buf);652  out << "Call to '" << Name << "' should have at least 6 'X's in the"653    " format string to be secure (" << numX << " 'X'";654  if (numX != 1)655    out << 's';656  out << " seen";657  if (suffix) {658    out << ", " << suffix << " character";659    if (suffix > 1)660      out << 's';661    out << " used as a suffix";662  }663  out << ')';664  BR.EmitBasicReport(AC->getDecl(), filter.checkName_mkstemp,665                     "Insecure temporary file creation", "Security",666                     out.str(), CELoc, strArg->getSourceRange());667}668 669//===----------------------------------------------------------------------===//670// Check: Any use of 'strcpy' is insecure.671//672// CWE-119: Improper Restriction of Operations within673// the Bounds of a Memory Buffer674//===----------------------------------------------------------------------===//675 676void WalkAST::checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD) {677  if (!filter.check_strcpy)678    return;679 680  if (!checkCall_strCommon(CE, FD))681    return;682 683  const auto *Target = CE->getArg(0)->IgnoreImpCasts(),684             *Source = CE->getArg(1)->IgnoreImpCasts();685 686  if (const auto *Array = dyn_cast<ConstantArrayType>(Target->getType())) {687    uint64_t ArraySize = BR.getContext().getTypeSize(Array) / 8;688    if (const auto *String = dyn_cast<StringLiteral>(Source)) {689      if (ArraySize >= String->getLength() + 1)690        return;691    }692  }693 694  // Issue a warning.695  PathDiagnosticLocation CELoc =696    PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);697  BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy,698                     "Potential insecure memory buffer bounds restriction in "699                     "call 'strcpy'",700                     "Security",701                     "Call to function 'strcpy' is insecure as it does not "702                     "provide bounding of the memory buffer. Replace "703                     "unbounded copy functions with analogous functions that "704                     "support length arguments such as 'strlcpy'. CWE-119.",705                     CELoc, CE->getCallee()->getSourceRange());706}707 708//===----------------------------------------------------------------------===//709// Check: Any use of 'strcat' is insecure.710//711// CWE-119: Improper Restriction of Operations within712// the Bounds of a Memory Buffer713//===----------------------------------------------------------------------===//714 715void WalkAST::checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD) {716  if (!filter.check_strcpy)717    return;718 719  if (!checkCall_strCommon(CE, FD))720    return;721 722  // Issue a warning.723  PathDiagnosticLocation CELoc =724    PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);725  BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy,726                     "Potential insecure memory buffer bounds restriction in "727                     "call 'strcat'",728                     "Security",729                     "Call to function 'strcat' is insecure as it does not "730                     "provide bounding of the memory buffer. Replace "731                     "unbounded copy functions with analogous functions that "732                     "support length arguments such as 'strlcat'. CWE-119.",733                     CELoc, CE->getCallee()->getSourceRange());734}735 736//===----------------------------------------------------------------------===//737// Check: Any use of 'sprintf', 'vsprintf', 'scanf', 'wscanf', 'fscanf',738//        'fwscanf', 'vscanf', 'vwscanf', 'vfscanf', 'vfwscanf', 'sscanf',739//        'swscanf', 'vsscanf', 'vswscanf', 'swprintf', 'snprintf', 'vswprintf',740//        'vsnprintf', 'memcpy', 'memmove', 'strncpy', 'strncat', 'memset',741//        'fprintf' is deprecated since C11.742//743//        Use of 'sprintf', 'fprintf', 'vsprintf', 'scanf', 'wscanf', 'fscanf',744//        'fwscanf', 'vscanf', 'vwscanf', 'vfscanf', 'vfwscanf', 'sscanf',745//        'swscanf', 'vsscanf', 'vswscanf' without buffer limitations746//        is insecure.747//748// CWE-119: Improper Restriction of Operations within749// the Bounds of a Memory Buffer750//===----------------------------------------------------------------------===//751 752void WalkAST::checkDeprecatedOrUnsafeBufferHandling(const CallExpr *CE,753                                                    const FunctionDecl *FD) {754  if (!filter.check_DeprecatedOrUnsafeBufferHandling)755    return;756 757  if (!BR.getContext().getLangOpts().C11)758    return;759 760  // Issue a warning. ArgIndex == -1: Deprecated but not unsafe (has size761  // restrictions).762  enum { DEPR_ONLY = -1, UNKNOWN_CALL = -2 };763 764  StringRef Name = FD->getIdentifier()->getName();765  Name.consume_front("__builtin_");766 767  int ArgIndex =768      llvm::StringSwitch<int>(Name)769          .Cases({"scanf", "wscanf", "vscanf", "vwscanf"}, 0)770          .Cases({"fscanf", "fwscanf", "vfscanf", "vfwscanf", "sscanf",771                  "swscanf", "vsscanf", "vswscanf"},772                 1)773          .Cases({"sprintf", "vsprintf", "fprintf"}, 1)774          .Cases({"swprintf", "snprintf", "vswprintf", "vsnprintf", "memcpy",775                  "memmove", "memset", "strncpy", "strncat"},776                 DEPR_ONLY)777          .Default(UNKNOWN_CALL);778 779  assert(ArgIndex != UNKNOWN_CALL && "Unsupported function");780  bool BoundsProvided = ArgIndex == DEPR_ONLY;781 782  if (!BoundsProvided) {783    // Currently we only handle (not wide) string literals. It is possible to do784    // better, either by looking at references to const variables, or by doing785    // real flow analysis.786    auto FormatString =787        dyn_cast<StringLiteral>(CE->getArg(ArgIndex)->IgnoreParenImpCasts());788    if (FormatString && !FormatString->getString().contains("%s") &&789        !FormatString->getString().contains("%["))790      BoundsProvided = true;791  }792 793  SmallString<128> Buf1;794  SmallString<512> Buf2;795  llvm::raw_svector_ostream Out1(Buf1);796  llvm::raw_svector_ostream Out2(Buf2);797 798  Out1 << "Potential insecure memory buffer bounds restriction in call '"799       << Name << "'";800  Out2 << "Call to function '" << Name801       << "' is insecure as it does not provide ";802 803  if (!BoundsProvided) {804    Out2 << "bounding of the memory buffer or ";805  }806 807  Out2 << "security checks introduced "808          "in the C11 standard. Replace with analogous functions that "809          "support length arguments or provides boundary checks such as '"810       << Name << "_s' in case of C11";811 812  PathDiagnosticLocation CELoc =813      PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);814  BR.EmitBasicReport(AC->getDecl(),815                     filter.checkName_DeprecatedOrUnsafeBufferHandling,816                     Out1.str(), "Security", Out2.str(), CELoc,817                     CE->getCallee()->getSourceRange());818}819 820//===----------------------------------------------------------------------===//821// Common check for str* functions with no bounds parameters.822//===----------------------------------------------------------------------===//823 824bool WalkAST::checkCall_strCommon(const CallExpr *CE, const FunctionDecl *FD) {825  const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();826  if (!FPT)827    return false;828 829  // Verify the function takes two arguments, three in the _chk version.830  int numArgs = FPT->getNumParams();831  if (numArgs != 2 && numArgs != 3)832    return false;833 834  // Verify the type for both arguments.835  for (int i = 0; i < 2; i++) {836    // Verify that the arguments are pointers.837    const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>();838    if (!PT)839      return false;840 841    // Verify that the argument is a 'char*'.842    if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)843      return false;844  }845 846  return true;847}848 849//===----------------------------------------------------------------------===//850// Check: Linear congruent random number generators should not be used,851// i.e. rand(), random().852//853// E. Bach, "Efficient prediction of Marsaglia-Zaman random number generators,"854// in IEEE Transactions on Information Theory, vol. 44, no. 3, pp. 1253-1257,855// May 1998, https://doi.org/10.1109/18.669305856//857// CWE-338: Use of cryptographically weak prng858//===----------------------------------------------------------------------===//859 860void WalkAST::checkCall_rand(const CallExpr *CE, const FunctionDecl *FD) {861  if (!filter.check_rand || !CheckRand)862    return;863 864  const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>();865  if (!FTP)866    return;867 868  if (FTP->getNumParams() == 1) {869    // Is the argument an 'unsigned short *'?870    // (Actually any integer type is allowed.)871    const PointerType *PT = FTP->getParamType(0)->getAs<PointerType>();872    if (!PT)873      return;874 875    if (! PT->getPointeeType()->isIntegralOrUnscopedEnumerationType())876      return;877  } else if (FTP->getNumParams() != 0)878    return;879 880  // Issue a warning.881  SmallString<256> buf1;882  llvm::raw_svector_ostream os1(buf1);883  os1 << '\'' << *FD << "' is a poor random number generator";884 885  SmallString<256> buf2;886  llvm::raw_svector_ostream os2(buf2);887  os2 << "Function '" << *FD888      << "' is obsolete because it implements a poor random number generator."889      << "  Use 'arc4random' instead";890 891  PathDiagnosticLocation CELoc =892    PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);893  BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand, os1.str(),894                     "Security", os2.str(), CELoc,895                     CE->getCallee()->getSourceRange());896}897 898// See justification for rand().899void WalkAST::checkCall_random(const CallExpr *CE, const FunctionDecl *FD) {900  if (!CheckRand || !filter.check_rand)901    return;902 903  const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>();904  if (!FTP)905    return;906 907  // Verify that the function takes no argument.908  if (FTP->getNumParams() != 0)909    return;910 911  // Issue a warning.912  PathDiagnosticLocation CELoc =913    PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);914  BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand,915                     "'random' is not a secure random number generator",916                     "Security",917                     "The 'random' function produces a sequence of values that "918                     "an adversary may be able to predict.  Use 'arc4random' "919                     "instead", CELoc, CE->getCallee()->getSourceRange());920}921 922//===----------------------------------------------------------------------===//923// Check: 'vfork' should not be used.924// POS33-C: Do not use vfork().925//===----------------------------------------------------------------------===//926 927void WalkAST::checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD) {928  if (!filter.check_vfork)929    return;930 931  // All calls to vfork() are insecure, issue a warning.932  PathDiagnosticLocation CELoc =933    PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);934  BR.EmitBasicReport(AC->getDecl(), filter.checkName_vfork,935                     "Potential insecure implementation-specific behavior in "936                     "call 'vfork'",937                     "Security",938                     "Call to function 'vfork' is insecure as it can lead to "939                     "denial of service situations in the parent process. "940                     "Replace calls to vfork with calls to the safer "941                     "'posix_spawn' function",942                     CELoc, CE->getCallee()->getSourceRange());943}944 945//===----------------------------------------------------------------------===//946// Check: '-decodeValueOfObjCType:at:' should not be used.947// It is deprecated in favor of '-decodeValueOfObjCType:at:size:' due to948// likelihood of buffer overflows.949//===----------------------------------------------------------------------===//950 951void WalkAST::checkMsg_decodeValueOfObjCType(const ObjCMessageExpr *ME) {952  if (!filter.check_decodeValueOfObjCType)953    return;954 955  // Check availability of the secure alternative:956  // iOS 11+, macOS 10.13+, tvOS 11+, and watchOS 4.0+957  // FIXME: We probably shouldn't register the check if it's not available.958  const TargetInfo &TI = AC->getASTContext().getTargetInfo();959  const llvm::Triple &T = TI.getTriple();960  const VersionTuple &VT = TI.getPlatformMinVersion();961  switch (T.getOS()) {962  case llvm::Triple::IOS:963    if (VT < VersionTuple(11, 0))964      return;965    break;966  case llvm::Triple::MacOSX:967    if (VT < VersionTuple(10, 13))968      return;969    break;970  case llvm::Triple::WatchOS:971    if (VT < VersionTuple(4, 0))972      return;973    break;974  case llvm::Triple::TvOS:975    if (VT < VersionTuple(11, 0))976      return;977    break;978  case llvm::Triple::XROS:979    break;980  default:981    return;982  }983 984  PathDiagnosticLocation MELoc =985      PathDiagnosticLocation::createBegin(ME, BR.getSourceManager(), AC);986  BR.EmitBasicReport(987      AC->getDecl(), filter.checkName_decodeValueOfObjCType,988      "Potential buffer overflow in '-decodeValueOfObjCType:at:'", "Security",989      "Deprecated method '-decodeValueOfObjCType:at:' is insecure "990      "as it can lead to potential buffer overflows. Use the safer "991      "'-decodeValueOfObjCType:at:size:' method.",992      MELoc, ME->getSourceRange());993}994 995//===----------------------------------------------------------------------===//996// Check: The caller should always verify that the privileges997// were dropped successfully.998//999// Some library functions, like setuid() and setgid(), should always be used1000// with a check of the return value to verify that the function completed1001// successfully.  If the drop fails, the software will continue to run1002// with the raised privileges, which might provide additional access1003// to unprivileged users.1004//1005// (Note that this check predates __attribute__((warn_unused_result)).1006// Do we still need it now that we have a compiler warning for this?1007// Are these standard functions already annotated this way?)1008//===----------------------------------------------------------------------===//1009 1010void WalkAST::checkUncheckedReturnValue(CallExpr *CE) {1011  if (!filter.check_UncheckedReturn)1012    return;1013 1014  const FunctionDecl *FD = CE->getDirectCallee();1015  if (!FD)1016    return;1017 1018  if (II_setid[0] == nullptr) {1019    static const char * const identifiers[num_setids] = {1020      "setuid", "setgid", "seteuid", "setegid",1021      "setreuid", "setregid"1022    };1023 1024    for (size_t i = 0; i < num_setids; i++)1025      II_setid[i] = &BR.getContext().Idents.get(identifiers[i]);1026  }1027 1028  const IdentifierInfo *id = FD->getIdentifier();1029  size_t identifierid;1030 1031  for (identifierid = 0; identifierid < num_setids; identifierid++)1032    if (id == II_setid[identifierid])1033      break;1034 1035  if (identifierid >= num_setids)1036    return;1037 1038  const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>();1039  if (!FTP)1040    return;1041 1042  // Verify that the function takes one or two arguments (depending on1043  //   the function).1044  if (FTP->getNumParams() != (identifierid < 4 ? 1 : 2))1045    return;1046 1047  // The arguments must be integers.1048  for (unsigned i = 0; i < FTP->getNumParams(); i++)1049    if (!FTP->getParamType(i)->isIntegralOrUnscopedEnumerationType())1050      return;1051 1052  // Issue a warning.1053  SmallString<256> buf1;1054  llvm::raw_svector_ostream os1(buf1);1055  os1 << "Return value is not checked in call to '" << *FD << '\'';1056 1057  SmallString<256> buf2;1058  llvm::raw_svector_ostream os2(buf2);1059  os2 << "The return value from the call to '" << *FD1060      << "' is not checked.  If an error occurs in '" << *FD1061      << "', the following code may execute with unexpected privileges";1062 1063  PathDiagnosticLocation CELoc =1064    PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);1065  BR.EmitBasicReport(AC->getDecl(), filter.checkName_UncheckedReturn, os1.str(),1066                     "Security", os2.str(), CELoc,1067                     CE->getCallee()->getSourceRange());1068}1069 1070//===----------------------------------------------------------------------===//1071// SecuritySyntaxChecker1072//===----------------------------------------------------------------------===//1073 1074namespace {1075class SecuritySyntaxChecker : public Checker<check::ASTCodeBody> {1076public:1077  ChecksFilter filter;1078 1079  void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,1080                        BugReporter &BR) const {1081    WalkAST walker(BR, mgr.getAnalysisDeclContext(D), filter);1082    walker.Visit(D->getBody());1083  }1084};1085}1086 1087void ento::registerSecuritySyntaxChecker(CheckerManager &mgr) {1088  mgr.registerChecker<SecuritySyntaxChecker>();1089}1090 1091bool ento::shouldRegisterSecuritySyntaxChecker(const CheckerManager &mgr) {1092  return true;1093}1094 1095#define REGISTER_CHECKER(name)                                                 \1096  void ento::register##name(CheckerManager &mgr) {                             \1097    SecuritySyntaxChecker *checker = mgr.getChecker<SecuritySyntaxChecker>();  \1098    checker->filter.check_##name = true;                                       \1099    checker->filter.checkName_##name = mgr.getCurrentCheckerName();            \1100  }                                                                            \1101                                                                               \1102  bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }1103 1104REGISTER_CHECKER(bcmp)1105REGISTER_CHECKER(bcopy)1106REGISTER_CHECKER(bzero)1107REGISTER_CHECKER(gets)1108REGISTER_CHECKER(getpw)1109REGISTER_CHECKER(mkstemp)1110REGISTER_CHECKER(mktemp)1111REGISTER_CHECKER(strcpy)1112REGISTER_CHECKER(rand)1113REGISTER_CHECKER(vfork)1114REGISTER_CHECKER(FloatLoopCounter)1115REGISTER_CHECKER(UncheckedReturn)1116REGISTER_CHECKER(DeprecatedOrUnsafeBufferHandling)1117REGISTER_CHECKER(decodeValueOfObjCType)1118