clang-tools 6.0.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/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
clang::ast_matchers
Definition: ClangTidyDiagnosticConsumer.h:27
clang::tidy::cert::@80::ConversionKind
ConversionKind
Definition: StrToNumCheck.cpp:39
clang
Definition: AndroidTidyModule.cpp:28
StrToNumCheck.h