xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
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