StrToNumCheck.cpp 7.3 KB

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