UncountedLocalVarsChecker.cpp 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. //=======- UncountedLocalVarsChecker.cpp -------------------------*- 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. #include "ASTUtils.h"
  9. #include "DiagOutputUtils.h"
  10. #include "PtrTypesSemantics.h"
  11. #include "clang/AST/CXXInheritance.h"
  12. #include "clang/AST/Decl.h"
  13. #include "clang/AST/DeclCXX.h"
  14. #include "clang/AST/ParentMapContext.h"
  15. #include "clang/AST/RecursiveASTVisitor.h"
  16. #include "clang/Basic/SourceLocation.h"
  17. #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
  18. #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
  19. #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
  20. #include "clang/StaticAnalyzer/Core/Checker.h"
  21. #include "llvm/ADT/DenseSet.h"
  22. #include <optional>
  23. using namespace clang;
  24. using namespace ento;
  25. namespace {
  26. // for ( int a = ...) ... true
  27. // for ( int a : ...) ... true
  28. // if ( int* a = ) ... true
  29. // anything else ... false
  30. bool isDeclaredInForOrIf(const VarDecl *Var) {
  31. assert(Var);
  32. auto &ASTCtx = Var->getASTContext();
  33. auto parent = ASTCtx.getParents(*Var);
  34. if (parent.size() == 1) {
  35. if (auto *DS = parent.begin()->get<DeclStmt>()) {
  36. DynTypedNodeList grandParent = ASTCtx.getParents(*DS);
  37. if (grandParent.size() == 1) {
  38. return grandParent.begin()->get<ForStmt>() ||
  39. grandParent.begin()->get<IfStmt>() ||
  40. grandParent.begin()->get<CXXForRangeStmt>();
  41. }
  42. }
  43. }
  44. return false;
  45. }
  46. // FIXME: should be defined by anotations in the future
  47. bool isRefcountedStringsHack(const VarDecl *V) {
  48. assert(V);
  49. auto safeClass = [](const std::string &className) {
  50. return className == "String" || className == "AtomString" ||
  51. className == "UniquedString" || className == "Identifier";
  52. };
  53. QualType QT = V->getType();
  54. auto *T = QT.getTypePtr();
  55. if (auto *CXXRD = T->getAsCXXRecordDecl()) {
  56. if (safeClass(safeGetName(CXXRD)))
  57. return true;
  58. }
  59. if (T->isPointerType() || T->isReferenceType()) {
  60. if (auto *CXXRD = T->getPointeeCXXRecordDecl()) {
  61. if (safeClass(safeGetName(CXXRD)))
  62. return true;
  63. }
  64. }
  65. return false;
  66. }
  67. bool isGuardedScopeEmbeddedInGuardianScope(const VarDecl *Guarded,
  68. const VarDecl *MaybeGuardian) {
  69. assert(Guarded);
  70. assert(MaybeGuardian);
  71. if (!MaybeGuardian->isLocalVarDecl())
  72. return false;
  73. const CompoundStmt *guardiansClosestCompStmtAncestor = nullptr;
  74. ASTContext &ctx = MaybeGuardian->getASTContext();
  75. for (DynTypedNodeList guardianAncestors = ctx.getParents(*MaybeGuardian);
  76. !guardianAncestors.empty();
  77. guardianAncestors = ctx.getParents(
  78. *guardianAncestors
  79. .begin()) // FIXME - should we handle all of the parents?
  80. ) {
  81. for (auto &guardianAncestor : guardianAncestors) {
  82. if (auto *CStmtParentAncestor = guardianAncestor.get<CompoundStmt>()) {
  83. guardiansClosestCompStmtAncestor = CStmtParentAncestor;
  84. break;
  85. }
  86. }
  87. if (guardiansClosestCompStmtAncestor)
  88. break;
  89. }
  90. if (!guardiansClosestCompStmtAncestor)
  91. return false;
  92. // We need to skip the first CompoundStmt to avoid situation when guardian is
  93. // defined in the same scope as guarded variable.
  94. bool HaveSkippedFirstCompoundStmt = false;
  95. for (DynTypedNodeList guardedVarAncestors = ctx.getParents(*Guarded);
  96. !guardedVarAncestors.empty();
  97. guardedVarAncestors = ctx.getParents(
  98. *guardedVarAncestors
  99. .begin()) // FIXME - should we handle all of the parents?
  100. ) {
  101. for (auto &guardedVarAncestor : guardedVarAncestors) {
  102. if (auto *CStmtAncestor = guardedVarAncestor.get<CompoundStmt>()) {
  103. if (!HaveSkippedFirstCompoundStmt) {
  104. HaveSkippedFirstCompoundStmt = true;
  105. continue;
  106. }
  107. if (CStmtAncestor == guardiansClosestCompStmtAncestor)
  108. return true;
  109. }
  110. }
  111. }
  112. return false;
  113. }
  114. class UncountedLocalVarsChecker
  115. : public Checker<check::ASTDecl<TranslationUnitDecl>> {
  116. BugType Bug{this,
  117. "Uncounted raw pointer or reference not provably backed by "
  118. "ref-counted variable",
  119. "WebKit coding guidelines"};
  120. mutable BugReporter *BR;
  121. public:
  122. void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
  123. BugReporter &BRArg) const {
  124. BR = &BRArg;
  125. // The calls to checkAST* from AnalysisConsumer don't
  126. // visit template instantiations or lambda classes. We
  127. // want to visit those, so we make our own RecursiveASTVisitor.
  128. struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
  129. const UncountedLocalVarsChecker *Checker;
  130. explicit LocalVisitor(const UncountedLocalVarsChecker *Checker)
  131. : Checker(Checker) {
  132. assert(Checker);
  133. }
  134. bool shouldVisitTemplateInstantiations() const { return true; }
  135. bool shouldVisitImplicitCode() const { return false; }
  136. bool VisitVarDecl(VarDecl *V) {
  137. Checker->visitVarDecl(V);
  138. return true;
  139. }
  140. };
  141. LocalVisitor visitor(this);
  142. visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
  143. }
  144. void visitVarDecl(const VarDecl *V) const {
  145. if (shouldSkipVarDecl(V))
  146. return;
  147. const auto *ArgType = V->getType().getTypePtr();
  148. if (!ArgType)
  149. return;
  150. std::optional<bool> IsUncountedPtr = isUncountedPtr(ArgType);
  151. if (IsUncountedPtr && *IsUncountedPtr) {
  152. const Expr *const InitExpr = V->getInit();
  153. if (!InitExpr)
  154. return; // FIXME: later on we might warn on uninitialized vars too
  155. const clang::Expr *const InitArgOrigin =
  156. tryToFindPtrOrigin(InitExpr, /*StopAtFirstRefCountedObj=*/false)
  157. .first;
  158. if (!InitArgOrigin)
  159. return;
  160. if (isa<CXXThisExpr>(InitArgOrigin))
  161. return;
  162. if (auto *Ref = llvm::dyn_cast<DeclRefExpr>(InitArgOrigin)) {
  163. if (auto *MaybeGuardian =
  164. dyn_cast_or_null<VarDecl>(Ref->getFoundDecl())) {
  165. const auto *MaybeGuardianArgType =
  166. MaybeGuardian->getType().getTypePtr();
  167. if (!MaybeGuardianArgType)
  168. return;
  169. const CXXRecordDecl *const MaybeGuardianArgCXXRecord =
  170. MaybeGuardianArgType->getAsCXXRecordDecl();
  171. if (!MaybeGuardianArgCXXRecord)
  172. return;
  173. if (MaybeGuardian->isLocalVarDecl() &&
  174. (isRefCounted(MaybeGuardianArgCXXRecord) ||
  175. isRefcountedStringsHack(MaybeGuardian)) &&
  176. isGuardedScopeEmbeddedInGuardianScope(V, MaybeGuardian)) {
  177. return;
  178. }
  179. // Parameters are guaranteed to be safe for the duration of the call
  180. // by another checker.
  181. if (isa<ParmVarDecl>(MaybeGuardian))
  182. return;
  183. }
  184. }
  185. reportBug(V);
  186. }
  187. }
  188. bool shouldSkipVarDecl(const VarDecl *V) const {
  189. assert(V);
  190. if (!V->isLocalVarDecl())
  191. return true;
  192. if (isDeclaredInForOrIf(V))
  193. return true;
  194. return false;
  195. }
  196. void reportBug(const VarDecl *V) const {
  197. assert(V);
  198. SmallString<100> Buf;
  199. llvm::raw_svector_ostream Os(Buf);
  200. Os << "Local variable ";
  201. printQuotedQualifiedName(Os, V);
  202. Os << " is uncounted and unsafe.";
  203. PathDiagnosticLocation BSLoc(V->getLocation(), BR->getSourceManager());
  204. auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
  205. Report->addRange(V->getSourceRange());
  206. BR->emitReport(std::move(Report));
  207. }
  208. };
  209. } // namespace
  210. void ento::registerUncountedLocalVarsChecker(CheckerManager &Mgr) {
  211. Mgr.registerChecker<UncountedLocalVarsChecker>();
  212. }
  213. bool ento::shouldRegisterUncountedLocalVarsChecker(const CheckerManager &) {
  214. return true;
  215. }