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