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