brintos

brintos / llvm-project-archived public Read only

0
0
Text · 7.5 KiB · b82c9d3 Raw
235 lines · cpp
1//===----------------------------------------------------------------------===//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#include "UncheckedStringToNumberConversionCheck.h"10#include "clang/AST/ASTContext.h"11#include "clang/AST/FormatString.h"12#include "clang/ASTMatchers/ASTMatchFinder.h"13#include "llvm/ADT/StringSwitch.h"14#include <cassert>15 16using namespace clang::ast_matchers;17 18namespace clang::tidy::bugprone {19 20void UncheckedStringToNumberConversionCheck::registerMatchers(21    MatchFinder *Finder) {22  // Match any function call to the C standard library string conversion23  // functions that do no error checking.24  Finder->addMatcher(25      callExpr(26          callee(functionDecl(anyOf(27              functionDecl(hasAnyName("::atoi", "::atof", "::atol", "::atoll"))28                  .bind("converter"),29              functionDecl(hasAnyName("::scanf", "::sscanf", "::fscanf",30                                      "::vfscanf", "::vscanf", "::vsscanf"))31                  .bind("formatted")))))32          .bind("expr"),33      this);34}35 36namespace {37enum class ConversionKind {38  None,39  ToInt,40  ToUInt,41  ToLongInt,42  ToLongUInt,43  ToIntMax,44  ToUIntMax,45  ToFloat,46  ToDouble,47  ToLongDouble48};49 50} // namespace51 52static ConversionKind classifyConversionFunc(const FunctionDecl *FD) {53  return llvm::StringSwitch<ConversionKind>(FD->getName())54      .Cases({"atoi", "atol"}, ConversionKind::ToInt)55      .Case("atoll", ConversionKind::ToLongInt)56      .Case("atof", ConversionKind::ToDouble)57      .Default(ConversionKind::None);58}59 60static ConversionKind classifyFormatString(StringRef Fmt, const LangOptions &LO,61                                           const TargetInfo &TI) {62  // Scan the format string for the first problematic format specifier, then63  // report that as the conversion type. This will miss additional conversion64  // specifiers, but that is acceptable behavior.65 66  class Handler : public analyze_format_string::FormatStringHandler {67    ConversionKind CK = ConversionKind::None;68 69    bool HandleScanfSpecifier(const analyze_scanf::ScanfSpecifier &FS,70                              const char *StartSpecifier,71                              unsigned SpecifierLen) override {72      // If we just consume the argument without assignment, we don't care73      // about it having conversion errors.74      if (!FS.consumesDataArgument())75        return true;76 77      // Get the conversion specifier and use it to determine the conversion78      // kind.79      const analyze_scanf::ScanfConversionSpecifier SCS =80          FS.getConversionSpecifier();81      if (SCS.isIntArg()) {82        switch (FS.getLengthModifier().getKind()) {83        case analyze_scanf::LengthModifier::AsLongLong:84          CK = ConversionKind::ToLongInt;85          break;86        case analyze_scanf::LengthModifier::AsIntMax:87          CK = ConversionKind::ToIntMax;88          break;89        default:90          CK = ConversionKind::ToInt;91          break;92        }93      } else if (SCS.isUIntArg()) {94        switch (FS.getLengthModifier().getKind()) {95        case analyze_scanf::LengthModifier::AsLongLong:96          CK = ConversionKind::ToLongUInt;97          break;98        case analyze_scanf::LengthModifier::AsIntMax:99          CK = ConversionKind::ToUIntMax;100          break;101        default:102          CK = ConversionKind::ToUInt;103          break;104        }105      } else if (SCS.isDoubleArg()) {106        switch (FS.getLengthModifier().getKind()) {107        case analyze_scanf::LengthModifier::AsLongDouble:108          CK = ConversionKind::ToLongDouble;109          break;110        case analyze_scanf::LengthModifier::AsLong:111          CK = ConversionKind::ToDouble;112          break;113        default:114          CK = ConversionKind::ToFloat;115          break;116        }117      }118 119      // Continue if we have yet to find a conversion kind that we care about.120      return CK == ConversionKind::None;121    }122 123  public:124    Handler() = default;125 126    ConversionKind get() const { return CK; }127  };128 129  Handler H;130  analyze_format_string::ParseScanfString(H, Fmt.begin(), Fmt.end(), LO, TI);131 132  return H.get();133}134 135static StringRef classifyConversionType(ConversionKind K) {136  switch (K) {137  case ConversionKind::None:138    llvm_unreachable("Unexpected conversion kind");139  case ConversionKind::ToInt:140  case ConversionKind::ToLongInt:141  case ConversionKind::ToIntMax:142    return "an integer value";143  case ConversionKind::ToUInt:144  case ConversionKind::ToLongUInt:145  case ConversionKind::ToUIntMax:146    return "an unsigned integer value";147  case ConversionKind::ToFloat:148  case ConversionKind::ToDouble:149  case ConversionKind::ToLongDouble:150    return "a floating-point value";151  }152  llvm_unreachable("Unknown conversion kind");153}154 155static StringRef classifyReplacement(ConversionKind K) {156  switch (K) {157  case ConversionKind::None:158    llvm_unreachable("Unexpected conversion kind");159  case ConversionKind::ToInt:160    return "strtol";161  case ConversionKind::ToUInt:162    return "strtoul";163  case ConversionKind::ToIntMax:164    return "strtoimax";165  case ConversionKind::ToLongInt:166    return "strtoll";167  case ConversionKind::ToLongUInt:168    return "strtoull";169  case ConversionKind::ToUIntMax:170    return "strtoumax";171  case ConversionKind::ToFloat:172    return "strtof";173  case ConversionKind::ToDouble:174    return "strtod";175  case ConversionKind::ToLongDouble:176    return "strtold";177  }178  llvm_unreachable("Unknown conversion kind");179}180 181void UncheckedStringToNumberConversionCheck::check(182    const MatchFinder::MatchResult &Result) {183  const auto *Call = Result.Nodes.getNodeAs<CallExpr>("expr");184  const FunctionDecl *FuncDecl = nullptr;185  ConversionKind Conversion = ConversionKind::None;186 187  if (const auto *ConverterFunc =188          Result.Nodes.getNodeAs<FunctionDecl>("converter")) {189    // Converter functions are always incorrect to use.190    FuncDecl = ConverterFunc;191    Conversion = classifyConversionFunc(ConverterFunc);192  } else if (const auto *FFD =193                 Result.Nodes.getNodeAs<FunctionDecl>("formatted")) {194    StringRef FmtStr;195    // The format string comes from the call expression and depends on which196    // flavor of scanf is called.197    // Index 0: scanf, vscanf, Index 1: fscanf, sscanf, vfscanf, vsscanf.198    const unsigned Idx =199        (FFD->getName() == "scanf" || FFD->getName() == "vscanf") ? 0 : 1;200 201    // Given the index, see if the call expression argument at that index is202    // a string literal.203    if (Call->getNumArgs() < Idx)204      return;205 206    if (const Expr *Arg = Call->getArg(Idx)->IgnoreParenImpCasts()) {207      if (const auto *SL = dyn_cast<StringLiteral>(Arg)) {208        FmtStr = SL->getString();209      }210    }211 212    // If we could not get the format string, bail out.213    if (FmtStr.empty())214      return;215 216    // Formatted input functions need further checking of the format string to217    // determine whether a problematic conversion may be happening.218    Conversion = classifyFormatString(FmtStr, getLangOpts(),219                                      Result.Context->getTargetInfo());220    if (Conversion != ConversionKind::None)221      FuncDecl = FFD;222  }223 224  if (!FuncDecl)225    return;226 227  diag(Call->getExprLoc(),228       "%0 used to convert a string to %1, but function will not report "229       "conversion errors; consider using '%2' instead")230      << FuncDecl << classifyConversionType(Conversion)231      << classifyReplacement(Conversion);232}233 234} // namespace clang::tidy::bugprone235