clang 20.0.0git
RawPtrRefCallArgsChecker.cpp
Go to the documentation of this file.
1//=======- RawPtrRefCallArgsChecker.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"
13#include "clang/AST/Decl.h"
14#include "clang/AST/DeclCXX.h"
21#include "llvm/ADT/DenseSet.h"
22#include "llvm/Support/SaveAndRestore.h"
23#include <optional>
24
25using namespace clang;
26using namespace ento;
27
28namespace {
29
30class RawPtrRefCallArgsChecker
31 : public Checker<check::ASTDecl<TranslationUnitDecl>> {
32 BugType Bug;
33 mutable BugReporter *BR;
34
37
38public:
39 RawPtrRefCallArgsChecker(const char *description)
40 : Bug(this, description, "WebKit coding guidelines") {}
41
42 virtual std::optional<bool> isUnsafeType(QualType) const = 0;
43 virtual std::optional<bool> isUnsafePtr(QualType) const = 0;
44 virtual const char *ptrKind() const = 0;
45
46 void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
47 BugReporter &BRArg) const {
48 BR = &BRArg;
49
50 // The calls to checkAST* from AnalysisConsumer don't
51 // visit template instantiations or lambda classes. We
52 // want to visit those, so we make our own RecursiveASTVisitor.
53 struct LocalVisitor : DynamicRecursiveASTVisitor {
54 const RawPtrRefCallArgsChecker *Checker;
55 Decl *DeclWithIssue{nullptr};
56
57 explicit LocalVisitor(const RawPtrRefCallArgsChecker *Checker)
58 : Checker(Checker) {
59 assert(Checker);
60 ShouldVisitTemplateInstantiations = true;
61 ShouldVisitImplicitCode = false;
62 }
63
64 bool TraverseClassTemplateDecl(ClassTemplateDecl *Decl) override {
66 return true;
67 return DynamicRecursiveASTVisitor::TraverseClassTemplateDecl(Decl);
68 }
69
70 bool TraverseDecl(Decl *D) override {
71 llvm::SaveAndRestore SavedDecl(DeclWithIssue);
72 if (D && (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)))
73 DeclWithIssue = D;
75 }
76
77 bool VisitCallExpr(CallExpr *CE) override {
78 Checker->visitCallExpr(CE, DeclWithIssue);
79 return true;
80 }
81 };
82
83 LocalVisitor visitor(this);
84 visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
85 }
86
87 void visitCallExpr(const CallExpr *CE, const Decl *D) const {
88 if (shouldSkipCall(CE))
89 return;
90
91 if (auto *F = CE->getDirectCallee()) {
92 // Skip the first argument for overloaded member operators (e. g. lambda
93 // or std::function call operator).
94 unsigned ArgIdx =
95 isa<CXXOperatorCallExpr>(CE) && isa_and_nonnull<CXXMethodDecl>(F);
96
97 if (auto *MemberCallExpr = dyn_cast<CXXMemberCallExpr>(CE)) {
98 if (auto *MD = MemberCallExpr->getMethodDecl()) {
99 auto name = safeGetName(MD);
100 if (name == "ref" || name == "deref")
101 return;
102 if (name == "incrementCheckedPtrCount" ||
103 name == "decrementCheckedPtrCount")
104 return;
105 }
106 auto *E = MemberCallExpr->getImplicitObjectArgument();
107 QualType ArgType = MemberCallExpr->getObjectType().getCanonicalType();
108 std::optional<bool> IsUnsafe = isUnsafeType(ArgType);
109 if (IsUnsafe && *IsUnsafe && !isPtrOriginSafe(E))
110 reportBugOnThis(E, D);
111 }
112
113 for (auto P = F->param_begin();
114 // FIXME: Also check variadic function parameters.
115 // FIXME: Also check default function arguments. Probably a different
116 // checker. In case there are default arguments the call can have
117 // fewer arguments than the callee has parameters.
118 P < F->param_end() && ArgIdx < CE->getNumArgs(); ++P, ++ArgIdx) {
119 // TODO: attributes.
120 // if ((*P)->hasAttr<SafeRefCntblRawPtrAttr>())
121 // continue;
122
123 QualType ArgType = (*P)->getType().getCanonicalType();
124 // FIXME: more complex types (arrays, references to raw pointers, etc)
125 std::optional<bool> IsUncounted = isUnsafePtr(ArgType);
126 if (!IsUncounted || !(*IsUncounted))
127 continue;
128
129 const auto *Arg = CE->getArg(ArgIdx);
130
131 if (auto *defaultArg = dyn_cast<CXXDefaultArgExpr>(Arg))
132 Arg = defaultArg->getExpr();
133
134 if (isPtrOriginSafe(Arg))
135 continue;
136
137 reportBug(Arg, *P, D);
138 }
139 }
140 }
141
142 bool isPtrOriginSafe(const Expr *Arg) const {
143 return tryToFindPtrOrigin(Arg, /*StopAtFirstRefCountedObj=*/true,
144 [&](const clang::Expr *ArgOrigin, bool IsSafe) {
145 if (IsSafe)
146 return true;
147 if (isa<CXXNullPtrLiteralExpr>(ArgOrigin)) {
148 // foo(nullptr)
149 return true;
150 }
151 if (isa<IntegerLiteral>(ArgOrigin)) {
152 // FIXME: Check the value.
153 // foo(NULL)
154 return true;
155 }
156 if (isASafeCallArg(ArgOrigin))
157 return true;
158 if (EFA.isACallToEnsureFn(ArgOrigin))
159 return true;
160 return false;
161 });
162 }
163
164 bool shouldSkipCall(const CallExpr *CE) const {
165 const auto *Callee = CE->getDirectCallee();
166
168 return true;
169
170 if (Callee && TFA.isTrivial(Callee))
171 return true;
172
173 if (CE->getNumArgs() == 0)
174 return false;
175
176 // If an assignment is problematic we should warn about the sole existence
177 // of object on LHS.
178 if (auto *MemberOp = dyn_cast<CXXOperatorCallExpr>(CE)) {
179 // Note: assignemnt to built-in type isn't derived from CallExpr.
180 if (MemberOp->getOperator() ==
181 OO_Equal) { // Ignore assignment to Ref/RefPtr.
182 auto *callee = MemberOp->getDirectCallee();
183 if (auto *calleeDecl = dyn_cast<CXXMethodDecl>(callee)) {
184 if (const CXXRecordDecl *classDecl = calleeDecl->getParent()) {
185 if (isRefCounted(classDecl))
186 return true;
187 }
188 }
189 }
190 if (MemberOp->isAssignmentOp())
191 return false;
192 }
193
194 if (!Callee)
195 return false;
196
197 if (isMethodOnWTFContainerType(Callee))
198 return true;
199
200 auto overloadedOperatorType = Callee->getOverloadedOperator();
201 if (overloadedOperatorType == OO_EqualEqual ||
202 overloadedOperatorType == OO_ExclaimEqual ||
203 overloadedOperatorType == OO_LessEqual ||
204 overloadedOperatorType == OO_GreaterEqual ||
205 overloadedOperatorType == OO_Spaceship ||
206 overloadedOperatorType == OO_AmpAmp ||
207 overloadedOperatorType == OO_PipePipe)
208 return true;
209
210 if (isCtorOfRefCounted(Callee))
211 return true;
212
213 auto name = safeGetName(Callee);
214 if (name == "adoptRef" || name == "getPtr" || name == "WeakPtr" ||
215 name == "dynamicDowncast" || name == "downcast" ||
216 name == "checkedDowncast" || name == "uncheckedDowncast" ||
217 name == "bitwise_cast" || name == "is" || name == "equal" ||
218 name == "hash" || name == "isType" ||
219 // FIXME: Most/all of these should be implemented via attributes.
220 name == "equalIgnoringASCIICase" ||
221 name == "equalIgnoringASCIICaseCommon" ||
222 name == "equalIgnoringNullity" || name == "toString")
223 return true;
224
225 return false;
226 }
227
228 bool isMethodOnWTFContainerType(const FunctionDecl *Decl) const {
229 if (!isa<CXXMethodDecl>(Decl))
230 return false;
231 auto *ClassDecl = Decl->getParent();
232 if (!ClassDecl || !isa<CXXRecordDecl>(ClassDecl))
233 return false;
234
235 auto *NsDecl = ClassDecl->getParent();
236 if (!NsDecl || !isa<NamespaceDecl>(NsDecl))
237 return false;
238
239 auto MethodName = safeGetName(Decl);
240 auto ClsNameStr = safeGetName(ClassDecl);
241 StringRef ClsName = ClsNameStr; // FIXME: Make safeGetName return StringRef.
242 auto NamespaceName = safeGetName(NsDecl);
243 // FIXME: These should be implemented via attributes.
244 return NamespaceName == "WTF" &&
245 (MethodName == "find" || MethodName == "findIf" ||
246 MethodName == "reverseFind" || MethodName == "reverseFindIf" ||
247 MethodName == "findIgnoringASCIICase" || MethodName == "get" ||
248 MethodName == "inlineGet" || MethodName == "contains" ||
249 MethodName == "containsIf" ||
250 MethodName == "containsIgnoringASCIICase" ||
251 MethodName == "startsWith" || MethodName == "endsWith" ||
252 MethodName == "startsWithIgnoringASCIICase" ||
253 MethodName == "endsWithIgnoringASCIICase" ||
254 MethodName == "substring") &&
255 (ClsName.ends_with("Vector") || ClsName.ends_with("Set") ||
256 ClsName.ends_with("Map") || ClsName == "StringImpl" ||
257 ClsName.ends_with("String"));
258 }
259
260 void reportBug(const Expr *CallArg, const ParmVarDecl *Param,
261 const Decl *DeclWithIssue) const {
262 assert(CallArg);
263
265 llvm::raw_svector_ostream Os(Buf);
266
267 const std::string paramName = safeGetName(Param);
268 Os << "Call argument";
269 if (!paramName.empty()) {
270 Os << " for parameter ";
271 printQuotedQualifiedName(Os, Param);
272 }
273 Os << " is " << ptrKind() << " and unsafe.";
274
275 const SourceLocation SrcLocToReport =
276 isa<CXXDefaultArgExpr>(CallArg) ? Param->getDefaultArg()->getExprLoc()
277 : CallArg->getSourceRange().getBegin();
278
279 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
280 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
281 Report->addRange(CallArg->getSourceRange());
282 Report->setDeclWithIssue(DeclWithIssue);
283 BR->emitReport(std::move(Report));
284 }
285
286 void reportBugOnThis(const Expr *CallArg, const Decl *DeclWithIssue) const {
287 assert(CallArg);
288
289 const SourceLocation SrcLocToReport = CallArg->getSourceRange().getBegin();
290
292 llvm::raw_svector_ostream Os(Buf);
293 Os << "Call argument for 'this' parameter is " << ptrKind();
294 Os << " and unsafe.";
295
296 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
297 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
298 Report->addRange(CallArg->getSourceRange());
299 Report->setDeclWithIssue(DeclWithIssue);
300 BR->emitReport(std::move(Report));
301 }
302};
303
304class UncountedCallArgsChecker final : public RawPtrRefCallArgsChecker {
305public:
306 UncountedCallArgsChecker()
307 : RawPtrRefCallArgsChecker("Uncounted call argument for a raw "
308 "pointer/reference parameter") {}
309
310 std::optional<bool> isUnsafeType(QualType QT) const final {
311 return isUncounted(QT);
312 }
313
314 std::optional<bool> isUnsafePtr(QualType QT) const final {
315 return isUncountedPtr(QT);
316 }
317
318 const char *ptrKind() const final { return "uncounted"; }
319};
320
321class UncheckedCallArgsChecker final : public RawPtrRefCallArgsChecker {
322public:
323 UncheckedCallArgsChecker()
324 : RawPtrRefCallArgsChecker("Unchecked call argument for a raw "
325 "pointer/reference parameter") {}
326
327 std::optional<bool> isUnsafeType(QualType QT) const final {
328 return isUnchecked(QT);
329 }
330
331 std::optional<bool> isUnsafePtr(QualType QT) const final {
332 return isUncheckedPtr(QT);
333 }
334
335 const char *ptrKind() const final { return "unchecked"; }
336};
337
338} // namespace
339
340void ento::registerUncountedCallArgsChecker(CheckerManager &Mgr) {
341 Mgr.registerChecker<UncountedCallArgsChecker>();
342}
343
344bool ento::shouldRegisterUncountedCallArgsChecker(const CheckerManager &) {
345 return true;
346}
347
348void ento::registerUncheckedCallArgsChecker(CheckerManager &Mgr) {
349 Mgr.registerChecker<UncheckedCallArgsChecker>();
350}
351
352bool ento::shouldRegisterUncheckedCallArgsChecker(const CheckerManager &) {
353 return true;
354}
StringRef P
const Decl * D
Expr * E
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....
Defines the clang::SourceLocation class and associated facilities.
Represents a C++ struct/union/class.
Definition: DeclCXX.h:258
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition: Expr.h:2874
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Definition: Expr.h:3068
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return null.
Definition: Expr.h:3047
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
Definition: Expr.h:3055
Declaration of a class template.
DeclContext * getParent()
getParent - Returns the containing DeclContext.
Definition: DeclBase.h:2089
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:86
Recursive AST visitor that supports extension via dynamic dispatch.
virtual bool TraverseDecl(Decl *D)
Recursively visit a declaration, by dispatching to Traverse*Decl() based on the argument's dynamic ty...
bool isACallToEnsureFn(const Expr *E) const
Definition: ASTUtils.cpp:198
This represents one expression.
Definition: Expr.h:110
SourceLocation getExprLoc() const LLVM_READONLY
getExprLoc - Return the preferred location for the arrow when diagnosing a problem with a generic exp...
Definition: Expr.cpp:277
Represents a function declaration or definition.
Definition: Decl.h:1935
Represents a parameter to a function.
Definition: Decl.h:1725
Expr * getDefaultArg()
Definition: Decl.cpp:2975
A (possibly-)qualified type.
Definition: Type.h:929
Encodes a location in the source.
bool isInSystemHeader(SourceLocation Loc) const
Returns if a SourceLocation is in a system header.
SourceLocation getBegin() const
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition: Stmt.cpp:324
The top declaration context.
Definition: Decl.h:84
An inter-procedural analysis facility that detects functions with "trivial" behavior with respect to ...
bool isTrivial(const Decl *D) const
BugReporter is a utility class for generating PathDiagnostics for analysis.
Definition: BugReporter.h:585
const SourceManager & getSourceManager()
Definition: BugReporter.h:623
virtual void emitReport(std::unique_ptr< BugReport > R)
Add the given report to the set of reports tracked by BugReporter.
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
RangeSelector name(std::string ID)
Given a node with a "name", (like NamedDecl, DeclRefExpr, CxxCtorInitializer, and TypeLoc) selects th...
The JSON file list parser is used to communicate input to InstallAPI.
std::optional< bool > isUnchecked(const QualType T)
bool isCtorOfRefCounted(const clang::FunctionDecl *F)
void printQuotedQualifiedName(llvm::raw_ostream &Os, const NamedDeclDerivedT &D)
bool isASafeCallArg(const Expr *E)
For E referring to a ref-countable/-counted pointer/reference we return whether it's a safe call argu...
Definition: ASTUtils.cpp:138
bool isRefCounted(const CXXRecordDecl *R)
bool isRefType(const std::string &Name)
std::optional< bool > isUncountedPtr(const QualType T)
bool tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj, std::function< bool(const clang::Expr *, bool)> callback)
This function de-facto defines a set of transformations that we consider safe (in heuristical sense).
Definition: ASTUtils.cpp:25
std::string safeGetName(const T *ASTNode)
Definition: ASTUtils.h:81
std::optional< bool > isUnsafePtr(const QualType T)
std::optional< bool > isUncounted(const QualType T)
std::optional< bool > isUncheckedPtr(const QualType T)