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"
14#include "clang/AST/DynamicRecursiveASTVisitor.h"
15#include "clang/Analysis/DomainSpecific/CocoaConventions.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/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 {
71 if (isSmartPtrClass(Name: safeGetName(ASTNode: Decl)))
72 return true;
73 return DynamicRecursiveASTVisitor::TraverseClassTemplateDecl(D: Decl);
74 }
75
76 bool TraverseDecl(Decl *D) override {
77 llvm::SaveAndRestore SavedDecl(DeclWithIssue);
78 if (D && (isa<FunctionDecl>(Val: D) || isa<ObjCMethodDecl>(Val: D)))
79 DeclWithIssue = D;
80 return DynamicRecursiveASTVisitor::TraverseDecl(D);
81 }
82
83 bool VisitCallExpr(CallExpr *CE) override {
84 Checker->visitCallExpr(CE, D: DeclWithIssue);
85 return true;
86 }
87
88 bool VisitCXXConstructExpr(CXXConstructExpr *CE) override {
89 Checker->visitConstructExpr(CE, D: DeclWithIssue);
90 return true;
91 }
92
93 bool VisitTypedefDecl(TypedefDecl *TD) override {
94 if (Checker->RTC)
95 Checker->RTC->visitTypedef(TD);
96 return true;
97 }
98
99 bool VisitObjCMessageExpr(ObjCMessageExpr *ObjCMsgExpr) override {
100 Checker->visitObjCMessageExpr(E: ObjCMsgExpr, D: DeclWithIssue);
101 return true;
102 }
103 };
104
105 LocalVisitor visitor(this);
106 if (RTC)
107 RTC->visitTranslationUnitDecl(TUD);
108 visitor.TraverseDecl(D: const_cast<TranslationUnitDecl *>(TUD));
109 }
110
111 template <typename CallOrConstrcut>
112 void visitCallOrConstructExpr(const CallOrConstrcut *CE,
113 const FunctionDecl *F, const Decl *D) const {
114 if (F) {
115 // Skip the first argument for overloaded member operators (e. g. lambda
116 // or std::function call operator).
117 unsigned ArgIdx =
118 isa<CXXOperatorCallExpr>(CE) && isa_and_nonnull<CXXMethodDecl>(Val: F);
119
120 if (auto *MemberCallExpr = dyn_cast<CXXMemberCallExpr>(CE))
121 checkThisArg(MemberCallExpr, DeclWithIssue: D);
122
123 if (ArgIdx) {
124 auto *Arg = CE->getArg(0);
125 QualType ArgType = Arg->getType().getCanonicalType();
126 std::optional<bool> IsUnsafe = isUnsafeType(ArgType);
127 if (IsUnsafe && *IsUnsafe && !isPtrOriginSafe(Arg))
128 reportBugOnThis(CallArg: Arg, DeclWithIssue: D);
129 }
130
131 for (auto P = F->param_begin();
132 P < F->param_end() && ArgIdx < CE->getNumArgs(); ++P, ++ArgIdx) {
133 // TODO: attributes.
134 // if ((*P)->hasAttr<SafeRefCntblRawPtrAttr>())
135 // continue;
136 checkArg(Arg: CE->getArg(ArgIdx), ParamType: (*P)->getType(), Param: *P, DeclWithIssue: D);
137 }
138 for (; ArgIdx < CE->getNumArgs(); ++ArgIdx) {
139 auto *Arg = CE->getArg(ArgIdx);
140 checkArg(Arg, ParamType: Arg->getType(), Param: nullptr, DeclWithIssue: D);
141 }
142 }
143 }
144
145 void visitCallExpr(const CallExpr *CE, const Decl *D) const {
146 auto *Callee = CE->getDirectCallee();
147 if (shouldSkipCall(CE, Callee))
148 return;
149
150 if (Callee)
151 visitCallOrConstructExpr(CE, F: Callee, D);
152 else if (auto *Decl = CE->getCalleeDecl()) {
153 if (auto *FnType = Decl->getFunctionType()) {
154 if (auto *ProtoType = dyn_cast<FunctionProtoType>(Val: FnType)) {
155 if (auto *MemberCallExpr = dyn_cast<CXXMemberCallExpr>(Val: CE))
156 checkThisArg(MemberCallExpr, DeclWithIssue: D);
157 unsigned ArgIdx = 0;
158 for (auto PT = ProtoType->param_type_begin();
159 PT < ProtoType->param_type_end() && ArgIdx < CE->getNumArgs();
160 ++PT, ++ArgIdx)
161 checkArg(Arg: CE->getArg(Arg: ArgIdx), ParamType: *PT, Param: nullptr, DeclWithIssue: D);
162 for (; ArgIdx < CE->getNumArgs(); ++ArgIdx) {
163 auto *Arg = CE->getArg(Arg: ArgIdx);
164 checkArg(Arg, ParamType: Arg->getType(), Param: nullptr, DeclWithIssue: D);
165 }
166 }
167 }
168 }
169 }
170
171 void visitConstructExpr(const CXXConstructExpr *CE, const Decl *D) const {
172 auto *Constructor = CE->getConstructor();
173 if (shouldSkipCall(CE, Callee: Constructor))
174 return;
175 if (Constructor)
176 visitCallOrConstructExpr(CE, F: Constructor, D);
177 }
178
179 void visitObjCMessageExpr(const ObjCMessageExpr *E, const Decl *D) const {
180 if (BR->getSourceManager().isInSystemHeader(Loc: E->getExprLoc()))
181 return;
182
183 if (auto *Receiver = E->getInstanceReceiver()) {
184 std::optional<bool> IsUnsafe = isUnsafePtr(E->getReceiverType());
185 if (IsUnsafe && *IsUnsafe && !isPtrOriginSafe(Arg: Receiver)) {
186 if (isAllocInit(E))
187 return;
188 auto SelectorName = E->getSelector().getNameForSlot(argIndex: 0);
189 if (SelectorName == "isEqual" || SelectorName == "isEqualToString")
190 return;
191 reportBugOnReceiver(CallArg: Receiver, DeclWithIssue: D);
192 }
193 }
194
195 auto *MethodDecl = E->getMethodDecl();
196 if (!MethodDecl)
197 return;
198
199 auto ArgCount = E->getNumArgs();
200 for (unsigned i = 0; i < ArgCount; ++i) {
201 auto *Arg = E->getArg(Arg: i);
202 bool hasParam = i < MethodDecl->param_size();
203 auto *Param = hasParam ? MethodDecl->getParamDecl(Idx: i) : nullptr;
204 auto ArgType = Arg->getType();
205 std::optional<bool> IsUnsafe = isUnsafePtr(ArgType);
206 if (!IsUnsafe || !(*IsUnsafe))
207 continue;
208 if (isPtrOriginSafe(Arg))
209 continue;
210 reportBug(CallArg: Arg, Param, DeclWithIssue: D);
211 }
212 }
213
214 void checkThisArg(const CXXMemberCallExpr *MemberCallExpr,
215 const Decl *DeclWithIssue) const {
216 if (auto *MD = MemberCallExpr->getMethodDecl()) {
217 auto name = safeGetName(ASTNode: MD);
218 if (name == "ref" || name == "deref")
219 return;
220 if (name == "incrementCheckedPtrCount" ||
221 name == "decrementCheckedPtrCount")
222 return;
223 }
224 auto *ThisExpr = MemberCallExpr->getImplicitObjectArgument();
225 QualType ArgType = MemberCallExpr->getObjectType().getCanonicalType();
226 std::optional<bool> IsUnsafe = isUnsafeType(ArgType);
227 if (!IsUnsafe || !*IsUnsafe)
228 return;
229
230 if (isPtrOriginSafe(Arg: ThisExpr))
231 return;
232
233 reportBugOnThis(CallArg: MemberCallExpr, DeclWithIssue);
234 }
235
236 void checkArg(const Expr *Arg, QualType ParamType, const ParmVarDecl *Param,
237 const Decl *DeclWithIssue) const {
238 std::optional<bool> IsUncounted = isUnsafePtr(ParamType);
239 if (!IsUncounted || !(*IsUncounted))
240 return;
241
242 if (auto *DefaultArg = dyn_cast<CXXDefaultArgExpr>(Val: Arg))
243 Arg = DefaultArg->getExpr();
244
245 if (isPtrOriginSafe(Arg))
246 return;
247
248 reportBug(CallArg: Arg, Param, DeclWithIssue);
249 }
250
251 bool isPtrOriginSafe(const Expr *Arg) const {
252 return tryToFindPtrOrigin(
253 E: Arg, /*StopAtFirstRefCountedObj=*/true,
254 isSafePtr: [&](const clang::CXXRecordDecl *Record) { return isSafePtr(Record); },
255 isSafePtrType: [&](const clang::QualType T) { return isSafePtrType(type: T); },
256 isSafeGlobalDecl: [&](const clang::Decl *D) { return isSafeDecl(D); },
257 callback: [&](const clang::Expr *ArgOrigin, bool IsSafe) {
258 if (IsSafe)
259 return true;
260 if (isNullPtr(E: ArgOrigin))
261 return true;
262 if (isa<IntegerLiteral>(Val: ArgOrigin)) {
263 // FIXME: Check the value.
264 // foo(123)
265 return true;
266 }
267 if (isa<CXXBoolLiteralExpr>(Val: ArgOrigin))
268 return true;
269 if (isa<ObjCStringLiteral>(Val: ArgOrigin))
270 return true;
271 if (isASafeCallArg(E: ArgOrigin))
272 return true;
273 if (EFA.isACallToEnsureFn(E: ArgOrigin)) {
274 auto *MCE = dyn_cast<CXXMemberCallExpr>(Val: ArgOrigin);
275 assert(MCE);
276 if (isPtrOriginSafe(Arg: MCE->getImplicitObjectArgument()))
277 return true;
278 }
279 if (isSafeExpr(ArgOrigin))
280 return true;
281 return false;
282 });
283 }
284
285 template <typename CallOrConstruct>
286 bool shouldSkipCall(const CallOrConstruct *CE,
287 const FunctionDecl *Callee) const {
288 if (BR->getSourceManager().isInSystemHeader(Loc: CE->getExprLoc()))
289 return true;
290
291 if (Callee && TFA.isTrivial(D: Callee))
292 return true;
293
294 if (isTrivialBuiltinFunction(F: Callee))
295 return true;
296
297 if (CE->getNumArgs() == 0)
298 return false;
299
300 // If an assignment is problematic we should warn about the sole existence
301 // of object on LHS.
302 if (auto *MemberOp = dyn_cast<CXXOperatorCallExpr>(CE)) {
303 // Note: assignemnt to built-in type isn't derived from CallExpr.
304 if (MemberOp->getOperator() ==
305 OO_Equal) { // Ignore assignment to Ref/RefPtr.
306 auto *callee = MemberOp->getDirectCallee();
307 if (auto *calleeDecl = dyn_cast<CXXMethodDecl>(callee)) {
308 if (const CXXRecordDecl *classDecl = calleeDecl->getParent()) {
309 if (isSafePtr(Record: classDecl))
310 return true;
311 }
312 }
313 }
314 if (MemberOp->isAssignmentOp())
315 return false;
316 }
317
318 if (!Callee)
319 return false;
320
321 if (isMethodOnWTFContainerType(Decl: Callee))
322 return true;
323
324 auto overloadedOperatorType = Callee->getOverloadedOperator();
325 if (overloadedOperatorType == OO_EqualEqual ||
326 overloadedOperatorType == OO_ExclaimEqual ||
327 overloadedOperatorType == OO_LessEqual ||
328 overloadedOperatorType == OO_GreaterEqual ||
329 overloadedOperatorType == OO_Spaceship ||
330 overloadedOperatorType == OO_AmpAmp ||
331 overloadedOperatorType == OO_PipePipe)
332 return true;
333
334 if (isCtorOfSafePtr(F: Callee) || isPtrConversion(F: Callee))
335 return true;
336
337 auto name = safeGetName(ASTNode: Callee);
338 if (name == "adoptRef" || name == "getPtr" || name == "WeakPtr" ||
339 name == "is" || name == "equal" || name == "hash" || name == "isType" ||
340 // FIXME: Most/all of these should be implemented via attributes.
341 name == "CFEqual" || name == "equalIgnoringASCIICase" ||
342 name == "equalIgnoringASCIICaseCommon" ||
343 name == "equalIgnoringNullity" || name == "toString")
344 return true;
345
346 return false;
347 }
348
349 bool isMethodOnWTFContainerType(const FunctionDecl *Decl) const {
350 if (!isa<CXXMethodDecl>(Val: Decl))
351 return false;
352 auto *ClassDecl = Decl->getParent();
353 if (!ClassDecl || !isa<CXXRecordDecl>(Val: ClassDecl))
354 return false;
355
356 auto *NsDecl = ClassDecl->getParent();
357 if (!NsDecl || !isa<NamespaceDecl>(Val: NsDecl))
358 return false;
359
360 auto MethodName = safeGetName(ASTNode: Decl);
361 auto ClsNameStr = safeGetName(ASTNode: ClassDecl);
362 StringRef ClsName = ClsNameStr; // FIXME: Make safeGetName return StringRef.
363 auto NamespaceName = safeGetName(ASTNode: NsDecl);
364 // FIXME: These should be implemented via attributes.
365 return NamespaceName == "WTF" &&
366 (MethodName == "find" || MethodName == "findIf" ||
367 MethodName == "reverseFind" || MethodName == "reverseFindIf" ||
368 MethodName == "findIgnoringASCIICase" || MethodName == "get" ||
369 MethodName == "inlineGet" || MethodName == "contains" ||
370 MethodName == "containsIf" ||
371 MethodName == "containsIgnoringASCIICase" ||
372 MethodName == "startsWith" || MethodName == "endsWith" ||
373 MethodName == "startsWithIgnoringASCIICase" ||
374 MethodName == "endsWithIgnoringASCIICase" ||
375 MethodName == "substring") &&
376 (ClsName.ends_with(Suffix: "Vector") || ClsName.ends_with(Suffix: "Set") ||
377 ClsName.ends_with(Suffix: "Map") || ClsName == "StringImpl" ||
378 ClsName.ends_with(Suffix: "String"));
379 }
380
381 void reportBug(const Expr *CallArg, const ParmVarDecl *Param,
382 const Decl *DeclWithIssue) const {
383 assert(CallArg);
384
385 SmallString<100> Buf;
386 llvm::raw_svector_ostream Os(Buf);
387
388 const std::string paramName = safeGetName(ASTNode: Param);
389 Os << "Call argument";
390 if (!paramName.empty()) {
391 Os << " for parameter ";
392 printQuotedQualifiedName(Os, D: Param);
393 }
394 Os << " is " << ptrKind() << " and unsafe.";
395
396 bool usesDefaultArgValue = isa<CXXDefaultArgExpr>(Val: CallArg) && Param;
397 const SourceLocation SrcLocToReport =
398 usesDefaultArgValue ? Param->getDefaultArg()->getExprLoc()
399 : CallArg->getSourceRange().getBegin();
400
401 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
402 auto Report = std::make_unique<BasicBugReport>(args: Bug, args: Os.str(), args&: BSLoc);
403 Report->addRange(R: CallArg->getSourceRange());
404 Report->setDeclWithIssue(DeclWithIssue);
405 BR->emitReport(R: std::move(Report));
406 }
407
408 void reportBugOnThis(const Expr *CallArg, const Decl *DeclWithIssue) const {
409 assert(CallArg);
410
411 const SourceLocation SrcLocToReport = CallArg->getSourceRange().getBegin();
412
413 SmallString<100> Buf;
414 llvm::raw_svector_ostream Os(Buf);
415 Os << "Call argument for 'this' parameter is " << ptrKind();
416 Os << " and unsafe.";
417
418 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
419 auto Report = std::make_unique<BasicBugReport>(args: Bug, args: Os.str(), args&: BSLoc);
420 Report->addRange(R: CallArg->getSourceRange());
421 Report->setDeclWithIssue(DeclWithIssue);
422 BR->emitReport(R: std::move(Report));
423 }
424
425 void reportBugOnReceiver(const Expr *CallArg,
426 const Decl *DeclWithIssue) const {
427 assert(CallArg);
428
429 const SourceLocation SrcLocToReport = CallArg->getSourceRange().getBegin();
430
431 SmallString<100> Buf;
432 llvm::raw_svector_ostream Os(Buf);
433 Os << "Receiver is " << ptrKind() << " and unsafe.";
434
435 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
436 auto Report = std::make_unique<BasicBugReport>(args: Bug, args: Os.str(), args&: BSLoc);
437 Report->addRange(R: CallArg->getSourceRange());
438 Report->setDeclWithIssue(DeclWithIssue);
439 BR->emitReport(R: std::move(Report));
440 }
441};
442
443class UncountedCallArgsChecker final : public RawPtrRefCallArgsChecker {
444public:
445 UncountedCallArgsChecker()
446 : RawPtrRefCallArgsChecker("Uncounted call argument for a raw "
447 "pointer/reference parameter") {}
448
449 std::optional<bool> isUnsafeType(QualType QT) const final {
450 return isUncounted(T: QT);
451 }
452
453 std::optional<bool> isUnsafePtr(QualType QT) const final {
454 return isUncountedPtr(T: QT.getCanonicalType());
455 }
456
457 bool isSafePtr(const CXXRecordDecl *Record) const final {
458 return isRefCounted(Class: Record) || isCheckedPtr(Class: Record);
459 }
460
461 bool isSafePtrType(const QualType type) const final {
462 return isRefOrCheckedPtrType(T: type);
463 }
464
465 const char *ptrKind() const final { return "uncounted"; }
466};
467
468class UncheckedCallArgsChecker final : public RawPtrRefCallArgsChecker {
469public:
470 UncheckedCallArgsChecker()
471 : RawPtrRefCallArgsChecker("Unchecked call argument for a raw "
472 "pointer/reference parameter") {}
473
474 std::optional<bool> isUnsafeType(QualType QT) const final {
475 return isUnchecked(T: QT);
476 }
477
478 std::optional<bool> isUnsafePtr(QualType QT) const final {
479 return isUncheckedPtr(T: QT.getCanonicalType());
480 }
481
482 bool isSafePtr(const CXXRecordDecl *Record) const final {
483 return isRefCounted(Class: Record) || isCheckedPtr(Class: Record);
484 }
485
486 bool isSafePtrType(const QualType type) const final {
487 return isRefOrCheckedPtrType(T: type);
488 }
489
490 bool isSafeExpr(const Expr *E) const final {
491 return isExprToGetCheckedPtrCapableMember(E);
492 }
493
494 const char *ptrKind() const final { return "unchecked"; }
495};
496
497class UnretainedCallArgsChecker final : public RawPtrRefCallArgsChecker {
498public:
499 UnretainedCallArgsChecker()
500 : RawPtrRefCallArgsChecker("Unretained call argument for a raw "
501 "pointer/reference parameter") {
502 RTC = RetainTypeChecker();
503 }
504
505 std::optional<bool> isUnsafeType(QualType QT) const final {
506 return RTC->isUnretained(QT);
507 }
508
509 std::optional<bool> isUnsafePtr(QualType QT) const final {
510 return RTC->isUnretained(QT);
511 }
512
513 bool isSafePtr(const CXXRecordDecl *Record) const final {
514 return isRetainPtrOrOSPtr(Class: Record);
515 }
516
517 bool isSafePtrType(const QualType type) const final {
518 return isRetainPtrOrOSPtrType(T: type);
519 }
520
521 bool isSafeDecl(const Decl *D) const final {
522 // Treat NS/CF globals in system header as immortal.
523 return BR->getSourceManager().isInSystemHeader(Loc: D->getLocation());
524 }
525
526 const char *ptrKind() const final { return "unretained"; }
527};
528
529} // namespace
530
531void ento::registerUncountedCallArgsChecker(CheckerManager &Mgr) {
532 Mgr.registerChecker<UncountedCallArgsChecker>();
533}
534
535bool ento::shouldRegisterUncountedCallArgsChecker(const CheckerManager &) {
536 return true;
537}
538
539void ento::registerUncheckedCallArgsChecker(CheckerManager &Mgr) {
540 Mgr.registerChecker<UncheckedCallArgsChecker>();
541}
542
543bool ento::shouldRegisterUncheckedCallArgsChecker(const CheckerManager &) {
544 return true;
545}
546
547void ento::registerUnretainedCallArgsChecker(CheckerManager &Mgr) {
548 Mgr.registerChecker<UnretainedCallArgsChecker>();
549}
550
551bool ento::shouldRegisterUnretainedCallArgsChecker(const CheckerManager &) {
552 return true;
553}
554