123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230 |
- //===-- StrToNumCheck.cpp - clang-tidy ------------------------------------===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- #include "StrToNumCheck.h"
- #include "clang/AST/ASTContext.h"
- #include "clang/AST/FormatString.h"
- #include "clang/ASTMatchers/ASTMatchFinder.h"
- #include "llvm/ADT/StringSwitch.h"
- #include <cassert>
- using namespace clang::ast_matchers;
- namespace clang::tidy::cert {
- void StrToNumCheck::registerMatchers(MatchFinder *Finder) {
- // Match any function call to the C standard library string conversion
- // functions that do no error checking.
- Finder->addMatcher(
- callExpr(
- callee(functionDecl(anyOf(
- functionDecl(hasAnyName("::atoi", "::atof", "::atol", "::atoll"))
- .bind("converter"),
- functionDecl(hasAnyName("::scanf", "::sscanf", "::fscanf",
- "::vfscanf", "::vscanf", "::vsscanf"))
- .bind("formatted")))))
- .bind("expr"),
- this);
- }
- namespace {
- enum class ConversionKind {
- None,
- ToInt,
- ToUInt,
- ToLongInt,
- ToLongUInt,
- ToIntMax,
- ToUIntMax,
- ToFloat,
- ToDouble,
- ToLongDouble
- };
- ConversionKind classifyConversionFunc(const FunctionDecl *FD) {
- return llvm::StringSwitch<ConversionKind>(FD->getName())
- .Cases("atoi", "atol", ConversionKind::ToInt)
- .Case("atoll", ConversionKind::ToLongInt)
- .Case("atof", ConversionKind::ToDouble)
- .Default(ConversionKind::None);
- }
- ConversionKind classifyFormatString(StringRef Fmt, const LangOptions &LO,
- const TargetInfo &TI) {
- // Scan the format string for the first problematic format specifier, then
- // report that as the conversion type. This will miss additional conversion
- // specifiers, but that is acceptable behavior.
- class Handler : public analyze_format_string::FormatStringHandler {
- ConversionKind CK;
- bool HandleScanfSpecifier(const analyze_scanf::ScanfSpecifier &FS,
- const char *StartSpecifier,
- unsigned SpecifierLen) override {
- // If we just consume the argument without assignment, we don't care
- // about it having conversion errors.
- if (!FS.consumesDataArgument())
- return true;
- // Get the conversion specifier and use it to determine the conversion
- // kind.
- analyze_scanf::ScanfConversionSpecifier SCS = FS.getConversionSpecifier();
- if (SCS.isIntArg()) {
- switch (FS.getLengthModifier().getKind()) {
- case analyze_scanf::LengthModifier::AsLongLong:
- CK = ConversionKind::ToLongInt;
- break;
- case analyze_scanf::LengthModifier::AsIntMax:
- CK = ConversionKind::ToIntMax;
- break;
- default:
- CK = ConversionKind::ToInt;
- break;
- }
- } else if (SCS.isUIntArg()) {
- switch (FS.getLengthModifier().getKind()) {
- case analyze_scanf::LengthModifier::AsLongLong:
- CK = ConversionKind::ToLongUInt;
- break;
- case analyze_scanf::LengthModifier::AsIntMax:
- CK = ConversionKind::ToUIntMax;
- break;
- default:
- CK = ConversionKind::ToUInt;
- break;
- }
- } else if (SCS.isDoubleArg()) {
- switch (FS.getLengthModifier().getKind()) {
- case analyze_scanf::LengthModifier::AsLongDouble:
- CK = ConversionKind::ToLongDouble;
- break;
- case analyze_scanf::LengthModifier::AsLong:
- CK = ConversionKind::ToDouble;
- break;
- default:
- CK = ConversionKind::ToFloat;
- break;
- }
- }
- // Continue if we have yet to find a conversion kind that we care about.
- return CK == ConversionKind::None;
- }
- public:
- Handler() : CK(ConversionKind::None) {}
- ConversionKind get() const { return CK; }
- };
- Handler H;
- analyze_format_string::ParseScanfString(H, Fmt.begin(), Fmt.end(), LO, TI);
- return H.get();
- }
- StringRef classifyConversionType(ConversionKind K) {
- switch (K) {
- case ConversionKind::None:
- llvm_unreachable("Unexpected conversion kind");
- case ConversionKind::ToInt:
- case ConversionKind::ToLongInt:
- case ConversionKind::ToIntMax:
- return "an integer value";
- case ConversionKind::ToUInt:
- case ConversionKind::ToLongUInt:
- case ConversionKind::ToUIntMax:
- return "an unsigned integer value";
- case ConversionKind::ToFloat:
- case ConversionKind::ToDouble:
- case ConversionKind::ToLongDouble:
- return "a floating-point value";
- }
- llvm_unreachable("Unknown conversion kind");
- }
- StringRef classifyReplacement(ConversionKind K) {
- switch (K) {
- case ConversionKind::None:
- llvm_unreachable("Unexpected conversion kind");
- case ConversionKind::ToInt:
- return "strtol";
- case ConversionKind::ToUInt:
- return "strtoul";
- case ConversionKind::ToIntMax:
- return "strtoimax";
- case ConversionKind::ToLongInt:
- return "strtoll";
- case ConversionKind::ToLongUInt:
- return "strtoull";
- case ConversionKind::ToUIntMax:
- return "strtoumax";
- case ConversionKind::ToFloat:
- return "strtof";
- case ConversionKind::ToDouble:
- return "strtod";
- case ConversionKind::ToLongDouble:
- return "strtold";
- }
- llvm_unreachable("Unknown conversion kind");
- }
- } // unnamed namespace
- void StrToNumCheck::check(const MatchFinder::MatchResult &Result) {
- const auto *Call = Result.Nodes.getNodeAs<CallExpr>("expr");
- const FunctionDecl *FuncDecl = nullptr;
- ConversionKind Conversion;
- if (const auto *ConverterFunc =
- Result.Nodes.getNodeAs<FunctionDecl>("converter")) {
- // Converter functions are always incorrect to use.
- FuncDecl = ConverterFunc;
- Conversion = classifyConversionFunc(ConverterFunc);
- } else if (const auto *FFD =
- Result.Nodes.getNodeAs<FunctionDecl>("formatted")) {
- StringRef FmtStr;
- // The format string comes from the call expression and depends on which
- // flavor of scanf is called.
- // Index 0: scanf, vscanf, Index 1: fscanf, sscanf, vfscanf, vsscanf.
- unsigned Idx =
- (FFD->getName() == "scanf" || FFD->getName() == "vscanf") ? 0 : 1;
- // Given the index, see if the call expression argument at that index is
- // a string literal.
- if (Call->getNumArgs() < Idx)
- return;
- if (const Expr *Arg = Call->getArg(Idx)->IgnoreParenImpCasts()) {
- if (const auto *SL = dyn_cast<StringLiteral>(Arg)) {
- FmtStr = SL->getString();
- }
- }
- // If we could not get the format string, bail out.
- if (FmtStr.empty())
- return;
- // Formatted input functions need further checking of the format string to
- // determine whether a problematic conversion may be happening.
- Conversion = classifyFormatString(FmtStr, getLangOpts(),
- Result.Context->getTargetInfo());
- if (Conversion != ConversionKind::None)
- FuncDecl = FFD;
- }
- if (!FuncDecl)
- return;
- diag(Call->getExprLoc(),
- "%0 used to convert a string to %1, but function will not report "
- "conversion errors; consider using '%2' instead")
- << FuncDecl << classifyConversionType(Conversion)
- << classifyReplacement(Conversion);
- }
- } // namespace clang::tidy::cert
|