xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp (revision c66ec88fed842fbaad62c30d510644ceb7bd2d71)
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                     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 =
72           isa<CXXOperatorCallExpr>(CE) && dyn_cast_or_null<CXXMethodDecl>(F);
73 
74       for (auto P = F->param_begin();
75            // FIXME: Also check variadic function parameters.
76            // FIXME: Also check default function arguments. Probably a different
77            // checker. In case there are default arguments the call can have
78            // fewer arguments than the callee has parameters.
79            P < F->param_end() && ArgIdx < CE->getNumArgs(); ++P, ++ArgIdx) {
80         // TODO: attributes.
81         // if ((*P)->hasAttr<SafeRefCntblRawPtrAttr>())
82         //  continue;
83 
84         const auto *ArgType = (*P)->getType().getTypePtrOrNull();
85         if (!ArgType)
86           continue; // FIXME? Should we bail?
87 
88         // FIXME: more complex types (arrays, references to raw pointers, etc)
89         if (!isUncountedPtr(ArgType))
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 == "makeWeakPtr" || 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 
189 void ento::registerUncountedCallArgsChecker(CheckerManager &Mgr) {
190   Mgr.registerChecker<UncountedCallArgsChecker>();
191 }
192 
193 bool ento::shouldRegisterUncountedCallArgsChecker(const CheckerManager &) {
194   return true;
195 }
196