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