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