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.
getDestCapacityExpr(const MatchFinder::MatchResult & Result)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.
getLength(const Expr * E,const MatchFinder::MatchResult & Result)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.
getDestCapacity(const MatchFinder::MatchResult & Result)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.
getStrlenExpr(const MatchFinder::MatchResult & 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"))
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.
getGivenLength(const MatchFinder::MatchResult & Result)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.
exprToStr(const Expr * E,const MatchFinder::MatchResult & Result)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.
exprLocEnd(const Expr * E,const MatchFinder::MatchResult & Result)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.
isInjectUL(const MatchFinder::MatchResult & Result)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.
isKnownDest(const MatchFinder::MatchResult & Result)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)'
isDestBasedOnGivenLength(const MatchFinder::MatchResult & Result)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.
isDestAndSrcEquals(const MatchFinder::MatchResult & Result)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())'.
isStringDataAndLength(const MatchFinder::MatchResult & Result)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
isGivenLengthEqualToSrcLength(const MatchFinder::MatchResult & Result)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 
isCorrectGivenLength(const MatchFinder::MatchResult & Result)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.
isDestCapacityOverflows(const MatchFinder::MatchResult & Result)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
isFixedGivenLengthAndUnknownSrc(const MatchFinder::MatchResult & Result)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.
lengthExprHandle(const Expr * LengthExpr,LengthHandleKind LengthHandle,const MatchFinder::MatchResult & Result,DiagnosticBuilder & Diag)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 
lengthArgHandle(LengthHandleKind LengthHandle,const MatchFinder::MatchResult & Result,DiagnosticBuilder & Diag)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 
lengthArgPosHandle(unsigned ArgPos,LengthHandleKind LengthHandle,const MatchFinder::MatchResult & Result,DiagnosticBuilder & 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);
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.
isDestExprFix(const MatchFinder::MatchResult & Result,DiagnosticBuilder & Diag)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.
isDestCapacityFix(const MatchFinder::MatchResult & Result,DiagnosticBuilder & Diag)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 
removeArg(int ArgPos,const MatchFinder::MatchResult & Result,DiagnosticBuilder & 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:             ~~~~~~~~~~~
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 
renameFunc(StringRef NewFuncName,const MatchFinder::MatchResult & Result,DiagnosticBuilder & Diag)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 
renameMemcpy(StringRef Name,bool IsCopy,bool IsSafe,const MatchFinder::MatchResult & Result,DiagnosticBuilder & Diag)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 
insertDestCapacityArg(bool IsOverflows,StringRef Name,const MatchFinder::MatchResult & Result,DiagnosticBuilder & 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;
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 
insertNullTerminatorExpr(StringRef Name,const MatchFinder::MatchResult & Result,DiagnosticBuilder & Diag)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 
NotNullTerminatedResultCheck(StringRef Name,ClangTidyContext * Context)499 NotNullTerminatedResultCheck::NotNullTerminatedResultCheck(
500     StringRef Name, ClangTidyContext *Context)
501     : ClangTidyCheck(Name, Context),
502       WantToUseSafeFunctions(Options.get("WantToUseSafeFunctions", 1)) {}
503 
storeOptions(ClangTidyOptions::OptionMap & Opts)504 void NotNullTerminatedResultCheck::storeOptions(
505     ClangTidyOptions::OptionMap &Opts) {
506   Options.store(Opts, "WantToUseSafeFunctions", WantToUseSafeFunctions);
507 }
508 
registerPPCallbacks(const SourceManager & SM,Preprocessor * pp,Preprocessor * ModuleExpanderPP)509 void NotNullTerminatedResultCheck::registerPPCallbacks(
510     const SourceManager &SM, Preprocessor *pp, Preprocessor *ModuleExpanderPP) {
511   PP = pp;
512 }
513 
514 namespace {
AST_MATCHER_P(Expr,hasDefinition,ast_matchers::internal::Matcher<Expr>,InnerMatcher)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 
registerMatchers(MatchFinder * Finder)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 
check(const MatchFinder::MatchResult & Result)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 
memoryHandlerFunctionFix(StringRef Name,const MatchFinder::MatchResult & Result)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 
memcpyFix(StringRef Name,const MatchFinder::MatchResult & Result,DiagnosticBuilder & Diag)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 
memcpy_sFix(StringRef Name,const MatchFinder::MatchResult & Result,DiagnosticBuilder & Diag)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 
memchrFix(StringRef Name,const MatchFinder::MatchResult & Result)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 
memmoveFix(StringRef Name,const MatchFinder::MatchResult & Result,DiagnosticBuilder & Diag)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 
strerror_sFix(const MatchFinder::MatchResult & Result)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 
ncmpFix(StringRef Name,const MatchFinder::MatchResult & Result)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 
xfrmFix(StringRef Name,const MatchFinder::MatchResult & Result)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