Coverage Report

Created: 2022-01-22 13:19

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
Line
Count
Source (jump to first uncovered line)
1
//=======- UncountedCallArgsChecker.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/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
23
using namespace clang;
24
using namespace ento;
25
26
namespace {
27
28
class UncountedCallArgsChecker
29
    : public Checker<check::ASTDecl<TranslationUnitDecl>> {
30
  BugType Bug{this,
31
            "Uncounted call argument for a raw pointer/reference parameter",
32
            "WebKit coding guidelines"};
33
  mutable BugReporter *BR;
34
35
public:
36
37
  void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
38
1
                    BugReporter &BRArg) const {
39
1
    BR = &BRArg;
40
41
    // The calls to checkAST* from AnalysisConsumer don't
42
    // visit template instantiations or lambda classes. We
43
    // want to visit those, so we make our own RecursiveASTVisitor.
44
1
    struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
45
1
      const UncountedCallArgsChecker *Checker;
46
1
      explicit LocalVisitor(const UncountedCallArgsChecker *Checker)
47
1
          : Checker(Checker) {
48
1
        assert(Checker);
49
1
      }
50
51
16
      bool shouldVisitTemplateInstantiations() const { return true; }
52
342
      bool shouldVisitImplicitCode() const { return false; }
53
54
82
      bool VisitCallExpr(const CallExpr *CE) {
55
82
        Checker->visitCallExpr(CE);
56
82
        return true;
57
82
      }
58
1
    };
59
60
1
    LocalVisitor visitor(this);
61
1
    visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
62
1
  }
63
64
82
  void visitCallExpr(const CallExpr *CE) const {
65
82
    if (shouldSkipCall(CE))
66
13
      return;
67
68
69
    if (auto *F = CE->getDirectCallee()) {
69
      // Skip the first argument for overloaded member operators (e. g. lambda
70
      // or std::function call operator).
71
69
      unsigned ArgIdx = isa<CXXOperatorCallExpr>(CE) && 
isa_and_nonnull<CXXMethodDecl>(F)9
;
72
73
69
      for (auto P = F->param_begin();
74
           // FIXME: Also check variadic function parameters.
75
           // FIXME: Also check default function arguments. Probably a different
76
           // checker. In case there are default arguments the call can have
77
           // fewer arguments than the callee has parameters.
78
115
           P < F->param_end() && 
ArgIdx < CE->getNumArgs()46
;
++P, ++ArgIdx46
) {
79
        // TODO: attributes.
80
        // if ((*P)->hasAttr<SafeRefCntblRawPtrAttr>())
81
        //  continue;
82
83
46
        const auto *ArgType = (*P)->getType().getTypePtrOrNull();
84
46
        if (!ArgType)
85
0
          continue; // FIXME? Should we bail?
86
87
        // FIXME: more complex types (arrays, references to raw pointers, etc)
88
46
        Optional<bool> IsUncounted = isUncountedPtr(ArgType);
89
46
        if (!IsUncounted || !(*IsUncounted))
90
3
          continue;
91
92
43
        const auto *Arg = CE->getArg(ArgIdx);
93
94
43
        std::pair<const clang::Expr *, bool> ArgOrigin =
95
43
            tryToFindPtrOrigin(Arg, true);
96
97
        // Temporary ref-counted object created as part of the call argument
98
        // would outlive the call.
99
43
        if (ArgOrigin.second)
100
5
          continue;
101
102
38
        if (isa<CXXNullPtrLiteralExpr>(ArgOrigin.first)) {
103
          // foo(nullptr)
104
1
          continue;
105
1
        }
106
37
        if (isa<IntegerLiteral>(ArgOrigin.first)) {
107
          // FIXME: Check the value.
108
          // foo(NULL)
109
1
          continue;
110
1
        }
111
112
36
        if (isASafeCallArg(ArgOrigin.first))
113
18
          continue;
114
115
18
        reportBug(Arg, *P);
116
18
      }
117
69
    }
118
69
  }
119
120
82
  bool shouldSkipCall(const CallExpr *CE) const {
121
82
    if (CE->getNumArgs() == 0)
122
26
      return false;
123
124
    // If an assignment is problematic we should warn about the sole existence
125
    // of object on LHS.
126
56
    if (auto *MemberOp = dyn_cast<CXXOperatorCallExpr>(CE)) {
127
      // Note: assignemnt to built-in type isn't derived from CallExpr.
128
9
      if (MemberOp->isAssignmentOp())
129
0
        return false;
130
9
    }
131
132
56
    const auto *Callee = CE->getDirectCallee();
133
56
    if (!Callee)
134
0
      return false;
135
136
56
    auto overloadedOperatorType = Callee->getOverloadedOperator();
137
56
    if (overloadedOperatorType == OO_EqualEqual ||
138
56
        overloadedOperatorType == OO_ExclaimEqual ||
139
56
        overloadedOperatorType == OO_LessEqual ||
140
56
        overloadedOperatorType == OO_GreaterEqual ||
141
56
        overloadedOperatorType == OO_Spaceship ||
142
56
        overloadedOperatorType == OO_AmpAmp ||
143
56
        overloadedOperatorType == OO_PipePipe)
144
0
      return true;
145
146
56
    if (isCtorOfRefCounted(Callee))
147
2
      return true;
148
149
54
    auto name = safeGetName(Callee);
150
54
    if (name == "adoptRef" || name == "getPtr" || name == "WeakPtr" ||
151
54
        name == "makeWeakPtr" || 
name == "downcast"52
||
name == "bitwise_cast"46
||
152
54
        
name == "is"43
||
name == "equal"43
||
name == "hash"43
||
153
54
        
name == "isType"43
154
        // FIXME: Most/all of these should be implemented via attributes.
155
54
        || 
name == "equalIgnoringASCIICase"43
||
156
54
        
name == "equalIgnoringASCIICaseCommon"43
||
157
54
        
name == "equalIgnoringNullity"43
)
158
11
      return true;
159
160
43
    return false;
161
54
  }
162
163
18
  void reportBug(const Expr *CallArg, const ParmVarDecl *Param) const {
164
18
    assert(CallArg);
165
166
0
    SmallString<100> Buf;
167
18
    llvm::raw_svector_ostream Os(Buf);
168
169
18
    const std::string paramName = safeGetName(Param);
170
18
    Os << "Call argument";
171
18
    if (!paramName.empty()) {
172
7
      Os << " for parameter ";
173
7
      printQuotedQualifiedName(Os, Param);
174
7
    }
175
18
    Os << " is uncounted and unsafe.";
176
177
18
    const SourceLocation SrcLocToReport =
178
18
        isa<CXXDefaultArgExpr>(CallArg) ? 
Param->getDefaultArg()->getExprLoc()1
179
18
                                        : 
CallArg->getSourceRange().getBegin()17
;
180
181
18
    PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
182
18
    auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
183
18
    Report->addRange(CallArg->getSourceRange());
184
18
    BR->emitReport(std::move(Report));
185
18
  }
186
};
187
} // namespace
188
189
1
void ento::registerUncountedCallArgsChecker(CheckerManager &Mgr) {
190
1
  Mgr.registerChecker<UncountedCallArgsChecker>();
191
1
}
192
193
2
bool ento::shouldRegisterUncountedCallArgsChecker(const CheckerManager &) {
194
2
  return true;
195
2
}