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 (dyn_cast<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 Memcpy_s = 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 Memmove_s = 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 Strerror_s = Match({"strerror_s", 0, None, 1, false});
773
774 auto AnyOfMatchers = anyOf(Memcpy, Memcpy_s, Memmove, Memmove_s, StrncmpRHS,
775 StrncmpLHS, Strxfrm, Strerror_s);
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