clang  14.0.0git
UncountedLocalVarsChecker.cpp
Go to the documentation of this file.
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 
9 #include "ASTUtils.h"
10 #include "DiagOutputUtils.h"
11 #include "PtrTypesSemantics.h"
13 #include "clang/AST/Decl.h"
14 #include "clang/AST/DeclCXX.h"
22 #include "llvm/ADT/DenseSet.h"
23 
24 using namespace clang;
25 using namespace ento;
26 
27 namespace {
28 
29 // for ( int a = ...) ... true
30 // for ( int a : ...) ... true
31 // if ( int* a = ) ... true
32 // anything else ... false
33 bool isDeclaredInForOrIf(const VarDecl *Var) {
34  assert(Var);
35  auto &ASTCtx = Var->getASTContext();
36  auto parent = ASTCtx.getParents(*Var);
37 
38  if (parent.size() == 1) {
39  if (auto *DS = parent.begin()->get<DeclStmt>()) {
40  DynTypedNodeList grandParent = ASTCtx.getParents(*DS);
41  if (grandParent.size() == 1) {
42  return grandParent.begin()->get<ForStmt>() ||
43  grandParent.begin()->get<IfStmt>() ||
44  grandParent.begin()->get<CXXForRangeStmt>();
45  }
46  }
47  }
48  return false;
49 }
50 
51 // FIXME: should be defined by anotations in the future
52 bool isRefcountedStringsHack(const VarDecl *V) {
53  assert(V);
54  auto safeClass = [](const std::string &className) {
55  return className == "String" || className == "AtomString" ||
56  className == "UniquedString" || className == "Identifier";
57  };
58  QualType QT = V->getType();
59  auto *T = QT.getTypePtr();
60  if (auto *CXXRD = T->getAsCXXRecordDecl()) {
61  if (safeClass(safeGetName(CXXRD)))
62  return true;
63  }
64  if (T->isPointerType() || T->isReferenceType()) {
65  if (auto *CXXRD = T->getPointeeCXXRecordDecl()) {
66  if (safeClass(safeGetName(CXXRD)))
67  return true;
68  }
69  }
70  return false;
71 }
72 
73 bool isGuardedScopeEmbeddedInGuardianScope(const VarDecl *Guarded,
74  const VarDecl *MaybeGuardian) {
75  assert(Guarded);
76  assert(MaybeGuardian);
77 
78  if (!MaybeGuardian->isLocalVarDecl())
79  return false;
80 
81  const CompoundStmt *guardiansClosestCompStmtAncestor = nullptr;
82 
83  ASTContext &ctx = MaybeGuardian->getASTContext();
84 
85  for (DynTypedNodeList guardianAncestors = ctx.getParents(*MaybeGuardian);
86  !guardianAncestors.empty();
87  guardianAncestors = ctx.getParents(
88  *guardianAncestors
89  .begin()) // FIXME - should we handle all of the parents?
90  ) {
91  for (auto &guardianAncestor : guardianAncestors) {
92  if (auto *CStmtParentAncestor = guardianAncestor.get<CompoundStmt>()) {
93  guardiansClosestCompStmtAncestor = CStmtParentAncestor;
94  break;
95  }
96  }
97  if (guardiansClosestCompStmtAncestor)
98  break;
99  }
100 
101  if (!guardiansClosestCompStmtAncestor)
102  return false;
103 
104  // We need to skip the first CompoundStmt to avoid situation when guardian is
105  // defined in the same scope as guarded variable.
106  bool HaveSkippedFirstCompoundStmt = false;
107  for (DynTypedNodeList guardedVarAncestors = ctx.getParents(*Guarded);
108  !guardedVarAncestors.empty();
109  guardedVarAncestors = ctx.getParents(
110  *guardedVarAncestors
111  .begin()) // FIXME - should we handle all of the parents?
112  ) {
113  for (auto &guardedVarAncestor : guardedVarAncestors) {
114  if (auto *CStmtAncestor = guardedVarAncestor.get<CompoundStmt>()) {
115  if (!HaveSkippedFirstCompoundStmt) {
116  HaveSkippedFirstCompoundStmt = true;
117  continue;
118  }
119  if (CStmtAncestor == guardiansClosestCompStmtAncestor)
120  return true;
121  }
122  }
123  }
124 
125  return false;
126 }
127 
128 class UncountedLocalVarsChecker
129  : public Checker<check::ASTDecl<TranslationUnitDecl>> {
130  BugType Bug{this,
131  "Uncounted raw pointer or reference not provably backed by "
132  "ref-counted variable",
133  "WebKit coding guidelines"};
134  mutable BugReporter *BR;
135 
136 public:
137  void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
138  BugReporter &BRArg) const {
139  BR = &BRArg;
140 
141  // The calls to checkAST* from AnalysisConsumer don't
142  // visit template instantiations or lambda classes. We
143  // want to visit those, so we make our own RecursiveASTVisitor.
144  struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
145  const UncountedLocalVarsChecker *Checker;
146  explicit LocalVisitor(const UncountedLocalVarsChecker *Checker)
147  : Checker(Checker) {
148  assert(Checker);
149  }
150 
151  bool shouldVisitTemplateInstantiations() const { return true; }
152  bool shouldVisitImplicitCode() const { return false; }
153 
154  bool VisitVarDecl(VarDecl *V) {
155  Checker->visitVarDecl(V);
156  return true;
157  }
158  };
159 
160  LocalVisitor visitor(this);
161  visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
162  }
163 
164  void visitVarDecl(const VarDecl *V) const {
165  if (shouldSkipVarDecl(V))
166  return;
167 
168  const auto *ArgType = V->getType().getTypePtr();
169  if (!ArgType)
170  return;
171 
172  Optional<bool> IsUncountedPtr = isUncountedPtr(ArgType);
173  if (IsUncountedPtr && *IsUncountedPtr) {
174  const Expr *const InitExpr = V->getInit();
175  if (!InitExpr)
176  return; // FIXME: later on we might warn on uninitialized vars too
177 
178  const clang::Expr *const InitArgOrigin =
179  tryToFindPtrOrigin(InitExpr, /*StopAtFirstRefCountedObj=*/false)
180  .first;
181  if (!InitArgOrigin)
182  return;
183 
184  if (isa<CXXThisExpr>(InitArgOrigin))
185  return;
186 
187  if (auto *Ref = llvm::dyn_cast<DeclRefExpr>(InitArgOrigin)) {
188  if (auto *MaybeGuardian =
189  dyn_cast_or_null<VarDecl>(Ref->getFoundDecl())) {
190  const auto *MaybeGuardianArgType =
191  MaybeGuardian->getType().getTypePtr();
192  if (!MaybeGuardianArgType)
193  return;
194  const CXXRecordDecl *const MaybeGuardianArgCXXRecord =
195  MaybeGuardianArgType->getAsCXXRecordDecl();
196  if (!MaybeGuardianArgCXXRecord)
197  return;
198 
199  if (MaybeGuardian->isLocalVarDecl() &&
200  (isRefCounted(MaybeGuardianArgCXXRecord) ||
201  isRefcountedStringsHack(MaybeGuardian)) &&
202  isGuardedScopeEmbeddedInGuardianScope(V, MaybeGuardian)) {
203  return;
204  }
205 
206  // Parameters are guaranteed to be safe for the duration of the call
207  // by another checker.
208  if (isa<ParmVarDecl>(MaybeGuardian))
209  return;
210  }
211  }
212 
213  reportBug(V);
214  }
215  }
216 
217  bool shouldSkipVarDecl(const VarDecl *V) const {
218  assert(V);
219  if (!V->isLocalVarDecl())
220  return true;
221 
222  if (isDeclaredInForOrIf(V))
223  return true;
224 
225  return false;
226  }
227 
228  void reportBug(const VarDecl *V) const {
229  assert(V);
230  SmallString<100> Buf;
231  llvm::raw_svector_ostream Os(Buf);
232 
233  Os << "Local variable ";
235  Os << " is uncounted and unsafe.";
236 
237  PathDiagnosticLocation BSLoc(V->getLocation(), BR->getSourceManager());
238  auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
239  Report->addRange(V->getSourceRange());
240  BR->emitReport(std::move(Report));
241  }
242 };
243 } // namespace
244 
245 void ento::registerUncountedLocalVarsChecker(CheckerManager &Mgr) {
246  Mgr.registerChecker<UncountedLocalVarsChecker>();
247 }
248 
249 bool ento::shouldRegisterUncountedLocalVarsChecker(const CheckerManager &) {
250  return true;
251 }
clang::Decl::getASTContext
ASTContext & getASTContext() const LLVM_READONLY
Definition: DeclBase.cpp:414
clang::DynTypedNodeList
Container for either a single DynTypedNode or for an ArrayRef to DynTypedNode.
Definition: ParentMapContext.h:92
string
string(SUBSTRING ${CMAKE_CURRENT_BINARY_DIR} 0 ${PATH_LIB_START} PATH_HEAD) string(SUBSTRING $
Definition: CMakeLists.txt:22
clang::ASTContext::getParents
DynTypedNodeList getParents(const NodeT &Node)
Forwards to get node parents from the ParentMapContext.
Definition: ParentMapContext.h:133
CXXInheritance.h
clang::QualType
A (possibly-)qualified type.
Definition: Type.h:673
clang::isUncountedPtr
llvm::Optional< bool > isUncountedPtr(const Type *T)
Definition: PtrTypesSemantics.cpp:127
DeclCXX.h
clang::IfStmt
IfStmt - This represents an if/then/else.
Definition: Stmt.h:1904
llvm::Optional< bool >
clang::tryToFindPtrOrigin
std::pair< const Expr *, bool > tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj)
This function de-facto defines a set of transformations that we consider safe (in heuristical sense).
Definition: ASTUtils.cpp:20
Decl.h
ParentMapContext.h
V
#define V(N, I)
Definition: ASTContext.h:3121
clang::TranslationUnitDecl
The top declaration context.
Definition: Decl.h:82
BuiltinCheckerRegistration.h
clang::CompoundStmt
CompoundStmt - This represents a group of statements like { stmt stmt }.
Definition: Stmt.h:1399
clang::isRefCounted
bool isRefCounted(const CXXRecordDecl *R)
Definition: PtrTypesSemantics.cpp:168
DiagOutputUtils.h
BugReporter.h
clang::ASTContext
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:212
clang::RecursiveASTVisitor
A class that does preorder or postorder depth-first traversal on the entire Clang AST and visits each...
Definition: RecursiveASTVisitor.h:164
clang::ForStmt
ForStmt - This represents a 'for (init;cond;inc)' stmt.
Definition: Stmt.h:2534
clang::DynTypedNodeList::size
size_t size() const
Definition: ParentMapContext.h:118
clang::safeGetName
std::string safeGetName(const T *ASTNode)
Definition: ASTUtils.h:69
llvm::SmallString
Definition: LLVM.h:37
clang::VarDecl
Represents a variable declaration or definition.
Definition: Decl.h:876
clang::VarDecl::isLocalVarDecl
bool isLocalVarDecl() const
Returns true for local variable declarations other than parameters.
Definition: Decl.h:1170
clang::DynTypedNodeList::empty
bool empty() const
Definition: ParentMapContext.h:119
clang::CXXRecordDecl
Represents a C++ struct/union/class.
Definition: DeclCXX.h:255
PtrTypesSemantics.h
SourceLocation.h
BugType.h
clang::printQuotedQualifiedName
void printQuotedQualifiedName(llvm::raw_ostream &Os, const NamedDeclDerivedT &D)
Definition: DiagOutputUtils.h:18
clang::CXXForRangeStmt
CXXForRangeStmt - This represents C++0x [stmt.ranged]'s ranged for statement, represented as 'for (ra...
Definition: StmtCXX.h:134
clang::DeclStmt
DeclStmt - Adaptor class for mixing declarations with statements and expressions.
Definition: Stmt.h:1292
Checker.h
ASTUtils.h
clang
Definition: CalledOnceCheck.h:17
RecursiveASTVisitor.h
clang::DynTypedNode::get
const T * get() const
Retrieve the stored node as type T.
Definition: ASTTypeTraits.h:263
clang::QualType::getTypePtr
const Type * getTypePtr() const
Retrieves a pointer to the underlying (unqualified) type.
Definition: Type.h:6425
clang::ValueDecl::getType
QualType getType() const
Definition: Decl.h:687
clang::Expr
This represents one expression.
Definition: Expr.h:109
clang::DynTypedNodeList::begin
const DynTypedNode * begin() const
Definition: ParentMapContext.h:105