SuspiciousReallocUsageCheck.cpp 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. //===--- SuspiciousReallocUsageCheck.cpp - clang-tidy----------------------===//
  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. #include "SuspiciousReallocUsageCheck.h"
  9. #include "../utils/Aliasing.h"
  10. #include "clang/AST/ASTContext.h"
  11. #include "clang/AST/DeclVisitor.h"
  12. #include "clang/AST/StmtVisitor.h"
  13. #include "clang/ASTMatchers/ASTMatchFinder.h"
  14. #include "clang/ASTMatchers/ASTMatchers.h"
  15. #include "clang/Lex/Lexer.h"
  16. using namespace clang::ast_matchers;
  17. using namespace clang;
  18. namespace {
  19. /// Check if two different expression nodes denote the same
  20. /// "pointer expression". The "pointer expression" can consist of member
  21. /// expressions and declaration references only (like \c a->b->c), otherwise the
  22. /// check is always false.
  23. class IsSamePtrExpr : public StmtVisitor<IsSamePtrExpr, bool> {
  24. /// The other expression to compare against.
  25. /// This variable is used to pass the data from a \c check function to any of
  26. /// the visit functions. Every visit function starts by converting \c OtherE
  27. /// to the current type and store it locally, and do not use \c OtherE later.
  28. const Expr *OtherE = nullptr;
  29. public:
  30. bool VisitDeclRefExpr(const DeclRefExpr *E1) {
  31. const auto *E2 = dyn_cast<DeclRefExpr>(OtherE);
  32. if (!E2)
  33. return false;
  34. const Decl *D1 = E1->getDecl()->getCanonicalDecl();
  35. return isa<VarDecl, FieldDecl>(D1) &&
  36. D1 == E2->getDecl()->getCanonicalDecl();
  37. }
  38. bool VisitMemberExpr(const MemberExpr *E1) {
  39. const auto *E2 = dyn_cast<MemberExpr>(OtherE);
  40. if (!E2)
  41. return false;
  42. if (!check(E1->getBase(), E2->getBase()))
  43. return false;
  44. DeclAccessPair FD = E1->getFoundDecl();
  45. return isa<FieldDecl>(FD.getDecl()) && FD == E2->getFoundDecl();
  46. }
  47. bool check(const Expr *E1, const Expr *E2) {
  48. E1 = E1->IgnoreParenCasts();
  49. E2 = E2->IgnoreParenCasts();
  50. OtherE = E2;
  51. return Visit(const_cast<Expr *>(E1));
  52. }
  53. };
  54. /// Check if there is an assignment or initialization that references a variable
  55. /// \c Var (at right-hand side) and is before \c VarRef in the source code.
  56. /// Only simple assignments like \code a = b \endcode are found.
  57. class FindAssignToVarBefore
  58. : public ConstStmtVisitor<FindAssignToVarBefore, bool> {
  59. const VarDecl *Var;
  60. const DeclRefExpr *VarRef;
  61. SourceManager &SM;
  62. bool isAccessForVar(const Expr *E) const {
  63. if (const auto *DeclRef = dyn_cast<DeclRefExpr>(E->IgnoreParenCasts()))
  64. return DeclRef->getDecl() &&
  65. DeclRef->getDecl()->getCanonicalDecl() == Var &&
  66. SM.isBeforeInTranslationUnit(E->getBeginLoc(),
  67. VarRef->getBeginLoc());
  68. return false;
  69. }
  70. public:
  71. FindAssignToVarBefore(const VarDecl *Var, const DeclRefExpr *VarRef,
  72. SourceManager &SM)
  73. : Var(Var->getCanonicalDecl()), VarRef(VarRef), SM(SM) {}
  74. bool VisitDeclStmt(const DeclStmt *S) {
  75. for (const Decl *D : S->getDeclGroup())
  76. if (const auto *LeftVar = dyn_cast<VarDecl>(D))
  77. if (LeftVar->hasInit())
  78. return isAccessForVar(LeftVar->getInit());
  79. return false;
  80. }
  81. bool VisitBinaryOperator(const BinaryOperator *S) {
  82. if (S->getOpcode() == BO_Assign)
  83. return isAccessForVar(S->getRHS());
  84. return false;
  85. }
  86. bool VisitStmt(const Stmt *S) {
  87. for (const Stmt *Child : S->children())
  88. if (Child && Visit(Child))
  89. return true;
  90. return false;
  91. }
  92. };
  93. } // namespace
  94. namespace clang::tidy::bugprone {
  95. void SuspiciousReallocUsageCheck::registerMatchers(MatchFinder *Finder) {
  96. // void *realloc(void *ptr, size_t size);
  97. auto ReallocDecl =
  98. functionDecl(hasName("::realloc"), parameterCountIs(2),
  99. hasParameter(0, hasType(pointerType(pointee(voidType())))),
  100. hasParameter(1, hasType(isInteger())))
  101. .bind("realloc");
  102. auto ReallocCall =
  103. callExpr(callee(ReallocDecl), hasArgument(0, expr().bind("ptr_input")),
  104. hasAncestor(functionDecl().bind("parent_function")))
  105. .bind("call");
  106. Finder->addMatcher(binaryOperator(hasOperatorName("="),
  107. hasLHS(expr().bind("ptr_result")),
  108. hasRHS(ignoringParenCasts(ReallocCall))),
  109. this);
  110. }
  111. void SuspiciousReallocUsageCheck::check(
  112. const MatchFinder::MatchResult &Result) {
  113. const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call");
  114. if (!Call)
  115. return;
  116. const auto *PtrInputExpr = Result.Nodes.getNodeAs<Expr>("ptr_input");
  117. const auto *PtrResultExpr = Result.Nodes.getNodeAs<Expr>("ptr_result");
  118. if (!PtrInputExpr || !PtrResultExpr)
  119. return;
  120. const auto *ReallocD = Result.Nodes.getNodeAs<Decl>("realloc");
  121. assert(ReallocD && "Value for 'realloc' should exist if 'call' was found.");
  122. SourceManager &SM = ReallocD->getASTContext().getSourceManager();
  123. if (!IsSamePtrExpr{}.check(PtrInputExpr, PtrResultExpr))
  124. return;
  125. if (const auto *DeclRef =
  126. dyn_cast<DeclRefExpr>(PtrInputExpr->IgnoreParenImpCasts()))
  127. if (const auto *Var = dyn_cast<VarDecl>(DeclRef->getDecl()))
  128. if (const auto *Func =
  129. Result.Nodes.getNodeAs<FunctionDecl>("parent_function"))
  130. if (FindAssignToVarBefore{Var, DeclRef, SM}.Visit(Func->getBody()))
  131. return;
  132. StringRef CodeOfAssignedExpr = Lexer::getSourceText(
  133. CharSourceRange::getTokenRange(PtrResultExpr->getSourceRange()), SM,
  134. getLangOpts());
  135. diag(Call->getBeginLoc(), "'%0' may be set to null if 'realloc' fails, which "
  136. "may result in a leak of the original buffer")
  137. << CodeOfAssignedExpr << PtrInputExpr->getSourceRange()
  138. << PtrResultExpr->getSourceRange();
  139. }
  140. } // namespace clang::tidy::bugprone