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