1 //===--- NotNullTerminatedResultCheck.cpp - clang-tidy ----------*- C++ -*-===// 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 "NotNullTerminatedResultCheck.h" 10 #include "clang/AST/ASTContext.h" 11 #include "clang/ASTMatchers/ASTMatchFinder.h" 12 #include "clang/Frontend/CompilerInstance.h" 13 #include "clang/Lex/Lexer.h" 14 #include "clang/Lex/PPCallbacks.h" 15 16 using namespace clang::ast_matchers; 17 18 namespace clang { 19 namespace tidy { 20 namespace bugprone { 21 22 constexpr llvm::StringLiteral FunctionExprName = "FunctionExpr"; 23 constexpr llvm::StringLiteral CastExprName = "CastExpr"; 24 constexpr llvm::StringLiteral UnknownDestName = "UnknownDest"; 25 constexpr llvm::StringLiteral DestArrayTyName = "DestArrayTy"; 26 constexpr llvm::StringLiteral DestVarDeclName = "DestVarDecl"; 27 constexpr llvm::StringLiteral DestMallocExprName = "DestMalloc"; 28 constexpr llvm::StringLiteral DestExprName = "DestExpr"; 29 constexpr llvm::StringLiteral SrcVarDeclName = "SrcVarDecl"; 30 constexpr llvm::StringLiteral SrcExprName = "SrcExpr"; 31 constexpr llvm::StringLiteral LengthExprName = "LengthExpr"; 32 constexpr llvm::StringLiteral WrongLengthExprName = "WrongLength"; 33 constexpr llvm::StringLiteral UnknownLengthName = "UnknownLength"; 34 35 enum class LengthHandleKind { Increase, Decrease }; 36 37 namespace { 38 static Preprocessor *PP; 39 } // namespace 40 41 // Returns the expression of destination's capacity which is part of a 42 // 'VariableArrayType', 'ConstantArrayTypeLoc' or an argument of a 'malloc()' 43 // family function call. 44 static const Expr *getDestCapacityExpr(const MatchFinder::MatchResult &Result) { 45 if (const auto *DestMalloc = Result.Nodes.getNodeAs<Expr>(DestMallocExprName)) 46 return DestMalloc; 47 48 if (const auto *DestVAT = 49 Result.Nodes.getNodeAs<VariableArrayType>(DestArrayTyName)) 50 return DestVAT->getSizeExpr(); 51 52 if (const auto *DestVD = Result.Nodes.getNodeAs<VarDecl>(DestVarDeclName)) 53 if (const TypeLoc DestTL = DestVD->getTypeSourceInfo()->getTypeLoc()) 54 if (const auto DestCTL = DestTL.getAs<ConstantArrayTypeLoc>()) 55 return DestCTL.getSizeExpr(); 56 57 return nullptr; 58 } 59 60 // Returns the length of \p E as an 'IntegerLiteral' or a 'StringLiteral' 61 // without the null-terminator. 62 static int getLength(const Expr *E, const MatchFinder::MatchResult &Result) { 63 if (!E) 64 return 0; 65 66 Expr::EvalResult Length; 67 E = E->IgnoreImpCasts(); 68 69 if (const auto *LengthDRE = dyn_cast<DeclRefExpr>(E)) 70 if (const auto *LengthVD = dyn_cast<VarDecl>(LengthDRE->getDecl())) 71 if (!isa<ParmVarDecl>(LengthVD)) 72 if (const Expr *LengthInit = LengthVD->getInit()) 73 if (LengthInit->EvaluateAsInt(Length, *Result.Context)) 74 return Length.Val.getInt().getSExtValue(); 75 76 if (const auto *LengthIL = dyn_cast<IntegerLiteral>(E)) 77 return LengthIL->getValue().getSExtValue(); 78 79 if (const auto *StrDRE = dyn_cast<DeclRefExpr>(E)) 80 if (const auto *StrVD = dyn_cast<VarDecl>(StrDRE->getDecl())) 81 if (const Expr *StrInit = StrVD->getInit()) 82 if (const auto *StrSL = 83 dyn_cast<StringLiteral>(StrInit->IgnoreImpCasts())) 84 return StrSL->getLength(); 85 86 if (const auto *SrcSL = dyn_cast<StringLiteral>(E)) 87 return SrcSL->getLength(); 88 89 return 0; 90 } 91 92 // Returns the capacity of the destination array. 93 // For example in 'char dest[13]; memcpy(dest, ...)' it returns 13. 94 static int getDestCapacity(const MatchFinder::MatchResult &Result) { 95 if (const auto *DestCapacityExpr = getDestCapacityExpr(Result)) 96 return getLength(DestCapacityExpr, Result); 97 98 return 0; 99 } 100 101 // Returns the 'strlen()' if it is the given length. 102 static const CallExpr *getStrlenExpr(const MatchFinder::MatchResult &Result) { 103 if (const auto *StrlenExpr = 104 Result.Nodes.getNodeAs<CallExpr>(WrongLengthExprName)) 105 if (const Decl *D = StrlenExpr->getCalleeDecl()) 106 if (const FunctionDecl *FD = D->getAsFunction()) 107 if (const IdentifierInfo *II = FD->getIdentifier()) 108 if (II->isStr("strlen") || II->isStr("wcslen")) 109 return StrlenExpr; 110 111 return nullptr; 112 } 113 114 // Returns the length which is given in the memory/string handler function. 115 // For example in 'memcpy(dest, "foobar", 3)' it returns 3. 116 static int getGivenLength(const MatchFinder::MatchResult &Result) { 117 if (Result.Nodes.getNodeAs<Expr>(UnknownLengthName)) 118 return 0; 119 120 if (int Length = 121 getLength(Result.Nodes.getNodeAs<Expr>(WrongLengthExprName), Result)) 122 return Length; 123 124 if (int Length = 125 getLength(Result.Nodes.getNodeAs<Expr>(LengthExprName), Result)) 126 return Length; 127 128 // Special case, for example 'strlen("foo")'. 129 if (const CallExpr *StrlenCE = getStrlenExpr(Result)) 130 if (const Expr *Arg = StrlenCE->getArg(0)->IgnoreImpCasts()) 131 if (int ArgLength = getLength(Arg, Result)) 132 return ArgLength; 133 134 return 0; 135 } 136 137 // Returns a string representation of \p E. 138 static StringRef exprToStr(const Expr *E, 139 const MatchFinder::MatchResult &Result) { 140 if (!E) 141 return ""; 142 143 return Lexer::getSourceText( 144 CharSourceRange::getTokenRange(E->getSourceRange()), 145 *Result.SourceManager, Result.Context->getLangOpts(), 0); 146 } 147 148 // Returns the proper token based end location of \p E. 149 static SourceLocation exprLocEnd(const Expr *E, 150 const MatchFinder::MatchResult &Result) { 151 return Lexer::getLocForEndOfToken(E->getEndLoc(), 0, *Result.SourceManager, 152 Result.Context->getLangOpts()); 153 } 154 155 //===----------------------------------------------------------------------===// 156 // Rewrite decision helper functions. 157 //===----------------------------------------------------------------------===// 158 159 // Increment by integer '1' can result in overflow if it is the maximal value. 160 // After that it would be extended to 'size_t' and its value would be wrong, 161 // therefore we have to inject '+ 1UL' instead. 162 static bool isInjectUL(const MatchFinder::MatchResult &Result) { 163 return getGivenLength(Result) == std::numeric_limits<int>::max(); 164 } 165 166 // If the capacity of the destination array is unknown it is denoted as unknown. 167 static bool isKnownDest(const MatchFinder::MatchResult &Result) { 168 return !Result.Nodes.getNodeAs<Expr>(UnknownDestName); 169 } 170 171 // True if the capacity of the destination array is based on the given length, 172 // therefore we assume that it cannot overflow (e.g. 'malloc(given_length + 1)' 173 static bool isDestBasedOnGivenLength(const MatchFinder::MatchResult &Result) { 174 StringRef DestCapacityExprStr = 175 exprToStr(getDestCapacityExpr(Result), Result).trim(); 176 StringRef LengthExprStr = 177 exprToStr(Result.Nodes.getNodeAs<Expr>(LengthExprName), Result).trim(); 178 179 return DestCapacityExprStr != "" && LengthExprStr != "" && 180 DestCapacityExprStr.contains(LengthExprStr); 181 } 182 183 // Writing and reading from the same memory cannot remove the null-terminator. 184 static bool isDestAndSrcEquals(const MatchFinder::MatchResult &Result) { 185 if (const auto *DestDRE = Result.Nodes.getNodeAs<DeclRefExpr>(DestExprName)) 186 if (const auto *SrcDRE = Result.Nodes.getNodeAs<DeclRefExpr>(SrcExprName)) 187 return DestDRE->getDecl()->getCanonicalDecl() == 188 SrcDRE->getDecl()->getCanonicalDecl(); 189 190 return false; 191 } 192 193 // For example 'std::string str = "foo"; memcpy(dst, str.data(), str.length())'. 194 static bool isStringDataAndLength(const MatchFinder::MatchResult &Result) { 195 const auto *DestExpr = 196 Result.Nodes.getNodeAs<CXXMemberCallExpr>(DestExprName); 197 const auto *SrcExpr = Result.Nodes.getNodeAs<CXXMemberCallExpr>(SrcExprName); 198 const auto *LengthExpr = 199 Result.Nodes.getNodeAs<CXXMemberCallExpr>(WrongLengthExprName); 200 201 StringRef DestStr = "", SrcStr = "", LengthStr = ""; 202 if (DestExpr) 203 if (const CXXMethodDecl *DestMD = DestExpr->getMethodDecl()) 204 DestStr = DestMD->getName(); 205 206 if (SrcExpr) 207 if (const CXXMethodDecl *SrcMD = SrcExpr->getMethodDecl()) 208 SrcStr = SrcMD->getName(); 209 210 if (LengthExpr) 211 if (const CXXMethodDecl *LengthMD = LengthExpr->getMethodDecl()) 212 LengthStr = LengthMD->getName(); 213 214 return (LengthStr == "length" || LengthStr == "size") && 215 (SrcStr == "data" || DestStr == "data"); 216 } 217 218 static bool 219 isGivenLengthEqualToSrcLength(const MatchFinder::MatchResult &Result) { 220 if (Result.Nodes.getNodeAs<Expr>(UnknownLengthName)) 221 return false; 222 223 if (isStringDataAndLength(Result)) 224 return true; 225 226 int GivenLength = getGivenLength(Result); 227 int SrcLength = getLength(Result.Nodes.getNodeAs<Expr>(SrcExprName), Result); 228 229 if (GivenLength != 0 && SrcLength != 0 && GivenLength == SrcLength) 230 return true; 231 232 if (const auto *LengthExpr = Result.Nodes.getNodeAs<Expr>(LengthExprName)) 233 if (dyn_cast<BinaryOperator>(LengthExpr->IgnoreParenImpCasts())) 234 return false; 235 236 // Check the strlen()'s argument's 'VarDecl' is equal to the source 'VarDecl'. 237 if (const CallExpr *StrlenCE = getStrlenExpr(Result)) 238 if (const auto *ArgDRE = 239 dyn_cast<DeclRefExpr>(StrlenCE->getArg(0)->IgnoreImpCasts())) 240 if (const auto *SrcVD = Result.Nodes.getNodeAs<VarDecl>(SrcVarDeclName)) 241 return dyn_cast<VarDecl>(ArgDRE->getDecl()) == SrcVD; 242 243 return false; 244 } 245 246 static bool isCorrectGivenLength(const MatchFinder::MatchResult &Result) { 247 if (Result.Nodes.getNodeAs<Expr>(UnknownLengthName)) 248 return false; 249 250 return !isGivenLengthEqualToSrcLength(Result); 251 } 252 253 // If we rewrite the function call we need to create extra space to hold the 254 // null terminator. The new necessary capacity overflows without that '+ 1' 255 // size and we need to correct the given capacity. 256 static bool isDestCapacityOverflows(const MatchFinder::MatchResult &Result) { 257 if (!isKnownDest(Result)) 258 return true; 259 260 const Expr *DestCapacityExpr = getDestCapacityExpr(Result); 261 int DestCapacity = getLength(DestCapacityExpr, Result); 262 int GivenLength = getGivenLength(Result); 263 264 if (GivenLength != 0 && DestCapacity != 0) 265 return isGivenLengthEqualToSrcLength(Result) && DestCapacity == GivenLength; 266 267 // Assume that the destination array's capacity cannot overflow if the 268 // expression of the memory allocation contains '+ 1'. 269 StringRef DestCapacityExprStr = exprToStr(DestCapacityExpr, Result); 270 if (DestCapacityExprStr.contains("+1") || DestCapacityExprStr.contains("+ 1")) 271 return false; 272 273 return true; 274 } 275 276 static bool 277 isFixedGivenLengthAndUnknownSrc(const MatchFinder::MatchResult &Result) { 278 if (Result.Nodes.getNodeAs<IntegerLiteral>(WrongLengthExprName)) 279 return !getLength(Result.Nodes.getNodeAs<Expr>(SrcExprName), Result); 280 281 return false; 282 } 283 284 //===----------------------------------------------------------------------===// 285 // Code injection functions. 286 //===----------------------------------------------------------------------===// 287 288 // Increase or decrease \p LengthExpr by one. 289 static void lengthExprHandle(const Expr *LengthExpr, 290 LengthHandleKind LengthHandle, 291 const MatchFinder::MatchResult &Result, 292 DiagnosticBuilder &Diag) { 293 LengthExpr = LengthExpr->IgnoreParenImpCasts(); 294 295 // See whether we work with a macro. 296 bool IsMacroDefinition = false; 297 StringRef LengthExprStr = exprToStr(LengthExpr, Result); 298 Preprocessor::macro_iterator It = PP->macro_begin(); 299 while (It != PP->macro_end() && !IsMacroDefinition) { 300 if (It->first->getName() == LengthExprStr) 301 IsMacroDefinition = true; 302 303 ++It; 304 } 305 306 // Try to obtain an 'IntegerLiteral' and adjust it. 307 if (!IsMacroDefinition) { 308 if (const auto *LengthIL = dyn_cast<IntegerLiteral>(LengthExpr)) { 309 size_t NewLength = LengthIL->getValue().getSExtValue() + 310 (LengthHandle == LengthHandleKind::Increase 311 ? (isInjectUL(Result) ? 1UL : 1) 312 : -1); 313 314 const auto NewLengthFix = FixItHint::CreateReplacement( 315 LengthIL->getSourceRange(), 316 (Twine(NewLength) + (isInjectUL(Result) ? "UL" : "")).str()); 317 Diag << NewLengthFix; 318 return; 319 } 320 } 321 322 // Try to obtain and remove the '+ 1' string as a decrement fix. 323 const auto *BO = dyn_cast<BinaryOperator>(LengthExpr); 324 if (BO && BO->getOpcode() == BO_Add && 325 LengthHandle == LengthHandleKind::Decrease) { 326 const Expr *LhsExpr = BO->getLHS()->IgnoreImpCasts(); 327 const Expr *RhsExpr = BO->getRHS()->IgnoreImpCasts(); 328 329 if (const auto *LhsIL = dyn_cast<IntegerLiteral>(LhsExpr)) { 330 if (LhsIL->getValue().getSExtValue() == 1) { 331 Diag << FixItHint::CreateRemoval( 332 {LhsIL->getBeginLoc(), 333 RhsExpr->getBeginLoc().getLocWithOffset(-1)}); 334 return; 335 } 336 } 337 338 if (const auto *RhsIL = dyn_cast<IntegerLiteral>(RhsExpr)) { 339 if (RhsIL->getValue().getSExtValue() == 1) { 340 Diag << FixItHint::CreateRemoval( 341 {LhsExpr->getEndLoc().getLocWithOffset(1), RhsIL->getEndLoc()}); 342 return; 343 } 344 } 345 } 346 347 // Try to inject the '+ 1'/'- 1' string. 348 bool NeedInnerParen = BO && BO->getOpcode() != BO_Add; 349 350 if (NeedInnerParen) 351 Diag << FixItHint::CreateInsertion(LengthExpr->getBeginLoc(), "("); 352 353 SmallString<8> Injection; 354 if (NeedInnerParen) 355 Injection += ')'; 356 Injection += LengthHandle == LengthHandleKind::Increase ? " + 1" : " - 1"; 357 if (isInjectUL(Result)) 358 Injection += "UL"; 359 360 Diag << FixItHint::CreateInsertion(exprLocEnd(LengthExpr, Result), Injection); 361 } 362 363 static void lengthArgHandle(LengthHandleKind LengthHandle, 364 const MatchFinder::MatchResult &Result, 365 DiagnosticBuilder &Diag) { 366 const auto *LengthExpr = Result.Nodes.getNodeAs<Expr>(LengthExprName); 367 lengthExprHandle(LengthExpr, LengthHandle, Result, Diag); 368 } 369 370 static void lengthArgPosHandle(unsigned ArgPos, LengthHandleKind LengthHandle, 371 const MatchFinder::MatchResult &Result, 372 DiagnosticBuilder &Diag) { 373 const auto *FunctionExpr = Result.Nodes.getNodeAs<CallExpr>(FunctionExprName); 374 lengthExprHandle(FunctionExpr->getArg(ArgPos), LengthHandle, Result, Diag); 375 } 376 377 // The string handler functions are only operates with plain 'char'/'wchar_t' 378 // without 'unsigned/signed', therefore we need to cast it. 379 static bool isDestExprFix(const MatchFinder::MatchResult &Result, 380 DiagnosticBuilder &Diag) { 381 const auto *Dest = Result.Nodes.getNodeAs<Expr>(DestExprName); 382 if (!Dest) 383 return false; 384 385 std::string TempTyStr = Dest->getType().getAsString(); 386 StringRef TyStr = TempTyStr; 387 if (TyStr.startswith("char") || TyStr.startswith("wchar_t")) 388 return false; 389 390 Diag << FixItHint::CreateInsertion(Dest->getBeginLoc(), "(char *)"); 391 return true; 392 } 393 394 // If the destination array is the same length as the given length we have to 395 // increase the capacity by one to create space for the the null terminator. 396 static bool isDestCapacityFix(const MatchFinder::MatchResult &Result, 397 DiagnosticBuilder &Diag) { 398 bool IsOverflows = isDestCapacityOverflows(Result); 399 if (IsOverflows) 400 if (const Expr *CapacityExpr = getDestCapacityExpr(Result)) 401 lengthExprHandle(CapacityExpr, LengthHandleKind::Increase, Result, Diag); 402 403 return IsOverflows; 404 } 405 406 static void removeArg(int ArgPos, const MatchFinder::MatchResult &Result, 407 DiagnosticBuilder &Diag) { 408 // This is the following structure: (src, '\0', strlen(src)) 409 // ArgToRemove: ~~~~~~~~~~~ 410 // LHSArg: ~~~~ 411 // RemoveArgFix: ~~~~~~~~~~~~~ 412 const auto *FunctionExpr = Result.Nodes.getNodeAs<CallExpr>(FunctionExprName); 413 const Expr *ArgToRemove = FunctionExpr->getArg(ArgPos); 414 const Expr *LHSArg = FunctionExpr->getArg(ArgPos - 1); 415 const auto RemoveArgFix = FixItHint::CreateRemoval( 416 SourceRange(exprLocEnd(LHSArg, Result), 417 exprLocEnd(ArgToRemove, Result).getLocWithOffset(-1))); 418 Diag << RemoveArgFix; 419 } 420 421 static void renameFunc(StringRef NewFuncName, 422 const MatchFinder::MatchResult &Result, 423 DiagnosticBuilder &Diag) { 424 const auto *FunctionExpr = Result.Nodes.getNodeAs<CallExpr>(FunctionExprName); 425 int FuncNameLength = 426 FunctionExpr->getDirectCallee()->getIdentifier()->getLength(); 427 SourceRange FuncNameRange( 428 FunctionExpr->getBeginLoc(), 429 FunctionExpr->getBeginLoc().getLocWithOffset(FuncNameLength - 1)); 430 431 const auto FuncNameFix = 432 FixItHint::CreateReplacement(FuncNameRange, NewFuncName); 433 Diag << FuncNameFix; 434 } 435 436 static void renameMemcpy(StringRef Name, bool IsCopy, bool IsSafe, 437 const MatchFinder::MatchResult &Result, 438 DiagnosticBuilder &Diag) { 439 SmallString<10> NewFuncName; 440 NewFuncName = (Name[0] != 'w') ? "str" : "wcs"; 441 NewFuncName += IsCopy ? "cpy" : "ncpy"; 442 NewFuncName += IsSafe ? "_s" : ""; 443 renameFunc(NewFuncName, Result, Diag); 444 } 445 446 static void insertDestCapacityArg(bool IsOverflows, StringRef Name, 447 const MatchFinder::MatchResult &Result, 448 DiagnosticBuilder &Diag) { 449 const auto *FunctionExpr = Result.Nodes.getNodeAs<CallExpr>(FunctionExprName); 450 SmallString<64> NewSecondArg; 451 452 if (int DestLength = getDestCapacity(Result)) { 453 NewSecondArg = Twine(IsOverflows ? DestLength + 1 : DestLength).str(); 454 } else { 455 NewSecondArg = 456 (Twine(exprToStr(getDestCapacityExpr(Result), Result)) + 457 (IsOverflows ? (!isInjectUL(Result) ? " + 1" : " + 1UL") : "")) 458 .str(); 459 } 460 461 NewSecondArg += ", "; 462 const auto InsertNewArgFix = FixItHint::CreateInsertion( 463 FunctionExpr->getArg(1)->getBeginLoc(), NewSecondArg); 464 Diag << InsertNewArgFix; 465 } 466 467 static void insertNullTerminatorExpr(StringRef Name, 468 const MatchFinder::MatchResult &Result, 469 DiagnosticBuilder &Diag) { 470 const auto *FunctionExpr = Result.Nodes.getNodeAs<CallExpr>(FunctionExprName); 471 int FuncLocStartColumn = Result.SourceManager->getPresumedColumnNumber( 472 FunctionExpr->getBeginLoc()); 473 SourceRange SpaceRange( 474 FunctionExpr->getBeginLoc().getLocWithOffset(-FuncLocStartColumn + 1), 475 FunctionExpr->getBeginLoc()); 476 StringRef SpaceBeforeStmtStr = Lexer::getSourceText( 477 CharSourceRange::getCharRange(SpaceRange), *Result.SourceManager, 478 Result.Context->getLangOpts(), 0); 479 480 SmallString<128> NewAddNullTermExprStr; 481 NewAddNullTermExprStr = 482 (Twine('\n') + SpaceBeforeStmtStr + 483 exprToStr(Result.Nodes.getNodeAs<Expr>(DestExprName), Result) + "[" + 484 exprToStr(Result.Nodes.getNodeAs<Expr>(LengthExprName), Result) + 485 "] = " + ((Name[0] != 'w') ? "\'\\0\';" : "L\'\\0\';")) 486 .str(); 487 488 const auto AddNullTerminatorExprFix = FixItHint::CreateInsertion( 489 exprLocEnd(FunctionExpr, Result).getLocWithOffset(1), 490 NewAddNullTermExprStr); 491 Diag << AddNullTerminatorExprFix; 492 } 493 494 //===----------------------------------------------------------------------===// 495 // Checker logic with the matchers. 496 //===----------------------------------------------------------------------===// 497 498 NotNullTerminatedResultCheck::NotNullTerminatedResultCheck( 499 StringRef Name, ClangTidyContext *Context) 500 : ClangTidyCheck(Name, Context), 501 WantToUseSafeFunctions(Options.get("WantToUseSafeFunctions", 1)) {} 502 503 void NotNullTerminatedResultCheck::storeOptions( 504 ClangTidyOptions::OptionMap &Opts) { 505 Options.store(Opts, "WantToUseSafeFunctions", WantToUseSafeFunctions); 506 } 507 508 void NotNullTerminatedResultCheck::registerPPCallbacks( 509 const SourceManager &SM, Preprocessor *pp, Preprocessor *ModuleExpanderPP) { 510 PP = pp; 511 } 512 513 namespace { 514 AST_MATCHER_P(Expr, hasDefinition, ast_matchers::internal::Matcher<Expr>, 515 InnerMatcher) { 516 const Expr *SimpleNode = &Node; 517 SimpleNode = SimpleNode->IgnoreParenImpCasts(); 518 519 if (InnerMatcher.matches(*SimpleNode, Finder, Builder)) 520 return true; 521 522 auto DREHasInit = ignoringImpCasts( 523 declRefExpr(to(varDecl(hasInitializer(ignoringImpCasts(InnerMatcher)))))); 524 525 if (DREHasInit.matches(*SimpleNode, Finder, Builder)) 526 return true; 527 528 const char *const VarDeclName = "variable-declaration"; 529 auto DREHasDefinition = ignoringImpCasts(declRefExpr( 530 allOf(to(varDecl().bind(VarDeclName)), 531 hasAncestor(compoundStmt(hasDescendant(binaryOperator( 532 hasLHS(declRefExpr(to(varDecl(equalsBoundNode(VarDeclName))))), 533 hasRHS(ignoringImpCasts(InnerMatcher))))))))); 534 535 if (DREHasDefinition.matches(*SimpleNode, Finder, Builder)) 536 return true; 537 538 return false; 539 } 540 } // namespace 541 542 void NotNullTerminatedResultCheck::registerMatchers(MatchFinder *Finder) { 543 auto IncOp = 544 binaryOperator(hasOperatorName("+"), 545 hasEitherOperand(ignoringParenImpCasts(integerLiteral()))); 546 547 auto DecOp = 548 binaryOperator(hasOperatorName("-"), 549 hasEitherOperand(ignoringParenImpCasts(integerLiteral()))); 550 551 auto HasIncOp = anyOf(ignoringImpCasts(IncOp), hasDescendant(IncOp)); 552 auto HasDecOp = anyOf(ignoringImpCasts(DecOp), hasDescendant(DecOp)); 553 554 auto Container = ignoringImpCasts(cxxMemberCallExpr(hasDescendant(declRefExpr( 555 hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(recordDecl( 556 hasAnyName("::std::vector", "::std::list", "::std::deque")))))))))); 557 558 auto StringTy = type(hasUnqualifiedDesugaredType(recordType( 559 hasDeclaration(cxxRecordDecl(hasName("::std::basic_string")))))); 560 561 auto AnyOfStringTy = 562 anyOf(hasType(StringTy), hasType(qualType(pointsTo(StringTy)))); 563 564 auto CharTyArray = hasType(qualType(hasCanonicalType( 565 arrayType(hasElementType(isAnyCharacter())).bind(DestArrayTyName)))); 566 567 auto CharTyPointer = hasType( 568 qualType(hasCanonicalType(pointerType(pointee(isAnyCharacter()))))); 569 570 auto AnyOfCharTy = anyOf(CharTyArray, CharTyPointer); 571 572 //===--------------------------------------------------------------------===// 573 // The following six cases match problematic length expressions. 574 //===--------------------------------------------------------------------===// 575 576 // - Example: char src[] = "foo"; strlen(src); 577 auto Strlen = 578 callExpr(callee(functionDecl(hasAnyName("::strlen", "::wcslen")))) 579 .bind(WrongLengthExprName); 580 581 // - Example: std::string str = "foo"; str.size(); 582 auto SizeOrLength = 583 cxxMemberCallExpr( 584 allOf(on(expr(AnyOfStringTy).bind("Foo")), 585 has(memberExpr(member(hasAnyName("size", "length")))))) 586 .bind(WrongLengthExprName); 587 588 // - Example: char src[] = "foo"; sizeof(src); 589 auto SizeOfCharExpr = unaryExprOrTypeTraitExpr(has(expr(AnyOfCharTy))); 590 591 auto WrongLength = 592 ignoringImpCasts(anyOf(Strlen, SizeOrLength, hasDescendant(Strlen), 593 hasDescendant(SizeOrLength))); 594 595 // - Example: length = strlen(src); 596 auto DREWithoutInc = 597 ignoringImpCasts(declRefExpr(to(varDecl(hasInitializer(WrongLength))))); 598 599 auto AnyOfCallOrDREWithoutInc = anyOf(DREWithoutInc, WrongLength); 600 601 // - Example: int getLength(const char *str) { return strlen(str); } 602 auto CallExprReturnWithoutInc = ignoringImpCasts(callExpr(callee(functionDecl( 603 hasBody(has(returnStmt(hasReturnValue(AnyOfCallOrDREWithoutInc)))))))); 604 605 // - Example: int length = getLength(src); 606 auto DREHasReturnWithoutInc = ignoringImpCasts( 607 declRefExpr(to(varDecl(hasInitializer(CallExprReturnWithoutInc))))); 608 609 auto AnyOfWrongLengthInit = 610 anyOf(WrongLength, AnyOfCallOrDREWithoutInc, CallExprReturnWithoutInc, 611 DREHasReturnWithoutInc); 612 613 //===--------------------------------------------------------------------===// 614 // The following five cases match the 'destination' array length's 615 // expression which is used in 'memcpy()' and 'memmove()' matchers. 616 //===--------------------------------------------------------------------===// 617 618 // Note: Sometimes the size of char is explicitly written out. 619 auto SizeExpr = anyOf(SizeOfCharExpr, integerLiteral(equals(1))); 620 621 auto MallocLengthExpr = allOf( 622 callee(functionDecl( 623 hasAnyName("::alloca", "::calloc", "malloc", "realloc"))), 624 hasAnyArgument(allOf(unless(SizeExpr), expr().bind(DestMallocExprName)))); 625 626 // - Example: (char *)malloc(length); 627 auto DestMalloc = anyOf(callExpr(MallocLengthExpr), 628 hasDescendant(callExpr(MallocLengthExpr))); 629 630 // - Example: new char[length]; 631 auto DestCXXNewExpr = ignoringImpCasts( 632 cxxNewExpr(hasArraySize(expr().bind(DestMallocExprName)))); 633 634 auto AnyOfDestInit = anyOf(DestMalloc, DestCXXNewExpr); 635 636 // - Example: char dest[13]; or char dest[length]; 637 auto DestArrayTyDecl = declRefExpr( 638 to(anyOf(varDecl(CharTyArray).bind(DestVarDeclName), 639 varDecl(hasInitializer(AnyOfDestInit)).bind(DestVarDeclName)))); 640 641 // - Example: foo[bar[baz]].qux; (or just ParmVarDecl) 642 auto DestUnknownDecl = 643 declRefExpr(allOf(to(varDecl(AnyOfCharTy).bind(DestVarDeclName)), 644 expr().bind(UnknownDestName))) 645 .bind(DestExprName); 646 647 auto AnyOfDestDecl = ignoringImpCasts( 648 anyOf(allOf(hasDefinition(anyOf(AnyOfDestInit, DestArrayTyDecl, 649 hasDescendant(DestArrayTyDecl))), 650 expr().bind(DestExprName)), 651 anyOf(DestUnknownDecl, hasDescendant(DestUnknownDecl)))); 652 653 auto NullTerminatorExpr = binaryOperator( 654 hasLHS(anyOf(hasDescendant(declRefExpr( 655 to(varDecl(equalsBoundNode(DestVarDeclName))))), 656 hasDescendant(declRefExpr(equalsBoundNode(DestExprName))))), 657 hasRHS(ignoringImpCasts( 658 anyOf(characterLiteral(equals(0U)), integerLiteral(equals(0)))))); 659 660 auto SrcDecl = declRefExpr( 661 allOf(to(decl().bind(SrcVarDeclName)), 662 anyOf(hasAncestor(cxxMemberCallExpr().bind(SrcExprName)), 663 expr().bind(SrcExprName)))); 664 665 auto AnyOfSrcDecl = 666 ignoringImpCasts(anyOf(stringLiteral().bind(SrcExprName), 667 hasDescendant(stringLiteral().bind(SrcExprName)), 668 SrcDecl, hasDescendant(SrcDecl))); 669 670 //===--------------------------------------------------------------------===// 671 // Match the problematic function calls. 672 //===--------------------------------------------------------------------===// 673 674 struct CallContext { 675 CallContext(StringRef Name, Optional<unsigned> DestinationPos, 676 Optional<unsigned> SourcePos, unsigned LengthPos, 677 bool WithIncrease) 678 : Name(Name), DestinationPos(DestinationPos), SourcePos(SourcePos), 679 LengthPos(LengthPos), WithIncrease(WithIncrease){}; 680 681 StringRef Name; 682 Optional<unsigned> DestinationPos; 683 Optional<unsigned> SourcePos; 684 unsigned LengthPos; 685 bool WithIncrease; 686 }; 687 688 auto MatchDestination = [=](CallContext CC) { 689 return hasArgument(*CC.DestinationPos, 690 allOf(AnyOfDestDecl, 691 unless(hasAncestor(compoundStmt( 692 hasDescendant(NullTerminatorExpr)))), 693 unless(Container))); 694 }; 695 696 auto MatchSource = [=](CallContext CC) { 697 return hasArgument(*CC.SourcePos, AnyOfSrcDecl); 698 }; 699 700 auto MatchGivenLength = [=](CallContext CC) { 701 return hasArgument( 702 CC.LengthPos, 703 allOf( 704 anyOf( 705 ignoringImpCasts(integerLiteral().bind(WrongLengthExprName)), 706 allOf(unless(hasDefinition(SizeOfCharExpr)), 707 allOf(CC.WithIncrease 708 ? ignoringImpCasts(hasDefinition(HasIncOp)) 709 : ignoringImpCasts(allOf( 710 unless(hasDefinition(HasIncOp)), 711 anyOf(hasDefinition(binaryOperator().bind( 712 UnknownLengthName)), 713 hasDefinition(anything())))), 714 AnyOfWrongLengthInit))), 715 expr().bind(LengthExprName))); 716 }; 717 718 auto MatchCall = [=](CallContext CC) { 719 std::string CharHandlerFuncName = "::" + CC.Name.str(); 720 721 // Try to match with 'wchar_t' based function calls. 722 std::string WcharHandlerFuncName = 723 "::" + (CC.Name.startswith("mem") ? "w" + CC.Name.str() 724 : "wcs" + CC.Name.substr(3).str()); 725 726 return allOf(callee(functionDecl( 727 hasAnyName(CharHandlerFuncName, WcharHandlerFuncName))), 728 MatchGivenLength(CC)); 729 }; 730 731 auto Match = [=](CallContext CC) { 732 if (CC.DestinationPos && CC.SourcePos) 733 return allOf(MatchCall(CC), MatchDestination(CC), MatchSource(CC)); 734 735 if (CC.DestinationPos && !CC.SourcePos) 736 return allOf(MatchCall(CC), MatchDestination(CC), 737 hasArgument(*CC.DestinationPos, anything())); 738 739 if (!CC.DestinationPos && CC.SourcePos) 740 return allOf(MatchCall(CC), MatchSource(CC), 741 hasArgument(*CC.SourcePos, anything())); 742 743 llvm_unreachable("Unhandled match"); 744 }; 745 746 // void *memcpy(void *dest, const void *src, size_t count) 747 auto Memcpy = Match({"memcpy", 0, 1, 2, false}); 748 749 // errno_t memcpy_s(void *dest, size_t ds, const void *src, size_t count) 750 auto Memcpy_s = Match({"memcpy_s", 0, 2, 3, false}); 751 752 // void *memchr(const void *src, int c, size_t count) 753 auto Memchr = Match({"memchr", None, 0, 2, false}); 754 755 // void *memmove(void *dest, const void *src, size_t count) 756 auto Memmove = Match({"memmove", 0, 1, 2, false}); 757 758 // errno_t memmove_s(void *dest, size_t ds, const void *src, size_t count) 759 auto Memmove_s = Match({"memmove_s", 0, 2, 3, false}); 760 761 // int strncmp(const char *str1, const char *str2, size_t count); 762 auto StrncmpRHS = Match({"strncmp", None, 1, 2, true}); 763 auto StrncmpLHS = Match({"strncmp", None, 0, 2, true}); 764 765 // size_t strxfrm(char *dest, const char *src, size_t count); 766 auto Strxfrm = Match({"strxfrm", 0, 1, 2, false}); 767 768 // errno_t strerror_s(char *buffer, size_t bufferSize, int errnum); 769 auto Strerror_s = Match({"strerror_s", 0, None, 1, false}); 770 771 auto AnyOfMatchers = anyOf(Memcpy, Memcpy_s, Memmove, Memmove_s, StrncmpRHS, 772 StrncmpLHS, Strxfrm, Strerror_s); 773 774 Finder->addMatcher(callExpr(AnyOfMatchers).bind(FunctionExprName), this); 775 776 // Need to remove the CastExpr from 'memchr()' as 'strchr()' returns 'char *'. 777 Finder->addMatcher( 778 callExpr(Memchr, 779 unless(hasAncestor(castExpr(unless(implicitCastExpr()))))) 780 .bind(FunctionExprName), 781 this); 782 Finder->addMatcher( 783 castExpr(allOf(unless(implicitCastExpr()), 784 has(callExpr(Memchr).bind(FunctionExprName)))) 785 .bind(CastExprName), 786 this); 787 } 788 789 void NotNullTerminatedResultCheck::check( 790 const MatchFinder::MatchResult &Result) { 791 const auto *FunctionExpr = Result.Nodes.getNodeAs<CallExpr>(FunctionExprName); 792 if (FunctionExpr->getBeginLoc().isMacroID()) 793 return; 794 795 if (WantToUseSafeFunctions && PP->isMacroDefined("__STDC_LIB_EXT1__")) { 796 Optional<bool> AreSafeFunctionsWanted; 797 798 Preprocessor::macro_iterator It = PP->macro_begin(); 799 while (It != PP->macro_end() && !AreSafeFunctionsWanted.hasValue()) { 800 if (It->first->getName() == "__STDC_WANT_LIB_EXT1__") { 801 const auto *MI = PP->getMacroInfo(It->first); 802 const auto &T = MI->tokens().back(); 803 StringRef ValueStr = StringRef(T.getLiteralData(), T.getLength()); 804 llvm::APInt IntValue; 805 ValueStr.getAsInteger(10, IntValue); 806 AreSafeFunctionsWanted = IntValue.getSExtValue(); 807 } 808 809 ++It; 810 } 811 812 if (AreSafeFunctionsWanted.hasValue()) 813 UseSafeFunctions = AreSafeFunctionsWanted.getValue(); 814 } 815 816 StringRef Name = FunctionExpr->getDirectCallee()->getName(); 817 if (Name.startswith("mem") || Name.startswith("wmem")) 818 memoryHandlerFunctionFix(Name, Result); 819 else if (Name == "strerror_s") 820 strerror_sFix(Result); 821 else if (Name.endswith("ncmp")) 822 ncmpFix(Name, Result); 823 else if (Name.endswith("xfrm")) 824 xfrmFix(Name, Result); 825 } 826 827 void NotNullTerminatedResultCheck::memoryHandlerFunctionFix( 828 StringRef Name, const MatchFinder::MatchResult &Result) { 829 if (isCorrectGivenLength(Result)) 830 return; 831 832 if (Name.endswith("chr")) { 833 memchrFix(Name, Result); 834 return; 835 } 836 837 if ((Name.contains("cpy") || Name.contains("move")) && 838 (isDestAndSrcEquals(Result) || isFixedGivenLengthAndUnknownSrc(Result))) 839 return; 840 841 auto Diag = 842 diag(Result.Nodes.getNodeAs<CallExpr>(FunctionExprName)->getBeginLoc(), 843 "the result from calling '%0' is not null-terminated") 844 << Name; 845 846 if (Name.endswith("cpy")) { 847 memcpyFix(Name, Result, Diag); 848 } else if (Name.endswith("cpy_s")) { 849 memcpy_sFix(Name, Result, Diag); 850 } else if (Name.endswith("move")) { 851 memmoveFix(Name, Result, Diag); 852 } else if (Name.endswith("move_s")) { 853 isDestCapacityFix(Result, Diag); 854 lengthArgHandle(LengthHandleKind::Increase, Result, Diag); 855 } 856 } 857 858 void NotNullTerminatedResultCheck::memcpyFix( 859 StringRef Name, const MatchFinder::MatchResult &Result, 860 DiagnosticBuilder &Diag) { 861 bool IsOverflows = isDestCapacityFix(Result, Diag); 862 bool IsDestFixed = isDestExprFix(Result, Diag); 863 864 bool IsCopy = 865 isGivenLengthEqualToSrcLength(Result) || isDestBasedOnGivenLength(Result); 866 867 bool IsSafe = UseSafeFunctions && IsOverflows && isKnownDest(Result) && 868 !isDestBasedOnGivenLength(Result); 869 870 bool IsDestLengthNotRequired = 871 IsSafe && getLangOpts().CPlusPlus && 872 Result.Nodes.getNodeAs<ArrayType>(DestArrayTyName) && !IsDestFixed; 873 874 renameMemcpy(Name, IsCopy, IsSafe, Result, Diag); 875 876 if (IsSafe && !IsDestLengthNotRequired) 877 insertDestCapacityArg(IsOverflows, Name, Result, Diag); 878 879 if (IsCopy) 880 removeArg(2, Result, Diag); 881 882 if (!IsCopy && !IsSafe) 883 insertNullTerminatorExpr(Name, Result, Diag); 884 } 885 886 void NotNullTerminatedResultCheck::memcpy_sFix( 887 StringRef Name, const MatchFinder::MatchResult &Result, 888 DiagnosticBuilder &Diag) { 889 bool IsOverflows = isDestCapacityFix(Result, Diag); 890 bool IsDestFixed = isDestExprFix(Result, Diag); 891 892 bool RemoveDestLength = getLangOpts().CPlusPlus && 893 Result.Nodes.getNodeAs<ArrayType>(DestArrayTyName) && 894 !IsDestFixed; 895 bool IsCopy = isGivenLengthEqualToSrcLength(Result); 896 bool IsSafe = IsOverflows; 897 898 renameMemcpy(Name, IsCopy, IsSafe, Result, Diag); 899 900 if (!IsSafe || (IsSafe && RemoveDestLength)) 901 removeArg(1, Result, Diag); 902 else if (IsOverflows && isKnownDest(Result)) 903 lengthArgPosHandle(1, LengthHandleKind::Increase, Result, Diag); 904 905 if (IsCopy) 906 removeArg(3, Result, Diag); 907 908 if (!IsCopy && !IsSafe) 909 insertNullTerminatorExpr(Name, Result, Diag); 910 } 911 912 void NotNullTerminatedResultCheck::memchrFix( 913 StringRef Name, const MatchFinder::MatchResult &Result) { 914 const auto *FunctionExpr = Result.Nodes.getNodeAs<CallExpr>(FunctionExprName); 915 if (const auto GivenCL = dyn_cast<CharacterLiteral>(FunctionExpr->getArg(1))) 916 if (GivenCL->getValue() != 0) 917 return; 918 919 auto Diag = diag(FunctionExpr->getArg(2)->IgnoreParenCasts()->getBeginLoc(), 920 "the length is too short to include the null terminator"); 921 922 if (const auto *CastExpr = Result.Nodes.getNodeAs<Expr>(CastExprName)) { 923 const auto CastRemoveFix = FixItHint::CreateRemoval( 924 SourceRange(CastExpr->getBeginLoc(), 925 FunctionExpr->getBeginLoc().getLocWithOffset(-1))); 926 Diag << CastRemoveFix; 927 } 928 929 StringRef NewFuncName = (Name[0] != 'w') ? "strchr" : "wcschr"; 930 renameFunc(NewFuncName, Result, Diag); 931 removeArg(2, Result, Diag); 932 } 933 934 void NotNullTerminatedResultCheck::memmoveFix( 935 StringRef Name, const MatchFinder::MatchResult &Result, 936 DiagnosticBuilder &Diag) { 937 bool IsOverflows = isDestCapacityFix(Result, Diag); 938 939 if (UseSafeFunctions && isKnownDest(Result)) { 940 renameFunc((Name[0] != 'w') ? "memmove_s" : "wmemmove_s", Result, Diag); 941 insertDestCapacityArg(IsOverflows, Name, Result, Diag); 942 } 943 944 lengthArgHandle(LengthHandleKind::Increase, Result, Diag); 945 } 946 947 void NotNullTerminatedResultCheck::strerror_sFix( 948 const MatchFinder::MatchResult &Result) { 949 auto Diag = 950 diag(Result.Nodes.getNodeAs<CallExpr>(FunctionExprName)->getBeginLoc(), 951 "the result from calling 'strerror_s' is not null-terminated and " 952 "missing the last character of the error message"); 953 954 isDestCapacityFix(Result, Diag); 955 lengthArgHandle(LengthHandleKind::Increase, Result, Diag); 956 } 957 958 void NotNullTerminatedResultCheck::ncmpFix( 959 StringRef Name, const MatchFinder::MatchResult &Result) { 960 const auto *FunctionExpr = Result.Nodes.getNodeAs<CallExpr>(FunctionExprName); 961 const Expr *FirstArgExpr = FunctionExpr->getArg(0)->IgnoreImpCasts(); 962 const Expr *SecondArgExpr = FunctionExpr->getArg(1)->IgnoreImpCasts(); 963 bool IsLengthTooLong = false; 964 965 if (const CallExpr *StrlenExpr = getStrlenExpr(Result)) { 966 const Expr *LengthExprArg = StrlenExpr->getArg(0); 967 StringRef FirstExprStr = exprToStr(FirstArgExpr, Result).trim(); 968 StringRef SecondExprStr = exprToStr(SecondArgExpr, Result).trim(); 969 StringRef LengthArgStr = exprToStr(LengthExprArg, Result).trim(); 970 IsLengthTooLong = 971 LengthArgStr == FirstExprStr || LengthArgStr == SecondExprStr; 972 } else { 973 int SrcLength = 974 getLength(Result.Nodes.getNodeAs<Expr>(SrcExprName), Result); 975 int GivenLength = getGivenLength(Result); 976 if (SrcLength != 0 && GivenLength != 0) 977 IsLengthTooLong = GivenLength > SrcLength; 978 } 979 980 if (!IsLengthTooLong && !isStringDataAndLength(Result)) 981 return; 982 983 auto Diag = diag(FunctionExpr->getArg(2)->IgnoreParenCasts()->getBeginLoc(), 984 "comparison length is too long and might lead to a " 985 "buffer overflow"); 986 987 lengthArgHandle(LengthHandleKind::Decrease, Result, Diag); 988 } 989 990 void NotNullTerminatedResultCheck::xfrmFix( 991 StringRef Name, const MatchFinder::MatchResult &Result) { 992 if (!isDestCapacityOverflows(Result)) 993 return; 994 995 auto Diag = 996 diag(Result.Nodes.getNodeAs<CallExpr>(FunctionExprName)->getBeginLoc(), 997 "the result from calling '%0' is not null-terminated") 998 << Name; 999 1000 isDestCapacityFix(Result, Diag); 1001 lengthArgHandle(LengthHandleKind::Increase, Result, Diag); 1002 } 1003 1004 } // namespace bugprone 1005 } // namespace tidy 1006 } // namespace clang 1007