Coverage Report

Created: 2022-01-15 10:30

/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
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
24
bool isDeclaredInForOrIf(const VarDecl *Var) {
34
24
  assert(Var);
35
0
  auto &ASTCtx = Var->getASTContext();
36
24
  auto parent = ASTCtx.getParents(*Var);
37
38
24
  if (parent.size() == 1) {
39
24
    if (auto *DS = parent.begin()->get<DeclStmt>()) {
40
24
      DynTypedNodeList grandParent = ASTCtx.getParents(*DS);
41
24
      if (grandParent.size() == 1) {
42
24
        return grandParent.begin()->get<ForStmt>() ||
43
24
               
grandParent.begin()->get<IfStmt>()23
||
44
24
               
grandParent.begin()->get<CXXForRangeStmt>()22
;
45
24
      }
46
24
    }
47
24
  }
48
0
  return false;
49
24
}
50
51
// FIXME: should be defined by anotations in the future
52
1
bool isRefcountedStringsHack(const VarDecl *V) {
53
1
  assert(V);
54
1
  auto safeClass = [](const std::string &className) {
55
1
    return className == "String" || className == "AtomString" ||
56
1
           className == "UniquedString" || className == "Identifier";
57
1
  };
58
1
  QualType QT = V->getType();
59
1
  auto *T = QT.getTypePtr();
60
1
  if (auto *CXXRD = T->getAsCXXRecordDecl()) {
61
1
    if (safeClass(safeGetName(CXXRD)))
62
0
      return true;
63
1
  }
64
1
  if (T->isPointerType() || T->isReferenceType()) {
65
0
    if (auto *CXXRD = T->getPointeeCXXRecordDecl()) {
66
0
      if (safeClass(safeGetName(CXXRD)))
67
0
        return true;
68
0
    }
69
0
  }
70
1
  return false;
71
1
}
72
73
bool isGuardedScopeEmbeddedInGuardianScope(const VarDecl *Guarded,
74
7
                                           const VarDecl *MaybeGuardian) {
75
7
  assert(Guarded);
76
0
  assert(MaybeGuardian);
77
78
7
  if (!MaybeGuardian->isLocalVarDecl())
79
0
    return false;
80
81
7
  const CompoundStmt *guardiansClosestCompStmtAncestor = nullptr;
82
83
7
  ASTContext &ctx = MaybeGuardian->getASTContext();
84
85
7
  for (DynTypedNodeList guardianAncestors = ctx.getParents(*MaybeGuardian);
86
14
       !guardianAncestors.empty();
87
7
       guardianAncestors = ctx.getParents(
88
7
           *guardianAncestors
89
7
                .begin()) // FIXME - should we handle all of the parents?
90
14
  ) {
91
14
    for (auto &guardianAncestor : guardianAncestors) {
92
14
      if (auto *CStmtParentAncestor = guardianAncestor.get<CompoundStmt>()) {
93
7
        guardiansClosestCompStmtAncestor = CStmtParentAncestor;
94
7
        break;
95
7
      }
96
14
    }
97
14
    if (guardiansClosestCompStmtAncestor)
98
7
      break;
99
14
  }
100
101
7
  if (!guardiansClosestCompStmtAncestor)
102
0
    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
7
  bool HaveSkippedFirstCompoundStmt = false;
107
7
  for (DynTypedNodeList guardedVarAncestors = ctx.getParents(*Guarded);
108
25
       !guardedVarAncestors.empty();
109
18
       guardedVarAncestors = ctx.getParents(
110
18
           *guardedVarAncestors
111
18
                .begin()) // FIXME - should we handle all of the parents?
112
24
  ) {
113
24
    for (auto &guardedVarAncestor : guardedVarAncestors) {
114
24
      if (auto *CStmtAncestor = guardedVarAncestor.get<CompoundStmt>()) {
115
14
        if (!HaveSkippedFirstCompoundStmt) {
116
7
          HaveSkippedFirstCompoundStmt = true;
117
7
          continue;
118
7
        }
119
7
        if (CStmtAncestor == guardiansClosestCompStmtAncestor)
120
6
          return true;
121
7
      }
122
24
    }
123
24
  }
124
125
1
  return false;
126
7
}
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
1
                    BugReporter &BRArg) const {
139
1
    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
1
    struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
145
1
      const UncountedLocalVarsChecker *Checker;
146
1
      explicit LocalVisitor(const UncountedLocalVarsChecker *Checker)
147
1
          : Checker(Checker) {
148
1
        assert(Checker);
149
1
      }
150
151
11
      bool shouldVisitTemplateInstantiations() const { return true; }
152
155
      bool shouldVisitImplicitCode() const { return false; }
153
154
46
      bool VisitVarDecl(VarDecl *V) {
155
46
        Checker->visitVarDecl(V);
156
46
        return true;
157
46
      }
158
1
    };
