clang  19.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 <optional>
23 
24 using namespace clang;
25 using namespace ento;
26 
27 namespace {
28 
29 // FIXME: should be defined by anotations in the future
30 bool isRefcountedStringsHack(const VarDecl *V) {
31  assert(V);
32  auto safeClass = [](const std::string &className) {
33  return className == "String" || className == "AtomString" ||
34  className == "UniquedString" || className == "Identifier";
35  };
36  QualType QT = V->getType();
37  auto *T = QT.getTypePtr();
38  if (auto *CXXRD = T->getAsCXXRecordDecl()) {
39  if (safeClass(safeGetName(CXXRD)))
40  return true;
41  }
42  if (T->isPointerType() || T->isReferenceType()) {
43  if (auto *CXXRD = T->getPointeeCXXRecordDecl()) {
44  if (safeClass(safeGetName(CXXRD)))
45  return true;
46  }
47  }
48  return false;
49 }
50 
51 bool isGuardedScopeEmbeddedInGuardianScope(const VarDecl *Guarded,
52  const VarDecl *MaybeGuardian) {
53  assert(Guarded);
54  assert(MaybeGuardian);
55 
56  if (!MaybeGuardian->isLocalVarDecl())
57  return false;
58 
59  const CompoundStmt *guardiansClosestCompStmtAncestor = nullptr;
60 
61  ASTContext &ctx = MaybeGuardian->getASTContext();
62 
63  for (DynTypedNodeList guardianAncestors = ctx.getParents(*MaybeGuardian);
64  !guardianAncestors.empty();
65  guardianAncestors = ctx.getParents(
66  *guardianAncestors
67  .begin()) // FIXME - should we handle all of the parents?
68  ) {
69  for (auto &guardianAncestor : guardianAncestors) {
70  if (auto *CStmtParentAncestor = guardianAncestor.get<CompoundStmt>()) {
71  guardiansClosestCompStmtAncestor = CStmtParentAncestor;
72  break;
73  }
74  }
75  if (guardiansClosestCompStmtAncestor)
76  break;
77  }
78 
79  if (!guardiansClosestCompStmtAncestor)
80  return false;
81 
82  // We need to skip the first CompoundStmt to avoid situation when guardian is
83  // defined in the same scope as guarded variable.
84  bool HaveSkippedFirstCompoundStmt = false;
85  for (DynTypedNodeList guardedVarAncestors = ctx.getParents(*Guarded);
86  !guardedVarAncestors.empty();
87  guardedVarAncestors = ctx.getParents(
88  *guardedVarAncestors
89  .begin()) // FIXME - should we handle all of the parents?
90  ) {
91  for (auto &guardedVarAncestor : guardedVarAncestors) {
92  if (auto *CStmtAncestor = guardedVarAncestor.get<CompoundStmt>()) {
93  if (!HaveSkippedFirstCompoundStmt) {
94  HaveSkippedFirstCompoundStmt = true;
95  continue;
96  }
97  if (CStmtAncestor == guardiansClosestCompStmtAncestor)
98  return true;
99  }
100  }
101  }
102 
103  return false;
104 }
105 
106 class UncountedLocalVarsChecker
107  : public Checker<check::ASTDecl<TranslationUnitDecl>> {
108  BugType Bug{this,
109  "Uncounted raw pointer or reference not provably backed by "
110  "ref-counted variable",
111  "WebKit coding guidelines"};
112  mutable BugReporter *BR;
113 
114 public:
115  void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
116  BugReporter &BRArg) const {
117  BR = &BRArg;
118 
119  // The calls to checkAST* from AnalysisConsumer don't
120  // visit template instantiations or lambda classes. We
121  // want to visit those, so we make our own RecursiveASTVisitor.
122  struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
123  const UncountedLocalVarsChecker *Checker;
124 
126 
128 
129  explicit LocalVisitor(const UncountedLocalVarsChecker *Checker)
130  : Checker(Checker) {
131  assert(Checker);
132  }
133 
134  bool shouldVisitTemplateInstantiations() const { return true; }
135  bool shouldVisitImplicitCode() const { return false; }
136 
137  bool VisitVarDecl(VarDecl *V) {
138  auto *Init = V->getInit();
139  if (Init && V->isLocalVarDecl())
140  Checker->visitVarDecl(V, Init);
141  return true;
142  }
143 
144  bool VisitBinaryOperator(const BinaryOperator *BO) {
145  if (BO->isAssignmentOp()) {
146  if (auto *VarRef = dyn_cast<DeclRefExpr>(BO->getLHS())) {
147  if (auto *V = dyn_cast<VarDecl>(VarRef->getDecl()))
148  Checker->visitVarDecl(V, BO->getRHS());
149  }
150  }
151  return true;
152  }
153 
154  bool TraverseIfStmt(IfStmt *IS) {
155  if (!TFA.isTrivial(IS))
156  return Base::TraverseIfStmt(IS);
157  return true;
158  }
159 
160  bool TraverseForStmt(ForStmt *FS) {
161  if (!TFA.isTrivial(FS))
162  return Base::TraverseForStmt(FS);
163  return true;
164  }
165 
166  bool TraverseCXXForRangeStmt(CXXForRangeStmt *FRS) {
167  if (!TFA.isTrivial(FRS))
168  return Base::TraverseCXXForRangeStmt(FRS);
169  return true;
170  }
171 
172  bool TraverseWhileStmt(WhileStmt *WS) {
173  if (!TFA.isTrivial(WS))
174  return Base::TraverseWhileStmt(WS);
175  return true;
176  }
177 
178  bool TraverseCompoundStmt(CompoundStmt *CS) {
179  if (!TFA.isTrivial(CS))
180  return Base::TraverseCompoundStmt(CS);
181  return true;
182  }
183  };
184 
185  LocalVisitor visitor(this);
186  visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
187  }
188 
189  void visitVarDecl(const VarDecl *V, const Expr *Value) const {
190  if (shouldSkipVarDecl(V))
191  return;
192 
193  const auto *ArgType = V->getType().getTypePtr();
194  if (!ArgType)
195  return;
196 
197  std::optional<bool> IsUncountedPtr = isUncountedPtr(ArgType);
198  if (IsUncountedPtr && *IsUncountedPtr) {
199  if (tryToFindPtrOrigin(
200  Value, /*StopAtFirstRefCountedObj=*/false,
201  [&](const clang::Expr *InitArgOrigin, bool IsSafe) {
202  if (!InitArgOrigin)
203  return true;
204 
205  if (isa<CXXThisExpr>(InitArgOrigin))
206  return true;
207 
208  if (isa<CXXNullPtrLiteralExpr>(InitArgOrigin))
209  return true;
210 
211  if (isa<IntegerLiteral>(InitArgOrigin))
212  return true;
213 
214  if (auto *Ref = llvm::dyn_cast<DeclRefExpr>(InitArgOrigin)) {
215  if (auto *MaybeGuardian =
216  dyn_cast_or_null<VarDecl>(Ref->getFoundDecl())) {
217  const auto *MaybeGuardianArgType =
218  MaybeGuardian->getType().getTypePtr();
219  if (MaybeGuardianArgType) {
220  const CXXRecordDecl *const MaybeGuardianArgCXXRecord =
221  MaybeGuardianArgType->getAsCXXRecordDecl();
222  if (MaybeGuardianArgCXXRecord) {
223  if (MaybeGuardian->isLocalVarDecl() &&
224  (isRefCounted(MaybeGuardianArgCXXRecord) ||
225  isRefcountedStringsHack(MaybeGuardian)) &&
226  isGuardedScopeEmbeddedInGuardianScope(
227  V, MaybeGuardian))
228  return true;
229  }
230  }
231 
232  // Parameters are guaranteed to be safe for the duration of
233  // the call by another checker.
234  if (isa<ParmVarDecl>(MaybeGuardian))
235  return true;
236  }
237  }
238 
239  return false;
240  }))
241  return;
242 
243  reportBug(V, Value);
244  }
245  }
246 
247  bool shouldSkipVarDecl(const VarDecl *V) const {
248  assert(V);
249  return BR->getSourceManager().isInSystemHeader(V->getLocation());
250  }
251 
252  void reportBug(const VarDecl *V, const Expr *Value) const {
253  assert(V);
254  SmallString<100> Buf;
255  llvm::raw_svector_ostream Os(Buf);
256 
257  if (dyn_cast<ParmVarDecl>(V)) {
258  Os << "Assignment to an uncounted parameter ";
260  Os << " is unsafe.";
261 
262  PathDiagnosticLocation BSLoc(Value->getExprLoc(), BR->getSourceManager());
263  auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
264  Report->addRange(Value->getSourceRange());
265  BR->emitReport(std::move(Report));
266  } else {
267  if (V->hasLocalStorage())
268  Os << "Local variable ";
269  else if (V->isStaticLocal())
270  Os << "Static local variable ";
271  else if (V->hasGlobalStorage())
272  Os << "Global variable ";
273  else
274  Os << "Variable ";
276  Os << " is uncounted and unsafe.";
277 
278  PathDiagnosticLocation BSLoc(V->getLocation(), BR->getSourceManager());
279  auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
280  Report->addRange(V->getSourceRange());
281  BR->emitReport(std::move(Report));
282  }
283  }
284 };
285 } // namespace
286 
287 void ento::registerUncountedLocalVarsChecker(CheckerManager &Mgr) {
288  Mgr.registerChecker<UncountedLocalVarsChecker>();
289 }
290 
291 bool ento::shouldRegisterUncountedLocalVarsChecker(const CheckerManager &) {
292  return true;
293 }
#define V(N, I)
Definition: ASTContext.h:3299
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....
Defines the clang::SourceLocation class and associated facilities.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:185
DynTypedNodeList getParents(const NodeT &Node)
Forwards to get node parents from the ParentMapContext.
A builtin binary operation expression such as "x + y" or "x <= y".
Definition: Expr.h:3892
static bool isAssignmentOp(Opcode Opc)
Definition: Expr.h:4027
Expr * getRHS() const
Definition: Expr.h:3943
Expr * getLHS() const
Definition: Expr.h:3941
CXXForRangeStmt - This represents C++0x [stmt.ranged]'s ranged for statement, represented as 'for (ra...
Definition: StmtCXX.h:135
CompoundStmt - This represents a group of statements like { stmt stmt }.
Definition: Stmt.h:1606
ASTContext & getASTContext() const LLVM_READONLY
Definition: DeclBase.cpp:501
Container for either a single DynTypedNode or for an ArrayRef to DynTypedNode.
This represents one expression.
Definition: Expr.h:110
ForStmt - This represents a 'for (init;cond;inc)' stmt.
Definition: Stmt.h:2781
IfStmt - This represents an if/then/else.
Definition: Stmt.h:2138
A (possibly-)qualified type.
Definition: Type.h:940
const Type * getTypePtr() const
Retrieves a pointer to the underlying (unqualified) type.
Definition: Type.h:7371
A class that does preorder or postorder depth-first traversal on the entire Clang AST and visits each...
bool isInSystemHeader(SourceLocation Loc) const
Returns if a SourceLocation is in a system header.
The top declaration context.
Definition: Decl.h:84
An inter-procedural analysis facility that detects functions with "trivial" behavior with respect to ...
bool isTrivial(const Decl *D) const
CXXRecordDecl * getAsCXXRecordDecl() const
Retrieves the CXXRecordDecl that this type refers to, either because the type is a RecordType or beca...
Definition: Type.cpp:1881
bool isPointerType() const
Definition: Type.h:7624
bool isReferenceType() const
Definition: Type.h:7636
const CXXRecordDecl * getPointeeCXXRecordDecl() const
If this is a pointer or reference to a RecordType, return the CXXRecordDecl that the type refers to.
Definition: Type.cpp:1866
Represents a variable declaration or definition.
Definition: Decl.h:919
bool isLocalVarDecl() const
Returns true for local variable declarations other than parameters.
Definition: Decl.h:1241
WhileStmt - This represents a 'while' stmt.
Definition: Stmt.h:2584
BugReporter is a utility class for generating PathDiagnostics for analysis.
Definition: BugReporter.h:585
const SourceManager & getSourceManager()
Definition: BugReporter.h:623
virtual void emitReport(std::unique_ptr< BugReport > R)
Add the given report to the set of reports tracked by BugReporter.
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
bool Init(InterpState &S, CodePtr OpPC)
Definition: Interp.h:1472
The JSON file list parser is used to communicate input to InstallAPI.
std::optional< bool > isUncountedPtr(const Type *T)
if(T->getSizeExpr()) TRY_TO(TraverseStmt(const_cast< Expr * >(T -> getSizeExpr())))
void printQuotedQualifiedName(llvm::raw_ostream &Os, const NamedDeclDerivedT &D)
const FunctionProtoType * T
bool tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj, std::function< bool(const clang::Expr *, bool)> callback)
This function de-facto defines a set of transformations that we consider safe (in heuristical sense).
Definition: ASTUtils.cpp:19
std::string safeGetName(const T *ASTNode)
Definition: ASTUtils.h:68