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 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(), 0); 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 != "" && LengthExprStr != "" && 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 (dyn_cast<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(), 0); 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", 1)) {} 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 allOf(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( 585 allOf(on(expr(AnyOfStringTy).bind("Foo")), 586 has(memberExpr(member(hasAnyName("size", "length")))))) 587 .bind(WrongLengthExprName); 588 589 // - Example: char src[] = "foo"; sizeof(src); 590 auto SizeOfCharExpr = unaryExprOrTypeTraitExpr(has(expr(AnyOfCharTy))); 591 592 auto WrongLength = 593 ignoringImpCasts(anyOf(Strlen, SizeOrLength, hasDescendant(Strlen), 594 hasDescendant(SizeOrLength))); 595 596 // - Example: length = strlen(src); 597 auto DREWithoutInc = 598 ignoringImpCasts(declRefExpr(to(varDecl(hasInitializer(WrongLength))))); 599 600 auto AnyOfCallOrDREWithoutInc = anyOf(DREWithoutInc, WrongLength); 601 602 // - Example: int getLength(const char *str) { return strlen(str); } 603 auto CallExprReturnWithoutInc = ignoringImpCasts(callExpr(callee(functionDecl( 604 hasBody(has(returnStmt(hasReturnValue(AnyOfCallOrDREWithoutInc)))))))); 605 606 // - Example: int length = getLength(src); 607 auto DREHasReturnWithoutInc = ignoringImpCasts( 608 declRefExpr(to(varDecl(hasInitializer(CallExprReturnWithoutInc))))); 609 610 auto AnyOfWrongLengthInit = 611 anyOf(WrongLength, AnyOfCallOrDREWithoutInc, CallExprReturnWithoutInc, 612 DREHasReturnWithoutInc); 613 614 //===--------------------------------------------------------------------===// 615 // The following five cases match the 'destination' array length's 616 // expression which is used in 'memcpy()' and 'memmove()' matchers. 617 //===--------------------------------------------------------------------===// 618 619 // Note: Sometimes the size of char is explicitly written out. 620 auto SizeExpr = anyOf(SizeOfCharExpr, integerLiteral(equals(1))); 621 622 auto MallocLengthExpr = allOf( 623 callee(functionDecl( 624 hasAnyName("::alloca", "::calloc", "malloc", "realloc"))), 625 hasAnyArgument(allOf(unless(SizeExpr), expr().bind(DestMallocExprName)))); 626 627 // - Example: (char *)malloc(length); 628 auto DestMalloc = anyOf(callExpr(MallocLengthExpr), 629 hasDescendant(callExpr(MallocLengthExpr))); 630 631 // - Example: new char[length]; 632 auto DestCXXNewExpr = ignoringImpCasts( 633 cxxNewExpr(hasArraySize(expr().bind(DestMallocExprName)))); 634 635 auto AnyOfDestInit = anyOf(DestMalloc, DestCXXNewExpr); 636 637 // - Example: char dest[13]; or char dest[length]; 638 auto DestArrayTyDecl = declRefExpr( 639 to(anyOf(varDecl(CharTyArray).bind(DestVarDeclName), 640 varDecl(hasInitializer(AnyOfDestInit)).bind(DestVarDeclName)))); 641 642 // - Example: foo[bar[baz]].qux; (or just ParmVarDecl) 643 auto DestUnknownDecl = 644 declRefExpr(allOf(to(varDecl(AnyOfCharTy).bind(DestVarDeclName)), 645 expr().bind(UnknownDestName))) 646 .bind(DestExprName); 647 648 auto AnyOfDestDecl = ignoringImpCasts( 649 anyOf(allOf(hasDefinition(anyOf(AnyOfDestInit, DestArrayTyDecl, 650 hasDescendant(DestArrayTyDecl))), 651 expr().bind(DestExprName)), 652 anyOf(DestUnknownDecl, hasDescendant(DestUnknownDecl)))); 653 654 auto NullTerminatorExpr = binaryOperator( 655 hasLHS(anyOf(hasDescendant(declRefExpr( 656 to(varDecl(equalsBoundNode(DestVarDeclName))))), 657 hasDescendant(declRefExpr(equalsBoundNode(DestExprName))))), 658 hasRHS(ignoringImpCasts( 659 anyOf(characterLiteral(equals(0U)), integerLiteral(equals(0)))))); 660 661 auto SrcDecl = declRefExpr( 662 allOf(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, Optional<unsigned> DestinationPos, 677 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 Optional<unsigned> DestinationPos; 684 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 Memcpy_s = Match({"memcpy_s", 0, 2, 3, false}); 752 753 // void *memchr(const void *src, int c, size_t count) 754 auto Memchr = Match({"memchr", None, 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 Memmove_s = 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", None, 1, 2, true}); 764 auto StrncmpLHS = Match({"strncmp", None, 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 Strerror_s = Match({"strerror_s", 0, None, 1, false}); 771 772 auto AnyOfMatchers = anyOf(Memcpy, Memcpy_s, Memmove, Memmove_s, StrncmpRHS, 773 StrncmpLHS, Strxfrm, Strerror_s); 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 Optional<bool> AreSafeFunctionsWanted; 798 799 Preprocessor::macro_iterator It = PP->macro_begin(); 800 while (It != PP->macro_end() && !AreSafeFunctionsWanted.hasValue()) { 801 if (It->first->getName() == "__STDC_WANT_LIB_EXT1__") { 802 const auto *MI = PP->getMacroInfo(It->first); 803 const auto &T = MI->tokens().back(); 804 StringRef ValueStr = StringRef(T.getLiteralData(), T.getLength()); 805 llvm::APInt IntValue; 806 ValueStr.getAsInteger(10, IntValue); 807 AreSafeFunctionsWanted = IntValue.getZExtValue(); 808 } 809 810 ++It; 811 } 812 813 if (AreSafeFunctionsWanted.hasValue()) 814 UseSafeFunctions = AreSafeFunctionsWanted.getValue(); 815 } 816 817 StringRef Name = FunctionExpr->getDirectCallee()->getName(); 818 if (Name.startswith("mem") || Name.startswith("wmem")) 819 memoryHandlerFunctionFix(Name, Result); 820 else if (Name == "strerror_s") 821 strerror_sFix(Result); 822 else if (Name.endswith("ncmp")) 823 ncmpFix(Name, Result); 824 else if (Name.endswith("xfrm")) 825 xfrmFix(Name, Result); 826 } 827 828 void NotNullTerminatedResultCheck::memoryHandlerFunctionFix( 829 StringRef Name, const MatchFinder::MatchResult &Result) { 830 if (isCorrectGivenLength(Result)) 831 return; 832 833 if (Name.endswith("chr")) { 834 memchrFix(Name, Result); 835 return; 836 } 837 838 if ((Name.contains("cpy") || Name.contains("move")) && 839 (isDestAndSrcEquals(Result) || isFixedGivenLengthAndUnknownSrc(Result))) 840 return; 841 842 auto Diag = 843 diag(Result.Nodes.getNodeAs<CallExpr>(FunctionExprName)->getBeginLoc(), 844 "the result from calling '%0' is not null-terminated") 845 << Name; 846 847 if (Name.endswith("cpy")) { 848 memcpyFix(Name, Result, Diag); 849 } else if (Name.endswith("cpy_s")) { 850 memcpy_sFix(Name, Result, Diag); 851 } else if (Name.endswith("move")) { 852 memmoveFix(Name, Result, Diag); 853 } else if (Name.endswith("move_s")) { 854 isDestCapacityFix(Result, Diag); 855 lengthArgHandle(LengthHandleKind::Increase, Result, Diag); 856 } 857 } 858 859 void NotNullTerminatedResultCheck::memcpyFix( 860 StringRef Name, const MatchFinder::MatchResult &Result, 861 DiagnosticBuilder &Diag) { 862 bool IsOverflows = isDestCapacityFix(Result, Diag); 863 bool IsDestFixed = isDestExprFix(Result, Diag); 864 865 bool IsCopy = 866 isGivenLengthEqualToSrcLength(Result) || isDestBasedOnGivenLength(Result); 867 868 bool IsSafe = UseSafeFunctions && IsOverflows && isKnownDest(Result) && 869 !isDestBasedOnGivenLength(Result); 870 871 bool IsDestLengthNotRequired = 872 IsSafe && getLangOpts().CPlusPlus && 873 Result.Nodes.getNodeAs<ArrayType>(DestArrayTyName) && !IsDestFixed; 874 875 renameMemcpy(Name, IsCopy, IsSafe, Result, Diag); 876 877 if (IsSafe && !IsDestLengthNotRequired) 878 insertDestCapacityArg(IsOverflows, Name, Result, Diag); 879 880 if (IsCopy) 881 removeArg(2, Result, Diag); 882 883 if (!IsCopy && !IsSafe) 884 insertNullTerminatorExpr(Name, Result, Diag); 885 } 886 887 void NotNullTerminatedResultCheck::memcpy_sFix( 888 StringRef Name, const MatchFinder::MatchResult &Result, 889 DiagnosticBuilder &Diag) { 890 bool IsOverflows = isDestCapacityFix(Result, Diag); 891 bool IsDestFixed = isDestExprFix(Result, Diag); 892 893 bool RemoveDestLength = getLangOpts().CPlusPlus && 894 Result.Nodes.getNodeAs<ArrayType>(DestArrayTyName) && 895 !IsDestFixed; 896 bool IsCopy = isGivenLengthEqualToSrcLength(Result); 897 bool IsSafe = IsOverflows; 898 899 renameMemcpy(Name, IsCopy, IsSafe, Result, Diag); 900 901 if (!IsSafe || (IsSafe && RemoveDestLength)) 902 removeArg(1, Result, Diag); 903 else if (IsOverflows && isKnownDest(Result)) 904 lengthArgPosHandle(1, LengthHandleKind::Increase, Result, Diag); 905 906 if (IsCopy) 907 removeArg(3, Result, Diag); 908 909 if (!IsCopy && !IsSafe) 910 insertNullTerminatorExpr(Name, Result, Diag); 911 } 912 913 void NotNullTerminatedResultCheck::memchrFix( 914 StringRef Name, const MatchFinder::MatchResult &Result) { 915 const auto *FunctionExpr = Result.Nodes.getNodeAs<CallExpr>(FunctionExprName); 916 if (const auto GivenCL = dyn_cast<CharacterLiteral>(FunctionExpr->getArg(1))) 917 if (GivenCL->getValue() != 0) 918 return; 919 920 auto Diag = diag(FunctionExpr->getArg(2)->IgnoreParenCasts()->getBeginLoc(), 921 "the length is too short to include the null terminator"); 922 923 if (const auto *CastExpr = Result.Nodes.getNodeAs<Expr>(CastExprName)) { 924 const auto CastRemoveFix = FixItHint::CreateRemoval( 925 SourceRange(CastExpr->getBeginLoc(), 926 FunctionExpr->getBeginLoc().getLocWithOffset(-1))); 927 Diag << CastRemoveFix; 928 } 929 930 StringRef NewFuncName = (Name[0] != 'w') ? "strchr" : "wcschr"; 931 renameFunc(NewFuncName, Result, Diag); 932 removeArg(2, Result, Diag); 933 } 934 935 void NotNullTerminatedResultCheck::memmoveFix( 936 StringRef Name, const MatchFinder::MatchResult &Result, 937 DiagnosticBuilder &Diag) { 938 bool IsOverflows = isDestCapacityFix(Result, Diag); 939 940 if (UseSafeFunctions && isKnownDest(Result)) { 941 renameFunc((Name[0] != 'w') ? "memmove_s" : "wmemmove_s", Result, Diag); 942 insertDestCapacityArg(IsOverflows, Name, Result, Diag); 943 } 944 945 lengthArgHandle(LengthHandleKind::Increase, Result, Diag); 946 } 947 948 void NotNullTerminatedResultCheck::strerror_sFix( 949 const MatchFinder::MatchResult &Result) { 950 auto Diag = 951 diag(Result.Nodes.getNodeAs<CallExpr>(FunctionExprName)->getBeginLoc(), 952 "the result from calling 'strerror_s' is not null-terminated and " 953 "missing the last character of the error message"); 954 955 isDestCapacityFix(Result, Diag); 956 lengthArgHandle(LengthHandleKind::Increase, Result, Diag); 957 } 958 959 void NotNullTerminatedResultCheck::ncmpFix( 960 StringRef Name, const MatchFinder::MatchResult &Result) { 961 const auto *FunctionExpr = Result.Nodes.getNodeAs<CallExpr>(FunctionExprName); 962 const Expr *FirstArgExpr = FunctionExpr->getArg(0)->IgnoreImpCasts(); 963 const Expr *SecondArgExpr = FunctionExpr->getArg(1)->IgnoreImpCasts(); 964 bool IsLengthTooLong = false; 965 966 if (const CallExpr *StrlenExpr = getStrlenExpr(Result)) { 967 const Expr *LengthExprArg = StrlenExpr->getArg(0); 968 StringRef FirstExprStr = exprToStr(FirstArgExpr, Result).trim(); 969 StringRef SecondExprStr = exprToStr(SecondArgExpr, Result).trim(); 970 StringRef LengthArgStr = exprToStr(LengthExprArg, Result).trim(); 971 IsLengthTooLong = 972 LengthArgStr == FirstExprStr || LengthArgStr == SecondExprStr; 973 } else { 974 int SrcLength = 975 getLength(Result.Nodes.getNodeAs<Expr>(SrcExprName), Result); 976 int GivenLength = getGivenLength(Result); 977 if (SrcLength != 0 && GivenLength != 0) 978 IsLengthTooLong = GivenLength > SrcLength; 979 } 980 981 if (!IsLengthTooLong && !isStringDataAndLength(Result)) 982 return; 983 984 auto Diag = diag(FunctionExpr->getArg(2)->IgnoreParenCasts()->getBeginLoc(), 985 "comparison length is too long and might lead to a " 986 "buffer overflow"); 987 988 lengthArgHandle(LengthHandleKind::Decrease, Result, Diag); 989 } 990 991 void NotNullTerminatedResultCheck::xfrmFix( 992 StringRef Name, const MatchFinder::MatchResult &Result) { 993 if (!isDestCapacityOverflows(Result)) 994 return; 995 996 auto Diag = 997 diag(Result.Nodes.getNodeAs<CallExpr>(FunctionExprName)->getBeginLoc(), 998 "the result from calling '%0' is not null-terminated") 999 << Name; 1000 1001 isDestCapacityFix(Result, Diag); 1002 lengthArgHandle(LengthHandleKind::Increase, Result, Diag); 1003 } 1004 1005 } // namespace bugprone 1006 } // namespace tidy 1007 } // namespace clang 1008