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