tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

SprintfLiteralChecker.cpp (3209B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #include "SprintfLiteralChecker.h"
      6 #include "CustomMatchers.h"
      7 
      8 void SprintfLiteralChecker::registerMatchers(MatchFinder *AstMatcher) {
      9  AstMatcher->addMatcher(
     10      callExpr(
     11          isSnprintfLikeFunc(),
     12          allOf(hasArgument(
     13                    0, ignoringParenImpCasts(declRefExpr().bind("buffer"))),
     14                anyOf(hasArgument(1, sizeOfExpr(has(ignoringParenImpCasts(
     15                                         declRefExpr().bind("size"))))),
     16                      hasArgument(1, integerLiteral().bind("immediate")),
     17                      hasArgument(1, declRefExpr(to(varDecl(
     18                                         hasType(isConstQualified()),
     19                                         hasInitializer(integerLiteral().bind(
     20                                             "constant")))))))),isFirstParty())
     21          .bind("funcCall"),
     22      this);
     23 }
     24 
     25 void SprintfLiteralChecker::check(const MatchFinder::MatchResult &Result) {
     26  if (!Result.Context->getLangOpts().CPlusPlus) {
     27    // SprintfLiteral is not usable in C, so there is no point in issuing these
     28    // warnings.
     29    return;
     30  }
     31 
     32  const char *Error =
     33      "Use %1 instead of %0 when writing into a character array.";
     34  const char *Note =
     35      "This will prevent passing in the wrong size to %0 accidentally.";
     36 
     37  const CallExpr *D = Result.Nodes.getNodeAs<CallExpr>("funcCall");
     38 
     39  StringRef Name = D->getDirectCallee()->getName();
     40  const char *Replacement;
     41  if (Name == "snprintf") {
     42    Replacement = "SprintfLiteral";
     43  } else {
     44    assert(Name == "vsnprintf");
     45    Replacement = "VsprintfLiteral";
     46  }
     47 
     48  const DeclRefExpr *Buffer = Result.Nodes.getNodeAs<DeclRefExpr>("buffer");
     49  const DeclRefExpr *Size = Result.Nodes.getNodeAs<DeclRefExpr>("size");
     50  if (Size) {
     51    // Match calls like snprintf(x, sizeof(x), ...).
     52    if (Buffer->getFoundDecl() != Size->getFoundDecl()) {
     53      return;
     54    }
     55 
     56    diag(D->getBeginLoc(), Error, DiagnosticIDs::Error) << Name << Replacement;
     57    diag(D->getBeginLoc(), Note, DiagnosticIDs::Note) << Name;
     58    return;
     59  }
     60 
     61  const QualType QType = Buffer->getType();
     62  const ConstantArrayType *Type =
     63      dyn_cast<ConstantArrayType>(QType.getTypePtrOrNull());
     64  if (Type) {
     65    // Match calls like snprintf(x, 100, ...), where x is int[100];
     66    const IntegerLiteral *Literal =
     67        Result.Nodes.getNodeAs<IntegerLiteral>("immediate");
     68    if (!Literal) {
     69      // Match calls like: const int y = 100; snprintf(x, y, ...);
     70      Literal = Result.Nodes.getNodeAs<IntegerLiteral>("constant");
     71    }
     72 
     73    // We're going to assume here that the bitwidth of both of these values fits
     74    // within 64 bits. and zero-extend both values to 64-bits before comparing
     75    // them.
     76    uint64_t Size = Type->getSize().getZExtValue();
     77    uint64_t Lit = Literal->getValue().getZExtValue();
     78    if (Size <= Lit) {
     79      diag(D->getBeginLoc(), Error, DiagnosticIDs::Error)
     80          << Name << Replacement;
     81      diag(D->getBeginLoc(), Note, DiagnosticIDs::Note) << Name;
     82    }
     83  }
     84 }