clang 22.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"
12#include "clang/AST/Decl.h"
13#include "clang/AST/DeclCXX.h"
21#include "llvm/Support/SaveAndRestore.h"
22#include <optional>
23
24using namespace clang;
25using namespace ento;
26
27namespace {
28
29class RawPtrRefCallArgsChecker
30 : public Checker<check::ASTDecl<TranslationUnitDecl>> {
31 BugType Bug;
32
33 TrivialFunctionAnalysis TFA;
34 EnsureFunctionAnalysis EFA;
35
36protected:
37 mutable BugReporter *BR;
38 mutable std::optional<RetainTypeChecker> RTC;
39
40public:
41 RawPtrRefCallArgsChecker(const char *description)
42 : Bug(this, description, "WebKit coding guidelines") {}
43
44 virtual std::optional<bool> isUnsafeType(QualType) const = 0;
45 virtual std::optional<bool> isUnsafePtr(QualType) const = 0;
46 virtual bool isSafePtr(const CXXRecordDecl *Record) const = 0;
47 virtual bool isSafePtrType(const QualType type) const = 0;
48 virtual bool isSafeExpr(const Expr *) const { return false; }
49 virtual bool isSafeDecl(const Decl *) const { return false; }
50 virtual const char *ptrKind() const = 0;
51
52 void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
53 BugReporter &BRArg) const {
54 BR = &BRArg;
55
56 // The calls to checkAST* from AnalysisConsumer don't
57 // visit template instantiations or lambda classes. We
58 // want to visit those, so we make our own RecursiveASTVisitor.
59 struct LocalVisitor : DynamicRecursiveASTVisitor {
60 const RawPtrRefCallArgsChecker *Checker;
61 Decl *DeclWithIssue{nullptr};
62
63 explicit LocalVisitor(const RawPtrRefCallArgsChecker *Checker)
64 : Checker(Checker) {
65 assert(Checker);
66 ShouldVisitTemplateInstantiations = true;
67 ShouldVisitImplicitCode = false;
68 }
69
70 bool TraverseClassTemplateDecl(ClassTemplateDecl *Decl) override {
72 return true;
73 return DynamicRecursiveASTVisitor::TraverseClassTemplateDecl(Decl);
74 }
75
76 bool TraverseDecl(Decl *D) override {
77 llvm::SaveAndRestore SavedDecl(DeclWithIssue);
78 if (D && (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)))
79 DeclWithIssue = D;
81 }
82
83 bool VisitCallExpr(CallExpr *CE) override {
84 Checker->visitCallExpr(CE, DeclWithIssue);
85 return true;
86 }
87
88 bool VisitTypedefDecl(TypedefDecl *TD) override {
89 if (Checker->RTC)
90 Checker->RTC->visitTypedef(TD);
91 return true;
92 }
93
94 bool VisitObjCMessageExpr(ObjCMessageExpr *ObjCMsgExpr) override {
95 Checker->visitObjCMessageExpr(ObjCMsgExpr, DeclWithIssue);
96 return true;
97 }
98 };
99
100 LocalVisitor visitor(this);
101 if (RTC)
102 RTC->visitTranslationUnitDecl(TUD);
103 visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
104 }
105
106 void visitCallExpr(const CallExpr *CE, const Decl *D) const {
107 if (shouldSkipCall(CE))
108 return;
109
110 if (auto *F = CE->getDirectCallee()) {
111 // Skip the first argument for overloaded member operators (e. g. lambda
112 // or std::function call operator).
113 unsigned ArgIdx =
114 isa<CXXOperatorCallExpr>(CE) && isa_and_nonnull<CXXMethodDecl>(F);
115
116 if (auto *MemberCallExpr = dyn_cast<CXXMemberCallExpr>(CE)) {
117 if (auto *MD = MemberCallExpr->getMethodDecl()) {
118 auto name = safeGetName(MD);
119 if (name == "ref" || name == "deref")
120 return;
121 if (name == "incrementCheckedPtrCount" ||
122 name == "decrementCheckedPtrCount")
123 return;
124 }
125 auto *E = MemberCallExpr->getImplicitObjectArgument();
126 QualType ArgType = MemberCallExpr->getObjectType().getCanonicalType();
127 std::optional<bool> IsUnsafe = isUnsafeType(ArgType);
128 if (IsUnsafe && *IsUnsafe && !isPtrOriginSafe(E))
129 reportBugOnThis(E, D);
130 }
131
132 for (auto P = F->param_begin();
133 // FIXME: Also check variadic function parameters.
134 // FIXME: Also check default function arguments. Probably a different
135 // checker. In case there are default arguments the call can have
136 // fewer arguments than the callee has parameters.
137 P < F->param_end() && ArgIdx < CE->getNumArgs(); ++P, ++ArgIdx) {
138 // TODO: attributes.
139 // if ((*P)->hasAttr<SafeRefCntblRawPtrAttr>())
140 // continue;
141
142 QualType ArgType = (*P)->getType();
143 // FIXME: more complex types (arrays, references to raw pointers, etc)
144 std::optional<bool> IsUncounted = isUnsafePtr(ArgType);
145 if (!IsUncounted || !(*IsUncounted))
146 continue;
147
148 const auto *Arg = CE->getArg(ArgIdx);
149
150 if (auto *defaultArg = dyn_cast<CXXDefaultArgExpr>(Arg))
151 Arg = defaultArg->getExpr();
152
153 if (isPtrOriginSafe(Arg))
154 continue;
155
156 reportBug(Arg, *P, D);
157 }
158 for (; ArgIdx < CE->getNumArgs(); ++ArgIdx) {
159 const auto *Arg = CE->getArg(ArgIdx);
160 auto ArgType = Arg->getType();
161 std::optional<bool> IsUncounted = isUnsafePtr(ArgType);
162 if (!IsUncounted || !(*IsUncounted))
163 continue;
164
165 if (auto *defaultArg = dyn_cast<CXXDefaultArgExpr>(Arg))
166 Arg = defaultArg->getExpr();
167
168 if (isPtrOriginSafe(Arg))
169 continue;
170
171 reportBug(Arg, nullptr, D);
172 }
173 }
174 }
175
176 void visitObjCMessageExpr(const ObjCMessageExpr *E, const Decl *D) const {
177 if (BR->getSourceManager().isInSystemHeader(E->getExprLoc()))
178 return;
179
180 auto Selector = E->getSelector();
181 if (auto *Receiver = E->getInstanceReceiver()) {
182 std::optional<bool> IsUnsafe = isUnsafePtr(E->getReceiverType());
183 if (IsUnsafe && *IsUnsafe && !isPtrOriginSafe(Receiver)) {
184 if (auto *InnerMsg = dyn_cast<ObjCMessageExpr>(Receiver)) {
185 auto InnerSelector = InnerMsg->getSelector();
186 if (InnerSelector.getNameForSlot(0) == "alloc" &&
187 Selector.getNameForSlot(0).starts_with("init"))
188 return;
189 }
190 reportBugOnReceiver(Receiver, D);
191 }
192 }
193
194 auto *MethodDecl = E->getMethodDecl();
195 if (!MethodDecl)
196 return;
197
198 auto ArgCount = E->getNumArgs();
199 for (unsigned i = 0; i < ArgCount; ++i) {
200 auto *Arg = E->getArg(i);
201 bool hasParam = i < MethodDecl->param_size();
202 auto *Param = hasParam ? MethodDecl->getParamDecl(i) : nullptr;
203 auto ArgType = Arg->getType();
204 std::optional<bool> IsUnsafe = isUnsafePtr(ArgType);
205 if (!IsUnsafe || !(*IsUnsafe))
206 continue;
207 if (isPtrOriginSafe(Arg))
208 continue;
209 reportBug(Arg, Param, D);
210 }
211 }
212
213 bool isPtrOriginSafe(const Expr *Arg) const {
214 return tryToFindPtrOrigin(
215 Arg, /*StopAtFirstRefCountedObj=*/true,
216 [&](const clang::CXXRecordDecl *Record) { return isSafePtr(Record); },
217 [&](const clang::QualType T) { return isSafePtrType(T); },
218 [&](const clang::Decl *D) { return isSafeDecl(D); },
219 [&](const clang::Expr *ArgOrigin, bool IsSafe) {
220 if (IsSafe)
221 return true;
222 if (isNullPtr(ArgOrigin))
223 return true;
224 if (isa<IntegerLiteral>(ArgOrigin)) {
225 // FIXME: Check the value.
226 // foo(123)
227 return true;
228 }
229 if (isa<CXXBoolLiteralExpr>(ArgOrigin))
230 return true;
231 if (isa<ObjCStringLiteral>(ArgOrigin))
232 return true;
233 if (isASafeCallArg(ArgOrigin))
234 return true;
235 if (EFA.isACallToEnsureFn(ArgOrigin))
236 return true;
237 if (isSafeExpr(ArgOrigin))
238 return true;
239 return false;
240 });
241 }
242
243 bool shouldSkipCall(const CallExpr *CE) const {
244 const auto *Callee = CE->getDirectCallee();
245
246 if (BR->getSourceManager().isInSystemHeader(CE->getExprLoc()))
247 return true;
248
249 if (Callee && TFA.isTrivial(Callee) && !Callee->isVirtualAsWritten())
250 return true;
251
252 if (isTrivialBuiltinFunction(Callee))
253 return true;
254
255 if (CE->getNumArgs() == 0)
256 return false;
257
258 // If an assignment is problematic we should warn about the sole existence
259 // of object on LHS.
260 if (auto *MemberOp = dyn_cast<CXXOperatorCallExpr>(CE)) {
261 // Note: assignemnt to built-in type isn't derived from CallExpr.
262 if (MemberOp->getOperator() ==
263 OO_Equal) { // Ignore assignment to Ref/RefPtr.
264 auto *callee = MemberOp->getDirectCallee();
265 if (auto *calleeDecl = dyn_cast<CXXMethodDecl>(callee)) {
266 if (const CXXRecordDecl *classDecl = calleeDecl->getParent()) {
267 if (isSafePtr(classDecl))
268 return true;
269 }
270 }
271 }
272 if (MemberOp->isAssignmentOp())
273 return false;
274 }
275
276 if (!Callee)
277 return false;
278
279 if (isMethodOnWTFContainerType(Callee))
280 return true;
281
282 auto overloadedOperatorType = Callee->getOverloadedOperator();
283 if (overloadedOperatorType == OO_EqualEqual ||
284 overloadedOperatorType == OO_ExclaimEqual ||
285 overloadedOperatorType == OO_LessEqual ||
286 overloadedOperatorType == OO_GreaterEqual ||
287 overloadedOperatorType == OO_Spaceship ||
288 overloadedOperatorType == OO_AmpAmp ||
289 overloadedOperatorType == OO_PipePipe)
290 return true;
291
292 if (isCtorOfSafePtr(Callee) || isPtrConversion(Callee))
293 return true;
294
295 auto name = safeGetName(Callee);
296 if (name == "adoptRef" || name == "getPtr" || name == "WeakPtr" ||
297 name == "is" || name == "equal" || name == "hash" || name == "isType" ||
298 // FIXME: Most/all of these should be implemented via attributes.
299 name == "CFEqual" || name == "equalIgnoringASCIICase" ||
300 name == "equalIgnoringASCIICaseCommon" ||
301 name == "equalIgnoringNullity" || name == "toString")
302 return true;
303
304 return false;
305 }
306
307 bool isMethodOnWTFContainerType(const FunctionDecl *Decl) const {
308 if (!isa<CXXMethodDecl>(Decl))
309 return false;
310 auto *ClassDecl = Decl->getParent();
311 if (!ClassDecl || !isa<CXXRecordDecl>(ClassDecl))
312 return false;
313
314 auto *NsDecl = ClassDecl->getParent();
315 if (!NsDecl || !isa<NamespaceDecl>(NsDecl))
316 return false;
317
318 auto MethodName = safeGetName(Decl);
319 auto ClsNameStr = safeGetName(ClassDecl);
320 StringRef ClsName = ClsNameStr; // FIXME: Make safeGetName return StringRef.
321 auto NamespaceName = safeGetName(NsDecl);
322 // FIXME: These should be implemented via attributes.
323 return NamespaceName == "WTF" &&
324 (MethodName == "find" || MethodName == "findIf" ||
325 MethodName == "reverseFind" || MethodName == "reverseFindIf" ||
326 MethodName == "findIgnoringASCIICase" || MethodName == "get" ||
327 MethodName == "inlineGet" || MethodName == "contains" ||
328 MethodName == "containsIf" ||
329 MethodName == "containsIgnoringASCIICase" ||
330 MethodName == "startsWith" || MethodName == "endsWith" ||
331 MethodName == "startsWithIgnoringASCIICase" ||
332 MethodName == "endsWithIgnoringASCIICase" ||
333 MethodName == "substring") &&
334 (ClsName.ends_with("Vector") || ClsName.ends_with("Set") ||
335 ClsName.ends_with("Map") || ClsName == "StringImpl" ||
336 ClsName.ends_with("String"));
337 }
338
339 void reportBug(const Expr *CallArg, const ParmVarDecl *Param,
340 const Decl *DeclWithIssue) const {
341 assert(CallArg);
342
343 SmallString<100> Buf;
344 llvm::raw_svector_ostream Os(Buf);
345
346 const std::string paramName = safeGetName(Param);
347 Os << "Call argument";
348 if (!paramName.empty()) {
349 Os << " for parameter ";
350 printQuotedQualifiedName(Os, Param);
351 }
352 Os << " is " << ptrKind() << " and unsafe.";
353
354 bool usesDefaultArgValue = isa<CXXDefaultArgExpr>(CallArg) && Param;
355 const SourceLocation SrcLocToReport =
356 usesDefaultArgValue ? Param->getDefaultArg()->getExprLoc()
357 : CallArg->getSourceRange().getBegin();
358
359 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
360 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
361 Report->addRange(CallArg->getSourceRange());
362 Report->setDeclWithIssue(DeclWithIssue);
363 BR->emitReport(std::move(Report));
364 }
365
366 void reportBugOnThis(const Expr *CallArg, const Decl *DeclWithIssue) const {
367 assert(CallArg);
368
369 const SourceLocation SrcLocToReport = CallArg->getSourceRange().getBegin();
370
371 SmallString<100> Buf;
372 llvm::raw_svector_ostream Os(Buf);
373 Os << "Call argument for 'this' parameter is " << ptrKind();
374 Os << " and unsafe.";
375
376 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
377 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
378 Report->addRange(CallArg->getSourceRange());
379 Report->setDeclWithIssue(DeclWithIssue);
380 BR->emitReport(std::move(Report));
381 }
382
383 void reportBugOnReceiver(const Expr *CallArg,
384 const Decl *DeclWithIssue) const {
385 assert(CallArg);
386
387 const SourceLocation SrcLocToReport = CallArg->getSourceRange().getBegin();
388
389 SmallString<100> Buf;
390 llvm::raw_svector_ostream Os(Buf);
391 Os << "Receiver is " << ptrKind() << " and unsafe.";
392
393 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
394 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
395 Report->addRange(CallArg->getSourceRange());
396 Report->setDeclWithIssue(DeclWithIssue);
397 BR->emitReport(std::move(Report));
398 }
399};
400
401class UncountedCallArgsChecker final : public RawPtrRefCallArgsChecker {
402public:
403 UncountedCallArgsChecker()
404 : RawPtrRefCallArgsChecker("Uncounted call argument for a raw "
405 "pointer/reference parameter") {}
406
407 std::optional<bool> isUnsafeType(QualType QT) const final {
408 return isUncounted(QT);
409 }
410
411 std::optional<bool> isUnsafePtr(QualType QT) const final {
412 return isUncountedPtr(QT.getCanonicalType());
413 }
414
415 bool isSafePtr(const CXXRecordDecl *Record) const final {
417 }
418
419 bool isSafePtrType(const QualType type) const final {
421 }
422
423 const char *ptrKind() const final { return "uncounted"; }
424};
425
426class UncheckedCallArgsChecker final : public RawPtrRefCallArgsChecker {
427public:
428 UncheckedCallArgsChecker()
429 : RawPtrRefCallArgsChecker("Unchecked call argument for a raw "
430 "pointer/reference parameter") {}
431
432 std::optional<bool> isUnsafeType(QualType QT) const final {
433 return isUnchecked(QT);
434 }
435
436 std::optional<bool> isUnsafePtr(QualType QT) const final {
437 return isUncheckedPtr(QT.getCanonicalType());
438 }
439
440 bool isSafePtr(const CXXRecordDecl *Record) const final {
442 }
443
444 bool isSafePtrType(const QualType type) const final {
446 }
447
448 bool isSafeExpr(const Expr *E) const final {
450 }
451
452 const char *ptrKind() const final { return "unchecked"; }
453};
454
455class UnretainedCallArgsChecker final : public RawPtrRefCallArgsChecker {
456public:
457 UnretainedCallArgsChecker()
458 : RawPtrRefCallArgsChecker("Unretained call argument for a raw "
459 "pointer/reference parameter") {
460 RTC = RetainTypeChecker();
461 }
462
463 std::optional<bool> isUnsafeType(QualType QT) const final {
464 return RTC->isUnretained(QT);
465 }
466
467 std::optional<bool> isUnsafePtr(QualType QT) const final {
468 return RTC->isUnretained(QT);
469 }
470
471 bool isSafePtr(const CXXRecordDecl *Record) const final {
473 }
474
475 bool isSafePtrType(const QualType type) const final {
477 }
478
479 bool isSafeExpr(const Expr *E) const final {
482 }
483
484 bool isSafeDecl(const Decl *D) const final {
485 // Treat NS/CF globals in system header as immortal.
487 }
488
489 const char *ptrKind() const final { return "unretained"; }
490};
491
492} // namespace
493
494void ento::registerUncountedCallArgsChecker(CheckerManager &Mgr) {
495 Mgr.registerChecker<UncountedCallArgsChecker>();
496}
497
498bool ento::shouldRegisterUncountedCallArgsChecker(const CheckerManager &) {
499 return true;
500}
501
502void ento::registerUncheckedCallArgsChecker(CheckerManager &Mgr) {
503 Mgr.registerChecker<UncheckedCallArgsChecker>();
504}
505
506bool ento::shouldRegisterUncheckedCallArgsChecker(const CheckerManager &) {
507 return true;
508}
509
510void ento::registerUnretainedCallArgsChecker(CheckerManager &Mgr) {
511 Mgr.registerChecker<UnretainedCallArgsChecker>();
512}
513
514bool ento::shouldRegisterUnretainedCallArgsChecker(const CheckerManager &) {
515 return true;
516}
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....
llvm::MachO::Record Record
Definition MachO.h:31
Defines the clang::SourceLocation class and associated facilities.
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Definition Expr.h:3081
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return null.
Definition Expr.h:3060
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
Definition Expr.h:3068
SourceLocation getLocation() const
Definition DeclBase.h:439
virtual bool TraverseDecl(MaybeConst< Decl > *D)
SourceLocation getExprLoc() const LLVM_READONLY
getExprLoc - Return the preferred location for the arrow when diagnosing a problem with a generic exp...
Definition Expr.cpp:273
QualType getType() const
Definition Expr.h:144
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Definition ExprObjC.h:1403
Expr * getInstanceReceiver()
Returns the object expression (receiver) for an instance message, or null for a message that is not a...
Definition ExprObjC.h:1268
Selector getSelector() const
Definition ExprObjC.cpp:289
const ObjCMethodDecl * getMethodDecl() const
Definition ExprObjC.h:1364
QualType getReceiverType() const
Retrieve the receiver type to which this message is being directed.
Definition ExprObjC.cpp:296
unsigned getNumArgs() const
Return the number of actual arguments in this message, not counting the receiver.
Definition ExprObjC.h:1390
Expr * getDefaultArg()
Definition Decl.cpp:2999
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:338
const SourceManager & getSourceManager()
CHECKER * registerChecker(AT &&...Args)
Register a single-part checker (derived from Checker): construct its singleton instance,...
Simple checker classes that implement one frontend (i.e.
Definition Checker.h:553
const internal::VariadicAllOfMatcher< Type > type
Matches Types in the clang AST.
bool isCocoaObjectRef(QualType T)
std::variant< struct RequiresDecl, struct HeaderDecl, struct UmbrellaDirDecl, struct ModuleDecl, struct ExcludeDecl, struct ExportDecl, struct ExportAsDecl, struct ExternModuleDecl, struct UseDecl, struct LinkDecl, struct ConfigMacrosDecl, struct ConflictDecl > Decl
All declarations that can appear in a module declaration.
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.
bool isCtorOfSafePtr(const clang::FunctionDecl *F)
bool isTrivialBuiltinFunction(const FunctionDecl *F)
bool isa(CodeGen::Address addr)
Definition Address.h:330
bool isExprToGetCheckedPtrCapableMember(const clang::Expr *E)
Definition ASTUtils.cpp:304
bool isPtrConversion(const FunctionDecl *F)
std::optional< bool > isUnchecked(const QualType T)
bool isRefOrCheckedPtrType(const clang::QualType T)
void printQuotedQualifiedName(llvm::raw_ostream &Os, const NamedDeclDerivedT &D)
bool isRetainPtrOrOSPtrType(const clang::QualType T)
bool tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj, std::function< bool(const clang::CXXRecordDecl *)> isSafePtr, std::function< bool(const clang::QualType)> isSafePtrType, std::function< bool(const clang::Decl *)> isSafeGlobalDecl, 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::optional< bool > isUnsafePtr(const QualType T, bool IsArcEnabled)
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:240
const FunctionProtoType * T
bool isSmartPtrClass(const std::string &Name)
bool isRefCounted(const CXXRecordDecl *R)
bool isRetainPtrOrOSPtr(const std::string &Name)
std::optional< bool > isUncountedPtr(const QualType T)
bool isSafePtr(clang::CXXRecordDecl *Decl)
Definition ASTUtils.cpp:21
std::string safeGetName(const T *ASTNode)
Definition ASTUtils.h:91
bool isNullPtr(const clang::Expr *E)
Definition ASTUtils.cpp:270
bool isCheckedPtr(const std::string &Name)
DynamicRecursiveASTVisitorBase< false > DynamicRecursiveASTVisitor
std::optional< bool > isUncounted(const QualType T)
std::optional< bool > isUncheckedPtr(const QualType T)