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
35 TrivialFunctionAnalysis TFA;
36
37public:
38
39 void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
40 BugReporter &BRArg) const {
41 BR = &BRArg;
42
43 // The calls to checkAST* from AnalysisConsumer don't
44 // visit template instantiations or lambda classes. We
45 // want to visit those, so we make our own RecursiveASTVisitor.
46 struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
47 const UncountedCallArgsChecker *Checker;
48 explicit LocalVisitor(const UncountedCallArgsChecker *Checker)
49 : Checker(Checker) {
50 assert(Checker);
51 }
52
53 bool shouldVisitTemplateInstantiations() const { return true; }
54 bool shouldVisitImplicitCode() const { return false; }
55
56 bool VisitCallExpr(const CallExpr *CE) {
57 Checker->visitCallExpr(CE);
58 return true;
59 }
60 };
61
62 LocalVisitor visitor(this);
63 visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
64 }
65
66 void visitCallExpr(const CallExpr *CE) const {
67 if (shouldSkipCall(CE))
68 return;
69
70 if (auto *F = CE->getDirectCallee()) {
71 // Skip the first argument for overloaded member operators (e. g. lambda
72 // or std::function call operator).
73 unsigned ArgIdx = isa<CXXOperatorCallExpr>(Val: CE) && isa_and_nonnull<CXXMethodDecl>(Val: F);
74
75 if (auto *MemberCallExpr = dyn_cast<CXXMemberCallExpr>(Val: CE)) {
76 if (auto *MD = MemberCallExpr->getMethodDecl()) {
77 auto name = safeGetName(ASTNode: MD);
78 if (name == "ref" || name == "deref")
79 return;
80 }
81 auto *E = MemberCallExpr->getImplicitObjectArgument();
82 QualType ArgType = MemberCallExpr->getObjectType();
83 std::optional<bool> IsUncounted =
84 isUncounted(Class: ArgType->getAsCXXRecordDecl());
85 if (IsUncounted && *IsUncounted && !isPtrOriginSafe(Arg: E))
86 reportBugOnThis(CallArg: E);
87 }
88
89 for (auto P = F->param_begin();
90 // FIXME: Also check variadic function parameters.
91 // FIXME: Also check default function arguments. Probably a different
92 // checker. In case there are default arguments the call can have
93 // fewer arguments than the callee has parameters.
94 P < F->param_end() && ArgIdx < CE->getNumArgs(); ++P, ++ArgIdx) {
95 // TODO: attributes.
96 // if ((*P)->hasAttr<SafeRefCntblRawPtrAttr>())
97 // continue;
98
99 const auto *ArgType = (*P)->getType().getTypePtrOrNull();
100 if (!ArgType)
101 continue; // FIXME? Should we bail?
102
103 // FIXME: more complex types (arrays, references to raw pointers, etc)
104 std::optional<bool> IsUncounted = isUncountedPtr(ArgType);
105 if (!IsUncounted || !(*IsUncounted))
106 continue;
107
108 const auto *Arg = CE->getArg(Arg: ArgIdx);
109
110 if (auto *defaultArg = dyn_cast<CXXDefaultArgExpr>(Val: Arg))
111 Arg = defaultArg->getExpr();
112
113 if (isPtrOriginSafe(Arg))
114 continue;
115
116 reportBug(CallArg: Arg, Param: *P);
117 }
118 }
119 }
120
121 bool isPtrOriginSafe(const Expr *Arg) const {
122 std::pair<const clang::Expr *, bool> ArgOrigin =
123 tryToFindPtrOrigin(E: Arg, StopAtFirstRefCountedObj: true);
124
125 // Temporary ref-counted object created as part of the call argument
126 // would outlive the call.
127 if (ArgOrigin.second)
128 return true;
129
130 if (isa<CXXNullPtrLiteralExpr>(Val: ArgOrigin.first)) {
131 // foo(nullptr)
132 return true;
133 }
134 if (isa<IntegerLiteral>(Val: ArgOrigin.first)) {
135 // FIXME: Check the value.
136 // foo(NULL)
137 return true;
138 }
139
140 return isASafeCallArg(E: ArgOrigin.first);
141 }
142
143 bool shouldSkipCall(const CallExpr *CE) const {
144 const auto *Callee = CE->getDirectCallee();
145
146 if (Callee && TFA.isTrivial(Callee))
147 return true;
148
149 if (CE->getNumArgs() == 0)
150 return false;
151
152 // If an assignment is problematic we should warn about the sole existence
153 // of object on LHS.
154 if (auto *MemberOp = dyn_cast<CXXOperatorCallExpr>(Val: CE)) {
155 // Note: assignemnt to built-in type isn't derived from CallExpr.
156 if (MemberOp->getOperator() ==
157 OO_Equal) { // Ignore assignment to Ref/RefPtr.
158 auto *callee = MemberOp->getDirectCallee();
159 if (auto *calleeDecl = dyn_cast<CXXMethodDecl>(callee)) {
160 if (const CXXRecordDecl *classDecl = calleeDecl->getParent()) {
161 if (isRefCounted(Class: classDecl))
162 return true;
163 }
164 }
165 }
166 if (MemberOp->isAssignmentOp())
167 return false;
168 }
169
170 if (!Callee)
171 return false;
172
173 if (isMethodOnWTFContainerType(Decl: Callee))
174 return true;
175
176 auto overloadedOperatorType = Callee->getOverloadedOperator();
177 if (overloadedOperatorType == OO_EqualEqual ||
178 overloadedOperatorType == OO_ExclaimEqual ||
179 overloadedOperatorType == OO_LessEqual ||
180 overloadedOperatorType == OO_GreaterEqual ||
181 overloadedOperatorType == OO_Spaceship ||
182 overloadedOperatorType == OO_AmpAmp ||
183 overloadedOperatorType == OO_PipePipe)
184 return true;
185
186 if (isCtorOfRefCounted(F: Callee))
187 return true;
188
189 auto name = safeGetName(ASTNode: Callee);
190 if (name == "adoptRef" || name == "getPtr" || name == "WeakPtr" ||
191 name == "dynamicDowncast" || name == "downcast" ||
192 name == "checkedDowncast" || name == "uncheckedDowncast" ||
193 name == "bitwise_cast" || name == "is" || name == "equal" ||
194 name == "hash" || name == "isType" ||
195 // FIXME: Most/all of these should be implemented via attributes.
196 name == "equalIgnoringASCIICase" ||
197 name == "equalIgnoringASCIICaseCommon" ||
198 name == "equalIgnoringNullity" || name == "toString")
199 return true;
200
201 return false;
202 }
203
204 bool isMethodOnWTFContainerType(const FunctionDecl *Decl) const {
205 if (!isa<CXXMethodDecl>(Val: Decl))
206 return false;
207 auto *ClassDecl = Decl->getParent();
208 if (!ClassDecl || !isa<CXXRecordDecl>(ClassDecl))
209 return false;
210
211 auto *NsDecl = ClassDecl->getParent();
212 if (!NsDecl || !isa<NamespaceDecl>(NsDecl))
213 return false;
214
215 auto MethodName = safeGetName(ASTNode: Decl);
216 auto ClsNameStr = safeGetName(ClassDecl);
217 StringRef ClsName = ClsNameStr; // FIXME: Make safeGetName return StringRef.
218 auto NamespaceName = safeGetName(NsDecl);
219 // FIXME: These should be implemented via attributes.
220 return NamespaceName == "WTF" &&
221 (MethodName == "find" || MethodName == "findIf" ||
222 MethodName == "reverseFind" || MethodName == "reverseFindIf" ||
223 MethodName == "get" || MethodName == "inlineGet" ||
224 MethodName == "contains" || MethodName == "containsIf") &&
225 (ClsName.ends_with(Suffix: "Vector") || ClsName.ends_with(Suffix: "Set") ||
226 ClsName.ends_with(Suffix: "Map"));
227 }
228
229 void reportBug(const Expr *CallArg, const ParmVarDecl *Param) const {
230 assert(CallArg);
231
232 SmallString<100> Buf;
233 llvm::raw_svector_ostream Os(Buf);
234
235 const std::string paramName = safeGetName(ASTNode: Param);
236 Os << "Call argument";
237 if (!paramName.empty()) {
238 Os << " for parameter ";
239 printQuotedQualifiedName(Os, D: Param);
240 }
241 Os << " is uncounted and unsafe.";
242
243 const SourceLocation SrcLocToReport =
244 isa<CXXDefaultArgExpr>(Val: CallArg) ? Param->getDefaultArg()->getExprLoc()
245 : CallArg->getSourceRange().getBegin();
246
247 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
248 auto Report = std::make_unique<BasicBugReport>(args: Bug, args: Os.str(), args&: BSLoc);
249 Report->addRange(CallArg->getSourceRange());
250 BR->emitReport(R: std::move(Report));
251 }
252
253 void reportBugOnThis(const Expr *CallArg) const {
254 assert(CallArg);
255
256 const SourceLocation SrcLocToReport = CallArg->getSourceRange().getBegin();
257
258 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
259 auto Report = std::make_unique<BasicBugReport>(
260 args: Bug, args: "Call argument for 'this' parameter is uncounted and unsafe.",
261 args&: BSLoc);
262 Report->addRange(CallArg->getSourceRange());
263 BR->emitReport(R: std::move(Report));
264 }
265};
266} // namespace
267
268void ento::registerUncountedCallArgsChecker(CheckerManager &Mgr) {
269 Mgr.registerChecker<UncountedCallArgsChecker>();
270}
271
272bool ento::shouldRegisterUncountedCallArgsChecker(const CheckerManager &) {
273 return true;
274}
275

source code of clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp