Coverage Report

Created: 2023-09-12 09:32

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