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