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