1 //===--- NotNullTerminatedResultCheck.cpp - clang-tidy ----------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
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"
18 using namespace clang::ast_matchers
;
20 namespace clang::tidy::bugprone
{
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";
35 enum class LengthHandleKind
{ Increase
, Decrease
};
38 static Preprocessor
*PP
;
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
))
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();
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
) {
67 Expr::EvalResult Length
;
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();
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
);
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"))
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
))
122 getLength(Result
.Nodes
.getNodeAs
<Expr
>(WrongLengthExprName
), Result
))
126 getLength(Result
.Nodes
.getNodeAs
<Expr
>(LengthExprName
), Result
))
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
))
138 // Returns a string representation of \p E.
139 static StringRef
exprToStr(const Expr
*E
,
140 const MatchFinder::MatchResult
&Result
) {
144 return Lexer::getSourceText(
145 CharSourceRange::getTokenRange(E
->getSourceRange()),
146 *Result
.SourceManager
, Result
.Context
->getLangOpts(), nullptr);
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());
156 //===----------------------------------------------------------------------===//
157 // Rewrite decision helper functions.
158 //===----------------------------------------------------------------------===//
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();
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
);
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();
180 return !DestCapacityExprStr
.empty() && !LengthExprStr
.empty() &&
181 DestCapacityExprStr
.contains(LengthExprStr
);
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();
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
);
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");
220 isGivenLengthEqualToSrcLength(const MatchFinder::MatchResult
&Result
) {
221 if (Result
.Nodes
.getNodeAs
<Expr
>(UnknownLengthName
))
224 if (isStringDataAndLength(Result
))
227 int GivenLength
= getGivenLength(Result
);
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 (isa
<BinaryOperator
>(LengthExpr
->IgnoreParenImpCasts()))
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
;
247 static bool isCorrectGivenLength(const MatchFinder::MatchResult
&Result
) {
248 if (Result
.Nodes
.getNodeAs
<Expr
>(UnknownLengthName
))
251 return !isGivenLengthEqualToSrcLength(Result
);
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
))
261 const Expr
*DestCapacityExpr
= getDestCapacityExpr(Result
);
262 int DestCapacity
= getLength(DestCapacityExpr
, Result
);
263 int GivenLength
= getGivenLength(Result
);
265 if (GivenLength
!= 0 && DestCapacity
!= 0)
266 return isGivenLengthEqualToSrcLength(Result
) && DestCapacity
== GivenLength
;
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"))
278 isFixedGivenLengthAndUnknownSrc(const MatchFinder::MatchResult
&Result
) {
279 if (Result
.Nodes
.getNodeAs
<IntegerLiteral
>(WrongLengthExprName
))
280 return !getLength(Result
.Nodes
.getNodeAs
<Expr
>(SrcExprName
), Result
);
285 //===----------------------------------------------------------------------===//
286 // Code injection functions.
287 //===----------------------------------------------------------------------===//
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();
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;
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)
315 const auto NewLengthFix
= FixItHint::CreateReplacement(
316 LengthIL
->getSourceRange(),
317 (Twine(NewLength
) + (isInjectUL(Result
) ? "UL" : "")).str());
318 Diag
<< NewLengthFix
;
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();
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()});
348 // Try to inject the '+ 1'/'- 1' string.
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";
358 if (isInjectUL(Result
))
361 Diag
<< FixItHint::CreateInsertion(exprLocEnd(LengthExpr
, Result
), Injection
);
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
);
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
);
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
);
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 *)");
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
);
401 if (const Expr
*CapacityExpr
= getDestCapacityExpr(Result
))
402 lengthExprHandle(CapacityExpr
, LengthHandleKind::Increase
, Result
, Diag
);
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: ~~~~~~~~~~~
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
;
422 static void renameFunc(StringRef NewFuncName
,
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
);
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
);
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
;
453 if (int DestLength
= getDestCapacity(Result
)) {
454 NewSecondArg
= Twine(IsOverflows
? DestLength
+ 1 : DestLength
).str();
457 (Twine(exprToStr(getDestCapacityExpr(Result
), Result
)) +
458 (IsOverflows
? (!isInjectUL(Result
) ? " + 1" : " + 1UL") : ""))
462 NewSecondArg
+= ", ";
463 const auto InsertNewArgFix
= FixItHint::CreateInsertion(
464 FunctionExpr
->getArg(1)->getBeginLoc(), NewSecondArg
);
465 Diag
<< InsertNewArgFix
;
468 static void insertNullTerminatorExpr(StringRef Name
,
469 const MatchFinder::MatchResult
&Result
,
470 DiagnosticBuilder
&Diag
) {
471 const auto *FunctionExpr
= Result
.Nodes
.getNodeAs
<CallExpr
>(FunctionExprName
);
472 int FuncLocStartColumn
= Result
.SourceManager
->getPresumedColumnNumber(
473 FunctionExpr
->getBeginLoc());
474 SourceRange
SpaceRange(
475 FunctionExpr
->getBeginLoc().getLocWithOffset(-FuncLocStartColumn
+ 1),
476 FunctionExpr
->getBeginLoc());
477 StringRef SpaceBeforeStmtStr
= Lexer::getSourceText(
478 CharSourceRange::getCharRange(SpaceRange
), *Result
.SourceManager
,
479 Result
.Context
->getLangOpts(), nullptr);
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') ? R
"('\0';)" : R
"(L'\0';)"))
489 const auto AddNullTerminatorExprFix
= FixItHint::CreateInsertion(
490 exprLocEnd(FunctionExpr
, Result
).getLocWithOffset(1),
491 NewAddNullTermExprStr
);
492 Diag
<< AddNullTerminatorExprFix
;
495 //===----------------------------------------------------------------------===//
496 // Checker logic with the matchers.
497 //===----------------------------------------------------------------------===//
499 NotNullTerminatedResultCheck::NotNullTerminatedResultCheck(
500 StringRef Name
, ClangTidyContext
*Context
)
501 : ClangTidyCheck(Name
, Context
),
502 WantToUseSafeFunctions(Options
.get("WantToUseSafeFunctions", true)) {}
504 void NotNullTerminatedResultCheck::storeOptions(
505 ClangTidyOptions::OptionMap
&Opts
) {
506 Options
.store(Opts
, "WantToUseSafeFunctions", WantToUseSafeFunctions
);
509 void NotNullTerminatedResultCheck::registerPPCallbacks(
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 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
))
543 void NotNullTerminatedResultCheck::registerMatchers(MatchFinder
*Finder
) {
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
);
573 //===--------------------------------------------------------------------===//
574 // The following six cases match problematic length expressions.
575 //===--------------------------------------------------------------------===//
577 // - Example: char src[] = "foo"; strlen(src);
579 callExpr(callee(functionDecl(hasAnyName("::strlen", "::wcslen"))))
580 .bind(WrongLengthExprName
);
582 // - Example: std::string str = "foo"; str.size();
584 cxxMemberCallExpr(on(expr(AnyOfStringTy
).bind("Foo")),
585 has(memberExpr(member(hasAnyName("size", "length")))))
586 .bind(WrongLengthExprName
);
588 // - Example: char src[] = "foo"; sizeof(src);
589 auto SizeOfCharExpr
= unaryExprOrTypeTraitExpr(has(expr(AnyOfCharTy
)));
592 ignoringImpCasts(anyOf(Strlen
, SizeOrLength
, hasDescendant(Strlen
),
593 hasDescendant(SizeOrLength
)));
595 // - Example: length = strlen(src);
597 ignoringImpCasts(declRefExpr(to(varDecl(hasInitializer(WrongLength
)))));
599 auto AnyOfCallOrDREWithoutInc
= anyOf(DREWithoutInc
, WrongLength
);
601 // - Example: int getLength(const char *str) { return strlen(str); }
602 auto CallExprReturnWithoutInc
= ignoringImpCasts(callExpr(callee(functionDecl(
603 hasBody(has(returnStmt(hasReturnValue(AnyOfCallOrDREWithoutInc
))))))));
605 // - Example: int length = getLength(src);
606 auto DREHasReturnWithoutInc
= ignoringImpCasts(
607 declRefExpr(to(varDecl(hasInitializer(CallExprReturnWithoutInc
)))));
609 auto AnyOfWrongLengthInit
=
610 anyOf(WrongLength
, AnyOfCallOrDREWithoutInc
, CallExprReturnWithoutInc
,
611 DREHasReturnWithoutInc
);
613 //===--------------------------------------------------------------------===//
614 // The following five cases match the 'destination' array length's
615 // expression which is used in 'memcpy()' and 'memmove()' matchers.
616 //===--------------------------------------------------------------------===//
618 // Note: Sometimes the size of char is explicitly written out.
619 auto SizeExpr
= anyOf(SizeOfCharExpr
, integerLiteral(equals(1)));
621 auto MallocLengthExpr
= allOf(
623 hasAnyName("::alloca", "::calloc", "malloc", "realloc"))),
624 hasAnyArgument(allOf(unless(SizeExpr
), expr().bind(DestMallocExprName
))));
626 // - Example: (char *)malloc(length);
627 auto DestMalloc
= anyOf(callExpr(MallocLengthExpr
),
628 hasDescendant(callExpr(MallocLengthExpr
)));
630 // - Example: new char[length];
631 auto DestCXXNewExpr
= ignoringImpCasts(
632 cxxNewExpr(hasArraySize(expr().bind(DestMallocExprName
))));
634 auto AnyOfDestInit
= anyOf(DestMalloc
, DestCXXNewExpr
);
636 // - Example: char dest[13]; or char dest[length];
637 auto DestArrayTyDecl
= declRefExpr(
638 to(anyOf(varDecl(CharTyArray
).bind(DestVarDeclName
),
639 varDecl(hasInitializer(AnyOfDestInit
)).bind(DestVarDeclName
))));
641 // - Example: foo[bar[baz]].qux; (or just ParmVarDecl)
642 auto DestUnknownDecl
=
643 declRefExpr(to(varDecl(AnyOfCharTy
).bind(DestVarDeclName
)),
644 expr().bind(UnknownDestName
))
647 auto AnyOfDestDecl
= ignoringImpCasts(
648 anyOf(allOf(hasDefinition(anyOf(AnyOfDestInit
, DestArrayTyDecl
,
649 hasDescendant(DestArrayTyDecl
))),
650 expr().bind(DestExprName
)),
651 anyOf(DestUnknownDecl
, hasDescendant(DestUnknownDecl
))));
653 auto NullTerminatorExpr
= binaryOperator(
654 hasLHS(anyOf(hasDescendant(declRefExpr(to(varDecl(
655 equalsBoundNode(std::string(DestVarDeclName
)))))),
656 hasDescendant(declRefExpr(
657 equalsBoundNode(std::string(DestExprName
)))))),
658 hasRHS(ignoringImpCasts(
659 anyOf(characterLiteral(equals(0U)), integerLiteral(equals(0))))));
662 declRefExpr(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
)));
671 //===--------------------------------------------------------------------===//
672 // Match the problematic function calls.
673 //===--------------------------------------------------------------------===//
676 CallContext(StringRef Name
, std::optional
<unsigned> DestinationPos
,
677 std::optional
<unsigned> SourcePos
, unsigned LengthPos
,
679 : Name(Name
), DestinationPos(DestinationPos
), SourcePos(SourcePos
),
680 LengthPos(LengthPos
), WithIncrease(WithIncrease
){};
683 std::optional
<unsigned> DestinationPos
;
684 std::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();
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());
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");
747 // void *memcpy(void *dest, const void *src, size_t count)
748 auto Memcpy
= Match({"memcpy", 0, 1, 2, false});
750 // errno_t memcpy_s(void *dest, size_t ds, const void *src, size_t count)
751 auto MemcpyS
= Match({"memcpy_s", 0, 2, 3, false});
753 // void *memchr(const void *src, int c, size_t count)
754 auto Memchr
= Match({"memchr", std::nullopt
, 0, 2, false});
756 // void *memmove(void *dest, const void *src, size_t count)
757 auto Memmove
= Match({"memmove", 0, 1, 2, false});
759 // errno_t memmove_s(void *dest, size_t ds, const void *src, size_t count)
760 auto MemmoveS
= Match({"memmove_s", 0, 2, 3, false});
762 // int strncmp(const char *str1, const char *str2, size_t count);
763 auto StrncmpRHS
= Match({"strncmp", std::nullopt
, 1, 2, true});
764 auto StrncmpLHS
= Match({"strncmp", std::nullopt
, 0, 2, true});
766 // size_t strxfrm(char *dest, const char *src, size_t count);
767 auto Strxfrm
= Match({"strxfrm", 0, 1, 2, false});
769 // errno_t strerror_s(char *buffer, size_t bufferSize, int errnum);
770 auto StrerrorS
= Match({"strerror_s", 0, std::nullopt
, 1, false});
772 auto AnyOfMatchers
= anyOf(Memcpy
, MemcpyS
, Memmove
, MemmoveS
, StrncmpRHS
,
773 StrncmpLHS
, Strxfrm
, StrerrorS
);
775 Finder
->addMatcher(callExpr(AnyOfMatchers
).bind(FunctionExprName
), this);
777 // Need to remove the CastExpr from 'memchr()' as 'strchr()' returns 'char *'.
780 unless(hasAncestor(castExpr(unless(implicitCastExpr())))))
781 .bind(FunctionExprName
),
784 castExpr(allOf(unless(implicitCastExpr()),
785 has(callExpr(Memchr
).bind(FunctionExprName
))))
790 void NotNullTerminatedResultCheck::check(
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 std::optional
<bool> AreSafeFunctionsWanted
;
799 Preprocessor::macro_iterator It
= PP
->macro_begin();
800 while (It
!= PP
->macro_end() && !AreSafeFunctionsWanted
) {
801 if (It
->first
->getName() == "__STDC_WANT_LIB_EXT1__") {
802 const auto *MI
= PP
->getMacroInfo(It
->first
);
803 // PP->getMacroInfo() returns nullptr if macro has no definition.
805 const auto &T
= MI
->tokens().back();
806 if (T
.isLiteral() && T
.getLiteralData()) {
807 StringRef ValueStr
= StringRef(T
.getLiteralData(), T
.getLength());
808 llvm::APInt IntValue
;
809 ValueStr
.getAsInteger(10, IntValue
);
810 AreSafeFunctionsWanted
= IntValue
.getZExtValue();
818 if (AreSafeFunctionsWanted
)
819 UseSafeFunctions
= *AreSafeFunctionsWanted
;
822 StringRef Name
= FunctionExpr
->getDirectCallee()->getName();
823 if (Name
.startswith("mem") || Name
.startswith("wmem"))
824 memoryHandlerFunctionFix(Name
, Result
);
825 else if (Name
== "strerror_s")
826 strerror_sFix(Result
);
827 else if (Name
.endswith("ncmp"))
828 ncmpFix(Name
, Result
);
829 else if (Name
.endswith("xfrm"))
830 xfrmFix(Name
, Result
);
833 void NotNullTerminatedResultCheck::memoryHandlerFunctionFix(
834 StringRef Name
, const MatchFinder::MatchResult
&Result
) {
835 if (isCorrectGivenLength(Result
))
838 if (Name
.endswith("chr")) {
839 memchrFix(Name
, Result
);
843 if ((Name
.contains("cpy") || Name
.contains("move")) &&
844 (isDestAndSrcEquals(Result
) || isFixedGivenLengthAndUnknownSrc(Result
)))
848 diag(Result
.Nodes
.getNodeAs
<CallExpr
>(FunctionExprName
)->getBeginLoc(),
849 "the result from calling '%0' is not null-terminated")
852 if (Name
.endswith("cpy")) {
853 memcpyFix(Name
, Result
, Diag
);
854 } else if (Name
.endswith("cpy_s")) {
855 memcpy_sFix(Name
, Result
, Diag
);
856 } else if (Name
.endswith("move")) {
857 memmoveFix(Name
, Result
, Diag
);
858 } else if (Name
.endswith("move_s")) {
859 isDestCapacityFix(Result
, Diag
);
860 lengthArgHandle(LengthHandleKind::Increase
, Result
, Diag
);
864 void NotNullTerminatedResultCheck::memcpyFix(
865 StringRef Name
, const MatchFinder::MatchResult
&Result
,
866 DiagnosticBuilder
&Diag
) {
867 bool IsOverflows
= isDestCapacityFix(Result
, Diag
);
868 bool IsDestFixed
= isDestExprFix(Result
, Diag
);
871 isGivenLengthEqualToSrcLength(Result
) || isDestBasedOnGivenLength(Result
);
873 bool IsSafe
= UseSafeFunctions
&& IsOverflows
&& isKnownDest(Result
) &&
874 !isDestBasedOnGivenLength(Result
);
876 bool IsDestLengthNotRequired
=
877 IsSafe
&& getLangOpts().CPlusPlus
&&
878 Result
.Nodes
.getNodeAs
<ArrayType
>(DestArrayTyName
) && !IsDestFixed
;
880 renameMemcpy(Name
, IsCopy
, IsSafe
, Result
, Diag
);
882 if (IsSafe
&& !IsDestLengthNotRequired
)
883 insertDestCapacityArg(IsOverflows
, Name
, Result
, Diag
);
886 removeArg(2, Result
, Diag
);
888 if (!IsCopy
&& !IsSafe
)
889 insertNullTerminatorExpr(Name
, Result
, Diag
);
892 void NotNullTerminatedResultCheck::memcpy_sFix(
893 StringRef Name
, const MatchFinder::MatchResult
&Result
,
894 DiagnosticBuilder
&Diag
) {
895 bool IsOverflows
= isDestCapacityFix(Result
, Diag
);
896 bool IsDestFixed
= isDestExprFix(Result
, Diag
);
898 bool RemoveDestLength
= getLangOpts().CPlusPlus
&&
899 Result
.Nodes
.getNodeAs
<ArrayType
>(DestArrayTyName
) &&
901 bool IsCopy
= isGivenLengthEqualToSrcLength(Result
);
902 bool IsSafe
= IsOverflows
;
904 renameMemcpy(Name
, IsCopy
, IsSafe
, Result
, Diag
);
906 if (!IsSafe
|| (IsSafe
&& RemoveDestLength
))
907 removeArg(1, Result
, Diag
);
908 else if (IsOverflows
&& isKnownDest(Result
))
909 lengthArgPosHandle(1, LengthHandleKind::Increase
, Result
, Diag
);
912 removeArg(3, Result
, Diag
);
914 if (!IsCopy
&& !IsSafe
)
915 insertNullTerminatorExpr(Name
, Result
, Diag
);
918 void NotNullTerminatedResultCheck::memchrFix(
919 StringRef Name
, const MatchFinder::MatchResult
&Result
) {
920 const auto *FunctionExpr
= Result
.Nodes
.getNodeAs
<CallExpr
>(FunctionExprName
);
921 if (const auto *GivenCL
= dyn_cast
<CharacterLiteral
>(FunctionExpr
->getArg(1)))
922 if (GivenCL
->getValue() != 0)
925 auto Diag
= diag(FunctionExpr
->getArg(2)->IgnoreParenCasts()->getBeginLoc(),
926 "the length is too short to include the null terminator");
928 if (const auto *CastExpr
= Result
.Nodes
.getNodeAs
<Expr
>(CastExprName
)) {
929 const auto CastRemoveFix
= FixItHint::CreateRemoval(
930 SourceRange(CastExpr
->getBeginLoc(),
931 FunctionExpr
->getBeginLoc().getLocWithOffset(-1)));
932 Diag
<< CastRemoveFix
;
935 StringRef NewFuncName
= (Name
[0] != 'w') ? "strchr" : "wcschr";
936 renameFunc(NewFuncName
, Result
, Diag
);
937 removeArg(2, Result
, Diag
);
940 void NotNullTerminatedResultCheck::memmoveFix(
941 StringRef Name
, const MatchFinder::MatchResult
&Result
,
942 DiagnosticBuilder
&Diag
) const {
943 bool IsOverflows
= isDestCapacityFix(Result
, Diag
);
945 if (UseSafeFunctions
&& isKnownDest(Result
)) {
946 renameFunc((Name
[0] != 'w') ? "memmove_s" : "wmemmove_s", Result
, Diag
);
947 insertDestCapacityArg(IsOverflows
, Name
, Result
, Diag
);
950 lengthArgHandle(LengthHandleKind::Increase
, Result
, Diag
);
953 void NotNullTerminatedResultCheck::strerror_sFix(
954 const MatchFinder::MatchResult
&Result
) {
956 diag(Result
.Nodes
.getNodeAs
<CallExpr
>(FunctionExprName
)->getBeginLoc(),
957 "the result from calling 'strerror_s' is not null-terminated and "
958 "missing the last character of the error message");
960 isDestCapacityFix(Result
, Diag
);
961 lengthArgHandle(LengthHandleKind::Increase
, Result
, Diag
);
964 void NotNullTerminatedResultCheck::ncmpFix(
965 StringRef Name
, const MatchFinder::MatchResult
&Result
) {
966 const auto *FunctionExpr
= Result
.Nodes
.getNodeAs
<CallExpr
>(FunctionExprName
);
967 const Expr
*FirstArgExpr
= FunctionExpr
->getArg(0)->IgnoreImpCasts();
968 const Expr
*SecondArgExpr
= FunctionExpr
->getArg(1)->IgnoreImpCasts();
969 bool IsLengthTooLong
= false;
971 if (const CallExpr
*StrlenExpr
= getStrlenExpr(Result
)) {
972 const Expr
*LengthExprArg
= StrlenExpr
->getArg(0);
973 StringRef FirstExprStr
= exprToStr(FirstArgExpr
, Result
).trim();
974 StringRef SecondExprStr
= exprToStr(SecondArgExpr
, Result
).trim();
975 StringRef LengthArgStr
= exprToStr(LengthExprArg
, Result
).trim();
977 LengthArgStr
== FirstExprStr
|| LengthArgStr
== SecondExprStr
;
980 getLength(Result
.Nodes
.getNodeAs
<Expr
>(SrcExprName
), Result
);
981 int GivenLength
= getGivenLength(Result
);
982 if (SrcLength
!= 0 && GivenLength
!= 0)
983 IsLengthTooLong
= GivenLength
> SrcLength
;
986 if (!IsLengthTooLong
&& !isStringDataAndLength(Result
))
989 auto Diag
= diag(FunctionExpr
->getArg(2)->IgnoreParenCasts()->getBeginLoc(),
990 "comparison length is too long and might lead to a "
993 lengthArgHandle(LengthHandleKind::Decrease
, Result
, Diag
);
996 void NotNullTerminatedResultCheck::xfrmFix(
997 StringRef Name
, const MatchFinder::MatchResult
&Result
) {
998 if (!isDestCapacityOverflows(Result
))
1002 diag(Result
.Nodes
.getNodeAs
<CallExpr
>(FunctionExprName
)->getBeginLoc(),
1003 "the result from calling '%0' is not null-terminated")
1006 isDestCapacityFix(Result
, Diag
);
1007 lengthArgHandle(LengthHandleKind::Increase
, Result
, Diag
);
1010 } // namespace clang::tidy::bugprone