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/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/Analysis/Analyses/FormatString.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, 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
236