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