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
24 using namespace clang;
25 using namespace ento;
26
27 namespace {
28
29 class RawPtrRefCallArgsChecker
30 : public Checker<check::ASTDecl<TranslationUnitDecl>> {
31 BugType Bug;
32 mutable BugReporter *BR;
33
34 TrivialFunctionAnalysis TFA;
35 EnsureFunctionAnalysis EFA;
36
37 protected:
38 mutable std::optional<RetainTypeChecker> RTC;
39
40 public:
RawPtrRefCallArgsChecker(const char * description)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;
isSafeExpr(const Expr *) const48 virtual bool isSafeExpr(const Expr *) const { return false; }
49 virtual const char *ptrKind() const = 0;
50
checkASTDecl(const TranslationUnitDecl * TUD,AnalysisManager & MGR,BugReporter & BRArg) const51 void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
52 BugReporter &BRArg) const {
53 BR = &BRArg;
54
55 // The calls to checkAST* from AnalysisConsumer don't
56 // visit template instantiations or lambda classes. We
57 // want to visit those, so we make our own RecursiveASTVisitor.
58 struct LocalVisitor : DynamicRecursiveASTVisitor {
59 const RawPtrRefCallArgsChecker *Checker;
60 Decl *DeclWithIssue{nullptr};
61
62 explicit LocalVisitor(const RawPtrRefCallArgsChecker *Checker)
63 : Checker(Checker) {
64 assert(Checker);
65 ShouldVisitTemplateInstantiations = true;
66 ShouldVisitImplicitCode = false;
67 }
68
69 bool TraverseClassTemplateDecl(ClassTemplateDecl *Decl) override {
70 if (isSmartPtrClass(safeGetName(Decl)))
71 return true;
72 return DynamicRecursiveASTVisitor::TraverseClassTemplateDecl(Decl);
73 }
74
75 bool TraverseDecl(Decl *D) override {
76 llvm::SaveAndRestore SavedDecl(DeclWithIssue);
77 if (D && (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)))
78 DeclWithIssue = D;
79 return DynamicRecursiveASTVisitor::TraverseDecl(D);
80 }
81
82 bool VisitCallExpr(CallExpr *CE) override {
83 Checker->visitCallExpr(CE, DeclWithIssue);
84 return true;
85 }
86
87 bool VisitTypedefDecl(TypedefDecl *TD) override {
88 if (Checker->RTC)
89 Checker->RTC->visitTypedef(TD);
90 return true;
91 }
92
93 bool VisitObjCMessageExpr(ObjCMessageExpr *ObjCMsgExpr) override {
94 Checker->visitObjCMessageExpr(ObjCMsgExpr, DeclWithIssue);
95 return true;
96 }
97 };
98
99 LocalVisitor visitor(this);
100 if (RTC)
101 RTC->visitTranslationUnitDecl(TUD);
102 visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
103 }
104
visitCallExpr(const CallExpr * CE,const Decl * D) const105 void visitCallExpr(const CallExpr *CE, const Decl *D) const {
106 if (shouldSkipCall(CE))
107 return;
108
109 if (auto *F = CE->getDirectCallee()) {
110 // Skip the first argument for overloaded member operators (e. g. lambda
111 // or std::function call operator).
112 unsigned ArgIdx =
113 isa<CXXOperatorCallExpr>(CE) && isa_and_nonnull<CXXMethodDecl>(F);
114
115 if (auto *MemberCallExpr = dyn_cast<CXXMemberCallExpr>(CE)) {
116 if (auto *MD = MemberCallExpr->getMethodDecl()) {
117 auto name = safeGetName(MD);
118 if (name == "ref" || name == "deref")
119 return;
120 if (name == "incrementCheckedPtrCount" ||
121 name == "decrementCheckedPtrCount")
122 return;
123 }
124 auto *E = MemberCallExpr->getImplicitObjectArgument();
125 QualType ArgType = MemberCallExpr->getObjectType().getCanonicalType();
126 std::optional<bool> IsUnsafe = isUnsafeType(ArgType);
127 if (IsUnsafe && *IsUnsafe && !isPtrOriginSafe(E))
128 reportBugOnThis(E, D);
129 }
130
131 for (auto P = F->param_begin();
132 // FIXME: Also check variadic function parameters.
133 // FIXME: Also check default function arguments. Probably a different
134 // checker. In case there are default arguments the call can have
135 // fewer arguments than the callee has parameters.
136 P < F->param_end() && ArgIdx < CE->getNumArgs(); ++P, ++ArgIdx) {
137 // TODO: attributes.
138 // if ((*P)->hasAttr<SafeRefCntblRawPtrAttr>())
139 // continue;
140
141 QualType ArgType = (*P)->getType();
142 // FIXME: more complex types (arrays, references to raw pointers, etc)
143 std::optional<bool> IsUncounted = isUnsafePtr(ArgType);
144 if (!IsUncounted || !(*IsUncounted))
145 continue;
146
147 const auto *Arg = CE->getArg(ArgIdx);
148
149 if (auto *defaultArg = dyn_cast<CXXDefaultArgExpr>(Arg))
150 Arg = defaultArg->getExpr();
151
152 if (isPtrOriginSafe(Arg))
153 continue;
154
155 reportBug(Arg, *P, D);
156 }
157 for (; ArgIdx < CE->getNumArgs(); ++ArgIdx) {
158 const auto *Arg = CE->getArg(ArgIdx);
159 auto ArgType = Arg->getType();
160 std::optional<bool> IsUncounted = isUnsafePtr(ArgType);
161 if (!IsUncounted || !(*IsUncounted))
162 continue;
163
164 if (auto *defaultArg = dyn_cast<CXXDefaultArgExpr>(Arg))
165 Arg = defaultArg->getExpr();
166
167 if (isPtrOriginSafe(Arg))
168 continue;
169
170 reportBug(Arg, nullptr, D);
171 }
172 }
173 }
174
visitObjCMessageExpr(const ObjCMessageExpr * E,const Decl * D) const175 void visitObjCMessageExpr(const ObjCMessageExpr *E, const Decl *D) const {
176 if (BR->getSourceManager().isInSystemHeader(E->getExprLoc()))
177 return;
178
179 auto Selector = E->getSelector();
180 if (auto *Receiver = E->getInstanceReceiver()) {
181 std::optional<bool> IsUnsafe = isUnsafePtr(E->getReceiverType());
182 if (IsUnsafe && *IsUnsafe && !isPtrOriginSafe(Receiver)) {
183 if (auto *InnerMsg = dyn_cast<ObjCMessageExpr>(Receiver)) {
184 auto InnerSelector = InnerMsg->getSelector();
185 if (InnerSelector.getNameForSlot(0) == "alloc" &&
186 Selector.getNameForSlot(0).starts_with("init"))
187 return;
188 }
189 reportBugOnReceiver(Receiver, D);
190 }
191 }
192
193 auto *MethodDecl = E->getMethodDecl();
194 if (!MethodDecl)
195 return;
196
197 auto ArgCount = E->getNumArgs();
198 for (unsigned i = 0; i < ArgCount; ++i) {
199 auto *Arg = E->getArg(i);
200 bool hasParam = i < MethodDecl->param_size();
201 auto *Param = hasParam ? MethodDecl->getParamDecl(i) : nullptr;
202 auto ArgType = Arg->getType();
203 std::optional<bool> IsUnsafe = isUnsafePtr(ArgType);
204 if (!IsUnsafe || !(*IsUnsafe))
205 continue;
206 if (isPtrOriginSafe(Arg))
207 continue;
208 reportBug(Arg, Param, D);
209 }
210 }
211
isPtrOriginSafe(const Expr * Arg) const212 bool isPtrOriginSafe(const Expr *Arg) const {
213 return tryToFindPtrOrigin(
214 Arg, /*StopAtFirstRefCountedObj=*/true,
215 [&](const clang::CXXRecordDecl *Record) { return isSafePtr(Record); },
216 [&](const clang::QualType T) { return isSafePtrType(T); },
217 [&](const clang::Expr *ArgOrigin, bool IsSafe) {
218 if (IsSafe)
219 return true;
220 if (isa<CXXNullPtrLiteralExpr>(ArgOrigin)) {
221 // foo(nullptr)
222 return true;
223 }
224 if (isa<IntegerLiteral>(ArgOrigin)) {
225 // FIXME: Check the value.
226 // foo(NULL)
227 return true;
228 }
229 if (isa<ObjCStringLiteral>(ArgOrigin))
230 return true;
231 if (isASafeCallArg(ArgOrigin))
232 return true;
233 if (EFA.isACallToEnsureFn(ArgOrigin))
234 return true;
235 if (isSafeExpr(ArgOrigin))
236 return true;
237 return false;
238 });
239 }
240
shouldSkipCall(const CallExpr * CE) const241 bool shouldSkipCall(const CallExpr *CE) const {
242 const auto *Callee = CE->getDirectCallee();
243
244 if (BR->getSourceManager().isInSystemHeader(CE->getExprLoc()))
245 return true;
246
247 if (Callee && TFA.isTrivial(Callee) && !Callee->isVirtualAsWritten())
248 return true;
249
250 if (isTrivialBuiltinFunction(Callee))
251 return true;
252
253 if (CE->getNumArgs() == 0)
254 return false;
255
256 // If an assignment is problematic we should warn about the sole existence
257 // of object on LHS.
258 if (auto *MemberOp = dyn_cast<CXXOperatorCallExpr>(CE)) {
259 // Note: assignemnt to built-in type isn't derived from CallExpr.
260 if (MemberOp->getOperator() ==
261 OO_Equal) { // Ignore assignment to Ref/RefPtr.
262 auto *callee = MemberOp->getDirectCallee();
263 if (auto *calleeDecl = dyn_cast<CXXMethodDecl>(callee)) {
264 if (const CXXRecordDecl *classDecl = calleeDecl->getParent()) {
265 if (isSafePtr(classDecl))
266 return true;
267 }
268 }
269 }
270 if (MemberOp->isAssignmentOp())
271 return false;
272 }
273
274 if (!Callee)
275 return false;
276
277 if (isMethodOnWTFContainerType(Callee))
278 return true;
279
280 auto overloadedOperatorType = Callee->getOverloadedOperator();
281 if (overloadedOperatorType == OO_EqualEqual ||
282 overloadedOperatorType == OO_ExclaimEqual ||
283 overloadedOperatorType == OO_LessEqual ||
284 overloadedOperatorType == OO_GreaterEqual ||
285 overloadedOperatorType == OO_Spaceship ||
286 overloadedOperatorType == OO_AmpAmp ||
287 overloadedOperatorType == OO_PipePipe)
288 return true;
289
290 if (isCtorOfSafePtr(Callee) || isPtrConversion(Callee))
291 return true;
292
293 auto name = safeGetName(Callee);
294 if (name == "adoptRef" || name == "getPtr" || name == "WeakPtr" ||
295 name == "is" || name == "equal" || name == "hash" || name == "isType" ||
296 // FIXME: Most/all of these should be implemented via attributes.
297 name == "CFEqual" || name == "equalIgnoringASCIICase" ||
298 name == "equalIgnoringASCIICaseCommon" ||
299 name == "equalIgnoringNullity" || name == "toString")
300 return true;
301
302 return false;
303 }
304
isMethodOnWTFContainerType(const FunctionDecl * Decl) const305 bool isMethodOnWTFContainerType(const FunctionDecl *Decl) const {
306 if (!isa<CXXMethodDecl>(Decl))
307 return false;
308 auto *ClassDecl = Decl->getParent();
309 if (!ClassDecl || !isa<CXXRecordDecl>(ClassDecl))
310 return false;
311
312 auto *NsDecl = ClassDecl->getParent();
313 if (!NsDecl || !isa<NamespaceDecl>(NsDecl))
314 return false;
315
316 auto MethodName = safeGetName(Decl);
317 auto ClsNameStr = safeGetName(ClassDecl);
318 StringRef ClsName = ClsNameStr; // FIXME: Make safeGetName return StringRef.
319 auto NamespaceName = safeGetName(NsDecl);
320 // FIXME: These should be implemented via attributes.
321 return NamespaceName == "WTF" &&
322 (MethodName == "find" || MethodName == "findIf" ||
323 MethodName == "reverseFind" || MethodName == "reverseFindIf" ||
324 MethodName == "findIgnoringASCIICase" || MethodName == "get" ||
325 MethodName == "inlineGet" || MethodName == "contains" ||
326 MethodName == "containsIf" ||
327 MethodName == "containsIgnoringASCIICase" ||
328 MethodName == "startsWith" || MethodName == "endsWith" ||
329 MethodName == "startsWithIgnoringASCIICase" ||
330 MethodName == "endsWithIgnoringASCIICase" ||
331 MethodName == "substring") &&
332 (ClsName.ends_with("Vector") || ClsName.ends_with("Set") ||
333 ClsName.ends_with("Map") || ClsName == "StringImpl" ||
334 ClsName.ends_with("String"));
335 }
336
reportBug(const Expr * CallArg,const ParmVarDecl * Param,const Decl * DeclWithIssue) const337 void reportBug(const Expr *CallArg, const ParmVarDecl *Param,
338 const Decl *DeclWithIssue) const {
339 assert(CallArg);
340
341 SmallString<100> Buf;
342 llvm::raw_svector_ostream Os(Buf);
343
344 const std::string paramName = safeGetName(Param);
345 Os << "Call argument";
346 if (!paramName.empty()) {
347 Os << " for parameter ";
348 printQuotedQualifiedName(Os, Param);
349 }
350 Os << " is " << ptrKind() << " and unsafe.";
351
352 bool usesDefaultArgValue = isa<CXXDefaultArgExpr>(CallArg) && Param;
353 const SourceLocation SrcLocToReport =
354 usesDefaultArgValue ? Param->getDefaultArg()->getExprLoc()
355 : CallArg->getSourceRange().getBegin();
356
357 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
358 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
359 Report->addRange(CallArg->getSourceRange());
360 Report->setDeclWithIssue(DeclWithIssue);
361 BR->emitReport(std::move(Report));
362 }
363
reportBugOnThis(const Expr * CallArg,const Decl * DeclWithIssue) const364 void reportBugOnThis(const Expr *CallArg, const Decl *DeclWithIssue) const {
365 assert(CallArg);
366
367 const SourceLocation SrcLocToReport = CallArg->getSourceRange().getBegin();
368
369 SmallString<100> Buf;
370 llvm::raw_svector_ostream Os(Buf);
371 Os << "Call argument for 'this' parameter is " << ptrKind();
372 Os << " and unsafe.";
373
374 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
375 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
376 Report->addRange(CallArg->getSourceRange());
377 Report->setDeclWithIssue(DeclWithIssue);
378 BR->emitReport(std::move(Report));
379 }
380
reportBugOnReceiver(const Expr * CallArg,const Decl * DeclWithIssue) const381 void reportBugOnReceiver(const Expr *CallArg,
382 const Decl *DeclWithIssue) const {
383 assert(CallArg);
384
385 const SourceLocation SrcLocToReport = CallArg->getSourceRange().getBegin();
386
387 SmallString<100> Buf;
388 llvm::raw_svector_ostream Os(Buf);
389 Os << "Reciever is " << ptrKind() << " and unsafe.";
390
391 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
392 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
393 Report->addRange(CallArg->getSourceRange());
394 Report->setDeclWithIssue(DeclWithIssue);
395 BR->emitReport(std::move(Report));
396 }
397 };
398
399 class UncountedCallArgsChecker final : public RawPtrRefCallArgsChecker {
400 public:
UncountedCallArgsChecker()401 UncountedCallArgsChecker()
402 : RawPtrRefCallArgsChecker("Uncounted call argument for a raw "
403 "pointer/reference parameter") {}
404
isUnsafeType(QualType QT) const405 std::optional<bool> isUnsafeType(QualType QT) const final {
406 return isUncounted(QT);
407 }
408
isUnsafePtr(QualType QT) const409 std::optional<bool> isUnsafePtr(QualType QT) const final {
410 return isUncountedPtr(QT.getCanonicalType());
411 }
412
isSafePtr(const CXXRecordDecl * Record) const413 bool isSafePtr(const CXXRecordDecl *Record) const final {
414 return isRefCounted(Record) || isCheckedPtr(Record);
415 }
416
isSafePtrType(const QualType type) const417 bool isSafePtrType(const QualType type) const final {
418 return isRefOrCheckedPtrType(type);
419 }
420
ptrKind() const421 const char *ptrKind() const final { return "uncounted"; }
422 };
423
424 class UncheckedCallArgsChecker final : public RawPtrRefCallArgsChecker {
425 public:
UncheckedCallArgsChecker()426 UncheckedCallArgsChecker()
427 : RawPtrRefCallArgsChecker("Unchecked call argument for a raw "
428 "pointer/reference parameter") {}
429
isUnsafeType(QualType QT) const430 std::optional<bool> isUnsafeType(QualType QT) const final {
431 return isUnchecked(QT);
432 }
433
isUnsafePtr(QualType QT) const434 std::optional<bool> isUnsafePtr(QualType QT) const final {
435 return isUncheckedPtr(QT.getCanonicalType());
436 }
437
isSafePtr(const CXXRecordDecl * Record) const438 bool isSafePtr(const CXXRecordDecl *Record) const final {
439 return isRefCounted(Record) || isCheckedPtr(Record);
440 }
441
isSafePtrType(const QualType type) const442 bool isSafePtrType(const QualType type) const final {
443 return isRefOrCheckedPtrType(type);
444 }
445
isSafeExpr(const Expr * E) const446 bool isSafeExpr(const Expr *E) const final {
447 return isExprToGetCheckedPtrCapableMember(E);
448 }
449
ptrKind() const450 const char *ptrKind() const final { return "unchecked"; }
451 };
452
453 class UnretainedCallArgsChecker final : public RawPtrRefCallArgsChecker {
454 public:
UnretainedCallArgsChecker()455 UnretainedCallArgsChecker()
456 : RawPtrRefCallArgsChecker("Unretained call argument for a raw "
457 "pointer/reference parameter") {
458 RTC = RetainTypeChecker();
459 }
460
isUnsafeType(QualType QT) const461 std::optional<bool> isUnsafeType(QualType QT) const final {
462 return RTC->isUnretained(QT);
463 }
464
isUnsafePtr(QualType QT) const465 std::optional<bool> isUnsafePtr(QualType QT) const final {
466 return RTC->isUnretained(QT);
467 }
468
isSafePtr(const CXXRecordDecl * Record) const469 bool isSafePtr(const CXXRecordDecl *Record) const final {
470 return isRetainPtr(Record);
471 }
472
isSafePtrType(const QualType type) const473 bool isSafePtrType(const QualType type) const final {
474 return isRetainPtrType(type);
475 }
476
isSafeExpr(const Expr * E) const477 bool isSafeExpr(const Expr *E) const final {
478 return ento::cocoa::isCocoaObjectRef(E->getType()) &&
479 isa<ObjCMessageExpr>(E);
480 }
481
ptrKind() const482 const char *ptrKind() const final { return "unretained"; }
483 };
484
485 } // namespace
486
registerUncountedCallArgsChecker(CheckerManager & Mgr)487 void ento::registerUncountedCallArgsChecker(CheckerManager &Mgr) {
488 Mgr.registerChecker<UncountedCallArgsChecker>();
489 }
490
shouldRegisterUncountedCallArgsChecker(const CheckerManager &)491 bool ento::shouldRegisterUncountedCallArgsChecker(const CheckerManager &) {
492 return true;
493 }
494
registerUncheckedCallArgsChecker(CheckerManager & Mgr)495 void ento::registerUncheckedCallArgsChecker(CheckerManager &Mgr) {
496 Mgr.registerChecker<UncheckedCallArgsChecker>();
497 }
498
shouldRegisterUncheckedCallArgsChecker(const CheckerManager &)499 bool ento::shouldRegisterUncheckedCallArgsChecker(const CheckerManager &) {
500 return true;
501 }
502
registerUnretainedCallArgsChecker(CheckerManager & Mgr)503 void ento::registerUnretainedCallArgsChecker(CheckerManager &Mgr) {
504 Mgr.registerChecker<UnretainedCallArgsChecker>();
505 }
506
shouldRegisterUnretainedCallArgsChecker(const CheckerManager &)507 bool ento::shouldRegisterUnretainedCallArgsChecker(const CheckerManager &) {
508 return true;
509 }
510