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" 38 static Preprocessor *
PP;
45 if (
const auto *DestMalloc = Result.Nodes.getNodeAs<Expr>(DestMallocExprName))
48 if (
const auto *DestVAT =
49 Result.Nodes.getNodeAs<VariableArrayType>(DestArrayTyName))
50 return DestVAT->getSizeExpr();
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();
63 const MatchFinder::MatchResult &Result) {
68 E = E->IgnoreImpCasts();
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();
77 if (
const auto *LengthIL = dyn_cast<IntegerLiteral>(E))
78 return LengthIL->getValue().getZExtValue();
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();
87 if (
const auto *SrcSL = dyn_cast<StringLiteral>(E))
88 return SrcSL->getLength();
97 return getLength(DestCapacityExpr, Result);
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"))
118 if (Result.Nodes.getNodeAs<Expr>(UnknownLengthName))
122 getLength(Result.Nodes.getNodeAs<Expr>(WrongLengthExprName), Result))
126 getLength(Result.Nodes.getNodeAs<Expr>(LengthExprName), Result))
131 if (
const Expr *Arg = StrlenCE->getArg(0)->IgnoreImpCasts())
132 if (
int ArgLength =
getLength(Arg, Result))
140 const MatchFinder::MatchResult &Result) {
144 return Lexer::getSourceText(
146 *Result.SourceManager, Result.Context->getLangOpts(), 0);
151 const MatchFinder::MatchResult &Result) {
152 return Lexer::getLocForEndOfToken(E->getEndLoc(), 0, *Result.SourceManager,
153 Result.Context->getLangOpts());
163 static bool isInjectUL(
const MatchFinder::MatchResult &Result) {
175 StringRef DestCapacityExprStr =
177 StringRef LengthExprStr =
178 exprToStr(Result.Nodes.getNodeAs<Expr>(LengthExprName), Result).trim();
180 return DestCapacityExprStr !=
"" && LengthExprStr !=
"" &&
181 DestCapacityExprStr.contains(LengthExprStr);
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();
196 const auto *DestExpr =
197 Result.Nodes.getNodeAs<CXXMemberCallExpr>(
DestExprName);
198 const auto *SrcExpr = Result.Nodes.getNodeAs<CXXMemberCallExpr>(
SrcExprName);
199 const auto *LengthExpr =
202 StringRef DestStr =
"", SrcStr =
"", LengthStr =
"";
204 if (
const CXXMethodDecl *DestMD = DestExpr->getMethodDecl())
205 DestStr = DestMD->getName();
208 if (
const CXXMethodDecl *SrcMD = SrcExpr->getMethodDecl())
209 SrcStr = SrcMD->getName();
212 if (
const CXXMethodDecl *LengthMD = LengthExpr->getMethodDecl())
213 LengthStr = LengthMD->getName();
215 return (LengthStr ==
"length" || LengthStr ==
"size") &&
216 (SrcStr ==
"data" || DestStr ==
"data");
221 if (Result.Nodes.getNodeAs<Expr>(UnknownLengthName))
228 int SrcLength =
getLength(Result.Nodes.getNodeAs<Expr>(SrcExprName), Result);
230 if (GivenLength != 0 && SrcLength != 0 && GivenLength == SrcLength)
233 if (
const auto *LengthExpr = Result.Nodes.getNodeAs<Expr>(LengthExprName))
234 if (dyn_cast<BinaryOperator>(LengthExpr->IgnoreParenImpCasts()))
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;
248 if (Result.Nodes.getNodeAs<Expr>(UnknownLengthName))
262 int DestCapacity =
getLength(DestCapacityExpr, Result);
265 if (GivenLength != 0 && DestCapacity != 0)
270 StringRef DestCapacityExprStr =
exprToStr(DestCapacityExpr, Result);
271 if (DestCapacityExprStr.contains(
"+1") || DestCapacityExprStr.contains(
"+ 1"))
279 if (Result.Nodes.getNodeAs<IntegerLiteral>(WrongLengthExprName))
280 return !
getLength(Result.Nodes.getNodeAs<Expr>(SrcExprName), Result);
292 const MatchFinder::MatchResult &Result,
293 DiagnosticBuilder &Diag) {
294 LengthExpr = LengthExpr->IgnoreParenImpCasts();
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;
308 if (!IsMacroDefinition) {
309 if (
const auto *LengthIL = dyn_cast<IntegerLiteral>(LengthExpr)) {
310 size_t NewLength = LengthIL->getValue().getZExtValue() +
311 (LengthHandle == LengthHandleKind::Increase
315 const auto NewLengthFix = FixItHint::CreateReplacement(
316 LengthIL->getSourceRange(),
317 (Twine(NewLength) + (
isInjectUL(Result) ?
"UL" :
"")).str());
318 Diag << NewLengthFix;
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();
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)});
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()});
349 bool NeedInnerParen = BO && BO->getOpcode() != BO_Add;
352 Diag << FixItHint::CreateInsertion(LengthExpr->getBeginLoc(),
"(");
354 SmallString<8> Injection;
357 Injection += LengthHandle == LengthHandleKind::Increase ?
" + 1" :
" - 1";
361 Diag << FixItHint::CreateInsertion(
exprLocEnd(LengthExpr, Result), Injection);
365 const MatchFinder::MatchResult &Result,
366 DiagnosticBuilder &Diag) {
367 const auto *LengthExpr = Result.Nodes.getNodeAs<Expr>(
LengthExprName);
372 const MatchFinder::MatchResult &Result,
373 DiagnosticBuilder &Diag) {
374 const auto *FunctionExpr = Result.Nodes.getNodeAs<CallExpr>(
FunctionExprName);
381 DiagnosticBuilder &Diag) {
382 const auto *Dest = Result.Nodes.getNodeAs<Expr>(
DestExprName);
386 std::string TempTyStr = Dest->getType().getAsString();
387 StringRef TyStr = TempTyStr;
388 if (TyStr.startswith(
"char") || TyStr.startswith(
"wchar_t"))
391 Diag << FixItHint::CreateInsertion(Dest->getBeginLoc(),
"(char *)");
398 DiagnosticBuilder &Diag) {
407 static void removeArg(
int ArgPos,
const MatchFinder::MatchResult &Result,
408 DiagnosticBuilder &Diag) {
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(
418 exprLocEnd(ArgToRemove, Result).getLocWithOffset(-1)));
419 Diag << RemoveArgFix;
423 const MatchFinder::MatchResult &Result,
424 DiagnosticBuilder &Diag) {
425 const auto *FunctionExpr = Result.Nodes.getNodeAs<CallExpr>(
FunctionExprName);
427 FunctionExpr->getDirectCallee()->getIdentifier()->getLength();
428 SourceRange FuncNameRange(
429 FunctionExpr->getBeginLoc(),
430 FunctionExpr->getBeginLoc().getLocWithOffset(FuncNameLength - 1));
432 const auto FuncNameFix =
433 FixItHint::CreateReplacement(FuncNameRange, NewFuncName);
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" :
"";
448 const MatchFinder::MatchResult &Result,
449 DiagnosticBuilder &Diag) {
450 const auto *FunctionExpr = Result.Nodes.getNodeAs<CallExpr>(
FunctionExprName);
451 SmallString<64> NewSecondArg;
454 NewSecondArg = Twine(IsOverflows ? DestLength + 1 : DestLength).str();
458 (IsOverflows ? (!
isInjectUL(Result) ?
" + 1" :
" + 1UL") :
""))
462 NewSecondArg +=
", ";
463 const auto InsertNewArgFix = FixItHint::CreateInsertion(
464 FunctionExpr->getArg(1)->getBeginLoc(), NewSecondArg);
465 Diag << InsertNewArgFix;
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);
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\';"))
489 const auto AddNullTerminatorExprFix = FixItHint::CreateInsertion(
490 exprLocEnd(FunctionExpr, Result).getLocWithOffset(1),
491 NewAddNullTermExprStr);
492 Diag << AddNullTerminatorExprFix;
499 NotNullTerminatedResultCheck::NotNullTerminatedResultCheck(
502 WantToUseSafeFunctions(Options.get(
"WantToUseSafeFunctions", 1)) {}
506 Options.
store(Opts,
"WantToUseSafeFunctions", WantToUseSafeFunctions);
510 const SourceManager &SM, Preprocessor *pp, Preprocessor *ModuleExpanderPP) {
515 AST_MATCHER_P(Expr, hasDefinition, ast_matchers::internal::Matcher<Expr>,
517 const Expr *SimpleNode = &Node;
518 SimpleNode = SimpleNode->IgnoreParenImpCasts();
520 if (InnerMatcher.matches(*SimpleNode, Finder,
Builder))
523 auto DREHasInit = ignoringImpCasts(
524 declRefExpr(to(varDecl(hasInitializer(ignoringImpCasts(InnerMatcher))))));
526 if (DREHasInit.matches(*SimpleNode, Finder,
Builder))
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)))))))));
536 if (DREHasDefinition.matches(*SimpleNode, Finder,
Builder))
545 binaryOperator(hasOperatorName(
"+"),
546 hasEitherOperand(ignoringParenImpCasts(integerLiteral())));
549 binaryOperator(hasOperatorName(
"-"),
550 hasEitherOperand(ignoringParenImpCasts(integerLiteral())));
552 auto HasIncOp = anyOf(ignoringImpCasts(IncOp), hasDescendant(IncOp));
553 auto HasDecOp = anyOf(ignoringImpCasts(DecOp), hasDescendant(DecOp));
555 auto Container = ignoringImpCasts(cxxMemberCallExpr(hasDescendant(declRefExpr(
556 hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(recordDecl(
557 hasAnyName(
"::std::vector",
"::std::list",
"::std::deque"))))))))));
559 auto StringTy = type(hasUnqualifiedDesugaredType(recordType(
560 hasDeclaration(cxxRecordDecl(hasName(
"::std::basic_string"))))));
563 anyOf(hasType(StringTy), hasType(qualType(pointsTo(StringTy))));
565 auto CharTyArray = hasType(qualType(hasCanonicalType(
566 arrayType(hasElementType(isAnyCharacter())).bind(DestArrayTyName))));
568 auto CharTyPointer = hasType(
569 qualType(hasCanonicalType(pointerType(pointee(isAnyCharacter())))));
571 auto AnyOfCharTy = anyOf(CharTyArray, CharTyPointer);
579 callExpr(callee(functionDecl(hasAnyName(
"::strlen",
"::wcslen"))))
580 .bind(WrongLengthExprName);
585 allOf(on(expr(AnyOfStringTy).bind(
"Foo")),
586 has(memberExpr(member(hasAnyName(
"size",
"length"))))))
587 .bind(WrongLengthExprName);
590 auto SizeOfCharExpr = unaryExprOrTypeTraitExpr(has(expr(AnyOfCharTy)));
593 ignoringImpCasts(anyOf(Strlen, SizeOrLength, hasDescendant(Strlen),
594 hasDescendant(SizeOrLength)));
598 ignoringImpCasts(declRefExpr(to(varDecl(hasInitializer(WrongLength)))));
600 auto AnyOfCallOrDREWithoutInc = anyOf(DREWithoutInc, WrongLength);
603 auto CallExprReturnWithoutInc = ignoringImpCasts(callExpr(callee(functionDecl(
604 hasBody(has(returnStmt(hasReturnValue(AnyOfCallOrDREWithoutInc))))))));
607 auto DREHasReturnWithoutInc = ignoringImpCasts(
608 declRefExpr(to(varDecl(hasInitializer(CallExprReturnWithoutInc)))));
610 auto AnyOfWrongLengthInit =
611 anyOf(WrongLength, AnyOfCallOrDREWithoutInc, CallExprReturnWithoutInc,
612 DREHasReturnWithoutInc);
620 auto SizeExpr = anyOf(SizeOfCharExpr, integerLiteral(equals(1)));
622 auto MallocLengthExpr = allOf(
624 hasAnyName(
"::alloca",
"::calloc",
"malloc",
"realloc"))),
625 hasAnyArgument(allOf(unless(SizeExpr), expr().bind(DestMallocExprName))));
628 auto DestMalloc = anyOf(callExpr(MallocLengthExpr),
629 hasDescendant(callExpr(MallocLengthExpr)));
632 auto DestCXXNewExpr = ignoringImpCasts(
633 cxxNewExpr(hasArraySize(expr().bind(DestMallocExprName))));
635 auto AnyOfDestInit = anyOf(DestMalloc, DestCXXNewExpr);
638 auto DestArrayTyDecl = declRefExpr(
639 to(anyOf(varDecl(CharTyArray).bind(DestVarDeclName),
640 varDecl(hasInitializer(AnyOfDestInit)).bind(DestVarDeclName))));
643 auto DestUnknownDecl =
644 declRefExpr(allOf(to(varDecl(AnyOfCharTy).bind(DestVarDeclName)),
645 expr().bind(UnknownDestName)))
648 auto AnyOfDestDecl = ignoringImpCasts(
649 anyOf(allOf(hasDefinition(anyOf(AnyOfDestInit, DestArrayTyDecl,
650 hasDescendant(DestArrayTyDecl))),
651 expr().bind(DestExprName)),
652 anyOf(DestUnknownDecl, hasDescendant(DestUnknownDecl))));
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))))));
661 auto SrcDecl = declRefExpr(
662 allOf(to(decl().bind(SrcVarDeclName)),
663 anyOf(hasAncestor(cxxMemberCallExpr().bind(SrcExprName)),
664 expr().bind(SrcExprName))));
667 ignoringImpCasts(anyOf(stringLiteral().bind(SrcExprName),
668 hasDescendant(stringLiteral().bind(SrcExprName)),
669 SrcDecl, hasDescendant(SrcDecl)));
676 CallContext(StringRef
Name, Optional<unsigned> DestinationPos,
677 Optional<unsigned> SourcePos,
unsigned LengthPos,
679 :
Name(Name), DestinationPos(DestinationPos), SourcePos(SourcePos),
680 LengthPos(LengthPos), WithIncrease(WithIncrease){};
683 Optional<unsigned> DestinationPos;
684 Optional<unsigned> SourcePos;
689 auto MatchDestination = [=](CallContext CC) {
690 return hasArgument(*CC.DestinationPos,
692 unless(hasAncestor(compoundStmt(
693 hasDescendant(NullTerminatorExpr)))),
697 auto MatchSource = [=](CallContext CC) {
698 return hasArgument(*CC.SourcePos, AnyOfSrcDecl);
701 auto MatchGivenLength = [=](CallContext CC) {
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(
714 hasDefinition(anything())))),
715 AnyOfWrongLengthInit))),
716 expr().bind(LengthExprName)));
719 auto MatchCall = [=](CallContext CC) {
720 std::string CharHandlerFuncName =
"::" + CC.Name.str();
723 std::string WcharHandlerFuncName =
724 "::" + (CC.Name.startswith(
"mem") ?
"w" + CC.Name.str()
725 :
"wcs" + CC.Name.substr(3).str());
727 return allOf(callee(functionDecl(
728 hasAnyName(CharHandlerFuncName, WcharHandlerFuncName))),
729 MatchGivenLength(CC));
732 auto Match = [=](CallContext CC) {
733 if (CC.DestinationPos && CC.SourcePos)
734 return allOf(MatchCall(CC), MatchDestination(CC), MatchSource(CC));
736 if (CC.DestinationPos && !CC.SourcePos)
737 return allOf(MatchCall(CC), MatchDestination(CC),
738 hasArgument(*CC.DestinationPos, anything()));
740 if (!CC.DestinationPos && CC.SourcePos)
741 return allOf(MatchCall(CC), MatchSource(CC),
742 hasArgument(*CC.SourcePos, anything()));
744 llvm_unreachable(
"Unhandled match");
748 auto Memcpy = Match({
"memcpy", 0, 1, 2,
false});
751 auto Memcpy_s = Match({
"memcpy_s", 0, 2, 3,
false});
754 auto Memchr = Match({
"memchr", None, 0, 2,
false});
757 auto Memmove = Match({
"memmove", 0, 1, 2,
false});
760 auto Memmove_s = Match({
"memmove_s", 0, 2, 3,
false});
763 auto StrncmpRHS = Match({
"strncmp", None, 1, 2,
true});
764 auto StrncmpLHS = Match({
"strncmp", None, 0, 2,
true});
767 auto Strxfrm = Match({
"strxfrm", 0, 1, 2,
false});
770 auto Strerror_s = Match({
"strerror_s", 0, None, 1,
false});
772 auto AnyOfMatchers = anyOf(Memcpy, Memcpy_s, Memmove, Memmove_s, StrncmpRHS,
773 StrncmpLHS, Strxfrm, Strerror_s);
775 Finder->addMatcher(callExpr(AnyOfMatchers).bind(FunctionExprName),
this);
780 unless(hasAncestor(castExpr(unless(implicitCastExpr())))))
781 .bind(FunctionExprName),
784 castExpr(allOf(unless(implicitCastExpr()),
785 has(callExpr(Memchr).bind(FunctionExprName))))
791 const MatchFinder::MatchResult &Result) {
792 const auto *FunctionExpr = Result.Nodes.getNodeAs<CallExpr>(
FunctionExprName);
793 if (FunctionExpr->getBeginLoc().isMacroID())
796 if (WantToUseSafeFunctions &&
PP->isMacroDefined(
"__STDC_LIB_EXT1__")) {
797 Optional<bool> AreSafeFunctionsWanted;
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();
813 if (AreSafeFunctionsWanted.hasValue())
814 UseSafeFunctions = AreSafeFunctionsWanted.getValue();
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);
828 void NotNullTerminatedResultCheck::memoryHandlerFunctionFix(
829 StringRef
Name,
const MatchFinder::MatchResult &Result) {
833 if (Name.endswith(
"chr")) {
834 memchrFix(Name, Result);
838 if ((Name.contains(
"cpy") || Name.contains(
"move")) &&
843 diag(Result.Nodes.getNodeAs<CallExpr>(FunctionExprName)->getBeginLoc(),
844 "the result from calling '%0' is not null-terminated")
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")) {
859 void NotNullTerminatedResultCheck::memcpyFix(
860 StringRef Name,
const MatchFinder::MatchResult &Result,
861 DiagnosticBuilder &Diag) {
868 bool IsSafe = UseSafeFunctions && IsOverflows &&
isKnownDest(Result) &&
871 bool IsDestLengthNotRequired =
877 if (IsSafe && !IsDestLengthNotRequired)
883 if (!IsCopy && !IsSafe)
887 void NotNullTerminatedResultCheck::memcpy_sFix(
888 StringRef Name,
const MatchFinder::MatchResult &Result,
889 DiagnosticBuilder &Diag) {
897 bool IsSafe = IsOverflows;
901 if (!IsSafe || (IsSafe && RemoveDestLength))
909 if (!IsCopy && !IsSafe)
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)
920 auto Diag =
diag(FunctionExpr->getArg(2)->IgnoreParenCasts()->getBeginLoc(),
921 "the length is too short to include the null terminator");
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;
930 StringRef NewFuncName = (Name[0] !=
'w') ?
"strchr" :
"wcschr";
935 void NotNullTerminatedResultCheck::memmoveFix(
936 StringRef Name,
const MatchFinder::MatchResult &Result,
937 DiagnosticBuilder &Diag) {
941 renameFunc((Name[0] !=
'w') ?
"memmove_s" :
"wmemmove_s", Result, Diag);
948 void NotNullTerminatedResultCheck::strerror_sFix(
949 const MatchFinder::MatchResult &Result) {
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");
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;
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();
972 LengthArgStr == FirstExprStr || LengthArgStr == SecondExprStr;
975 getLength(Result.Nodes.getNodeAs<Expr>(SrcExprName), Result);
977 if (SrcLength != 0 && GivenLength != 0)
978 IsLengthTooLong = GivenLength > SrcLength;
984 auto Diag =
diag(FunctionExpr->getArg(2)->IgnoreParenCasts()->getBeginLoc(),
985 "comparison length is too long and might lead to a " 991 void NotNullTerminatedResultCheck::xfrmFix(
992 StringRef Name,
const MatchFinder::MatchResult &Result) {
997 diag(Result.Nodes.getNodeAs<CallExpr>(FunctionExprName)->getBeginLoc(),
998 "the result from calling '%0' is not null-terminated")
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
static unsigned getLength(const Expr *E, const MatchFinder::MatchResult &Result)
const FunctionDecl * Decl
static bool isDestAndSrcEquals(const MatchFinder::MatchResult &Result)
static void renameFunc(StringRef NewFuncName, const MatchFinder::MatchResult &Result, DiagnosticBuilder &Diag)
static const Expr * getDestCapacityExpr(const MatchFinder::MatchResult &Result)
constexpr llvm::StringLiteral WrongLengthExprName
static bool isCorrectGivenLength(const MatchFinder::MatchResult &Result)
static bool isInjectUL(const MatchFinder::MatchResult &Result)
static bool isGivenLengthEqualToSrcLength(const MatchFinder::MatchResult &Result)
constexpr llvm::StringLiteral SrcVarDeclName
constexpr llvm::StringLiteral LengthExprName
static void removeArg(int ArgPos, const MatchFinder::MatchResult &Result, DiagnosticBuilder &Diag)
static void lengthArgPosHandle(unsigned ArgPos, LengthHandleKind LengthHandle, const MatchFinder::MatchResult &Result, DiagnosticBuilder &Diag)
Base class for all clang-tidy checks.
static bool isKnownDest(const MatchFinder::MatchResult &Result)
static StringRef exprToStr(const Expr *E, const MatchFinder::MatchResult &Result)
const LangOptions & getLangOpts() const
Returns the language options from the context.
static void lengthArgHandle(LengthHandleKind LengthHandle, const MatchFinder::MatchResult &Result, DiagnosticBuilder &Diag)
static int getGivenLength(const MatchFinder::MatchResult &Result)
static bool isDestBasedOnGivenLength(const MatchFinder::MatchResult &Result)
static void renameMemcpy(StringRef Name, bool IsCopy, bool IsSafe, const MatchFinder::MatchResult &Result, DiagnosticBuilder &Diag)
static void lengthExprHandle(const Expr *LengthExpr, LengthHandleKind LengthHandle, const MatchFinder::MatchResult &Result, DiagnosticBuilder &Diag)
constexpr llvm::StringLiteral CastExprName
constexpr llvm::StringLiteral SrcExprName
constexpr llvm::StringLiteral UnknownDestName
static bool isStringDataAndLength(const MatchFinder::MatchResult &Result)
static bool isDestExprFix(const MatchFinder::MatchResult &Result, DiagnosticBuilder &Diag)
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
static bool isDestCapacityFix(const MatchFinder::MatchResult &Result, DiagnosticBuilder &Diag)
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
constexpr llvm::StringLiteral DestArrayTyName
static constexpr llvm::StringLiteral Name
std::map< std::string, std::string > OptionMap
llvm::Optional< Range > getTokenRange(const SourceManager &SM, const LangOptions &LangOpts, SourceLocation TokLoc)
Returns the taken range at TokLoc.
CodeCompletionBuilder Builder
constexpr llvm::StringLiteral FunctionExprName
constexpr llvm::StringLiteral UnknownLengthName
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
static SourceLocation exprLocEnd(const Expr *E, const MatchFinder::MatchResult &Result)
static const CallExpr * getStrlenExpr(const MatchFinder::MatchResult &Result)
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
static bool isDestCapacityOverflows(const MatchFinder::MatchResult &Result)
constexpr llvm::StringLiteral DestVarDeclName
static void insertNullTerminatorExpr(StringRef Name, const MatchFinder::MatchResult &Result, DiagnosticBuilder &Diag)
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
static bool isFixedGivenLengthAndUnknownSrc(const MatchFinder::MatchResult &Result)
constexpr llvm::StringLiteral DestExprName
constexpr llvm::StringLiteral DestMallocExprName
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
static int getDestCapacity(const MatchFinder::MatchResult &Result)
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) override
Override this to register PPCallbacks in the preprocessor.
AST_MATCHER_P(CXXMethodDecl, hasCanonicalDecl, ast_matchers::internal::Matcher< CXXMethodDecl >, InnerMatcher)
static void insertDestCapacityArg(bool IsOverflows, StringRef Name, const MatchFinder::MatchResult &Result, DiagnosticBuilder &Diag)