159
160
1
    LocalVisitor visitor(this);
161
1
    visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
162
1
  }
163
164
46
  void visitVarDecl(const VarDecl *V) const {
165
46
    if (shouldSkipVarDecl(V))
166
25
      return;
167
168
21
    const auto *ArgType = V->getType().getTypePtr();
169
21
    if (!ArgType)
170
0
      return;
171
172
21
    Optional<bool> IsUncountedPtr = isUncountedPtr(ArgType);
173
21
    if (IsUncountedPtr && *IsUncountedPtr) {
174
12
      const Expr *const InitExpr = V->getInit();
175
12
      if (!InitExpr)
176
1
        return; // FIXME: later on we might warn on uninitialized vars too
177
178
11
      const clang::Expr *const InitArgOrigin =
179
11
          tryToFindPtrOrigin(InitExpr, /*StopAtFirstRefCountedObj=*/false)
180
11
              .first;
181
11
      if (!InitArgOrigin)
182
0
        return;
183
184
11
      if (isa<CXXThisExpr>(InitArgOrigin))
185
0
        return;
186
187
11
      if (auto *Ref = llvm::dyn_cast<DeclRefExpr>(InitArgOrigin)) {
188
8
        if (auto *MaybeGuardian =
189
8
                dyn_cast_or_null<VarDecl>(Ref->getFoundDecl())) {
190
8
          const auto *MaybeGuardianArgType =
191
8
              MaybeGuardian->getType().getTypePtr();
192
8
          if (!MaybeGuardianArgType)
193
0
            return;
194
8
          const CXXRecordDecl *const MaybeGuardianArgCXXRecord =
195
8
              MaybeGuardianArgType->getAsCXXRecordDecl();
196
8
          if (!MaybeGuardianArgCXXRecord)
197
0
            return;
198
199
8
          if (MaybeGuardian->isLocalVarDecl() &&
200
8
              (isRefCounted(MaybeGuardianArgCXXRecord) ||
201
8
               
isRefcountedStringsHack(MaybeGuardian)1
) &&
202
8
              
isGuardedScopeEmbeddedInGuardianScope(V, MaybeGuardian)7
) {
203
6
            return;
204
6
          }
205
206
          // Parameters are guaranteed to be safe for the duration of the call
207
          // by another checker.
208
2
          if (isa<ParmVarDecl>(MaybeGuardian))
209
0
            return;
210
2
        }
211
8
      }
212
213
5
      reportBug(V);
214
5
    }
215
21
  }
216
217
46
  bool shouldSkipVarDecl(const VarDecl *V) const {
218
46
    assert(V);
219
46
    if (!V->isLocalVarDecl())
220
22
      return true;
221
222
24
    if (isDeclaredInForOrIf(V))
223
3
      return true;
224
225
21
    return false;
226
24
  }
227
228
5
  void reportBug(const VarDecl *V) const {
229
5
    assert(V);
230
0
    SmallString<100> Buf;
231
5
    llvm::raw_svector_ostream Os(Buf);
232
233
5
    Os << "Local variable ";
234
5
    printQuotedQualifiedName(Os, V);
235
5
    Os << " is uncounted and unsafe.";
236
237
5
    PathDiagnosticLocation BSLoc(V->getLocation(), BR->getSourceManager());
238
5
    auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
239
5
    Report->addRange(V->getSourceRange());
240
5
    BR->emitReport(std::move(Report));
241
5
  }
242
};
243
} // namespace
244
245
1
void ento::registerUncountedLocalVarsChecker(CheckerManager &Mgr) {
246
1
  Mgr.registerChecker<UncountedLocalVarsChecker>();
247
1
}
248
249
2
bool ento::shouldRegisterUncountedLocalVarsChecker(const CheckerManager &) {
250
2
  return true;
251
2
}