15ffd83dbSDimitry Andric //=======- UncountedCallArgsChecker.cpp --------------------------*- C++ -*-==//
25ffd83dbSDimitry Andric //
35ffd83dbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45ffd83dbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
55ffd83dbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
65ffd83dbSDimitry Andric //
75ffd83dbSDimitry Andric //===----------------------------------------------------------------------===//
85ffd83dbSDimitry Andric 
95ffd83dbSDimitry Andric #include "ASTUtils.h"
105ffd83dbSDimitry Andric #include "DiagOutputUtils.h"
115ffd83dbSDimitry Andric #include "PtrTypesSemantics.h"
125ffd83dbSDimitry Andric #include "clang/AST/CXXInheritance.h"
135ffd83dbSDimitry Andric #include "clang/AST/Decl.h"
145ffd83dbSDimitry Andric #include "clang/AST/DeclCXX.h"
155ffd83dbSDimitry Andric #include "clang/AST/RecursiveASTVisitor.h"
165ffd83dbSDimitry Andric #include "clang/Basic/SourceLocation.h"
175ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
185ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
195ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
205ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
21bdd1243dSDimitry Andric #include <optional>
225ffd83dbSDimitry Andric 
235ffd83dbSDimitry Andric using namespace clang;
245ffd83dbSDimitry Andric using namespace ento;
255ffd83dbSDimitry Andric 
265ffd83dbSDimitry Andric namespace {
275ffd83dbSDimitry Andric 
285ffd83dbSDimitry Andric class UncountedCallArgsChecker
295ffd83dbSDimitry Andric     : public Checker<check::ASTDecl<TranslationUnitDecl>> {
305ffd83dbSDimitry Andric   BugType Bug{this,
315ffd83dbSDimitry Andric             "Uncounted call argument for a raw pointer/reference parameter",
325ffd83dbSDimitry Andric             "WebKit coding guidelines"};
335ffd83dbSDimitry Andric   mutable BugReporter *BR;
345ffd83dbSDimitry Andric 
35*0fca6ea1SDimitry Andric   TrivialFunctionAnalysis TFA;
36*0fca6ea1SDimitry Andric 
375ffd83dbSDimitry Andric public:
385ffd83dbSDimitry Andric 
395ffd83dbSDimitry Andric   void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
405ffd83dbSDimitry Andric                     BugReporter &BRArg) const {
415ffd83dbSDimitry Andric     BR = &BRArg;
425ffd83dbSDimitry Andric 
435ffd83dbSDimitry Andric     // The calls to checkAST* from AnalysisConsumer don't
445ffd83dbSDimitry Andric     // visit template instantiations or lambda classes. We
455ffd83dbSDimitry Andric     // want to visit those, so we make our own RecursiveASTVisitor.
465ffd83dbSDimitry Andric     struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
475ffd83dbSDimitry Andric       const UncountedCallArgsChecker *Checker;
485ffd83dbSDimitry Andric       explicit LocalVisitor(const UncountedCallArgsChecker *Checker)
495ffd83dbSDimitry Andric           : Checker(Checker) {
505ffd83dbSDimitry Andric         assert(Checker);
515ffd83dbSDimitry Andric       }
525ffd83dbSDimitry Andric 
535ffd83dbSDimitry Andric       bool shouldVisitTemplateInstantiations() const { return true; }
545ffd83dbSDimitry Andric       bool shouldVisitImplicitCode() const { return false; }
555ffd83dbSDimitry Andric 
56*0fca6ea1SDimitry Andric       bool TraverseClassTemplateDecl(ClassTemplateDecl *Decl) {
57*0fca6ea1SDimitry Andric         if (isRefType(safeGetName(Decl)))
58*0fca6ea1SDimitry Andric           return true;
59*0fca6ea1SDimitry Andric         return RecursiveASTVisitor<LocalVisitor>::TraverseClassTemplateDecl(
60*0fca6ea1SDimitry Andric             Decl);
61*0fca6ea1SDimitry Andric       }
62*0fca6ea1SDimitry Andric 
635ffd83dbSDimitry Andric       bool VisitCallExpr(const CallExpr *CE) {
645ffd83dbSDimitry Andric         Checker->visitCallExpr(CE);
655ffd83dbSDimitry Andric         return true;
665ffd83dbSDimitry Andric       }
675ffd83dbSDimitry Andric     };
685ffd83dbSDimitry Andric 
695ffd83dbSDimitry Andric     LocalVisitor visitor(this);
705ffd83dbSDimitry Andric     visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
715ffd83dbSDimitry Andric   }
725ffd83dbSDimitry Andric 
735ffd83dbSDimitry Andric   void visitCallExpr(const CallExpr *CE) const {
745ffd83dbSDimitry Andric     if (shouldSkipCall(CE))
755ffd83dbSDimitry Andric       return;
765ffd83dbSDimitry Andric 
775ffd83dbSDimitry Andric     if (auto *F = CE->getDirectCallee()) {
785ffd83dbSDimitry Andric       // Skip the first argument for overloaded member operators (e. g. lambda
795ffd83dbSDimitry Andric       // or std::function call operator).
80349cc55cSDimitry Andric       unsigned ArgIdx = isa<CXXOperatorCallExpr>(CE) && isa_and_nonnull<CXXMethodDecl>(F);
815ffd83dbSDimitry Andric 
82*0fca6ea1SDimitry Andric       if (auto *MemberCallExpr = dyn_cast<CXXMemberCallExpr>(CE)) {
83*0fca6ea1SDimitry Andric         if (auto *MD = MemberCallExpr->getMethodDecl()) {
84*0fca6ea1SDimitry Andric           auto name = safeGetName(MD);
85*0fca6ea1SDimitry Andric           if (name == "ref" || name == "deref")
86*0fca6ea1SDimitry Andric             return;
87*0fca6ea1SDimitry Andric         }
88*0fca6ea1SDimitry Andric         auto *E = MemberCallExpr->getImplicitObjectArgument();
89*0fca6ea1SDimitry Andric         QualType ArgType = MemberCallExpr->getObjectType();
90*0fca6ea1SDimitry Andric         std::optional<bool> IsUncounted =
91*0fca6ea1SDimitry Andric             isUncounted(ArgType->getAsCXXRecordDecl());
92*0fca6ea1SDimitry Andric         if (IsUncounted && *IsUncounted && !isPtrOriginSafe(E))
93*0fca6ea1SDimitry Andric           reportBugOnThis(E);
94*0fca6ea1SDimitry Andric       }
95*0fca6ea1SDimitry Andric 
965ffd83dbSDimitry Andric       for (auto P = F->param_begin();
975ffd83dbSDimitry Andric            // FIXME: Also check variadic function parameters.
985ffd83dbSDimitry Andric            // FIXME: Also check default function arguments. Probably a different
995ffd83dbSDimitry Andric            // checker. In case there are default arguments the call can have
1005ffd83dbSDimitry Andric            // fewer arguments than the callee has parameters.
1015ffd83dbSDimitry Andric            P < F->param_end() && ArgIdx < CE->getNumArgs(); ++P, ++ArgIdx) {
1025ffd83dbSDimitry Andric         // TODO: attributes.
1035ffd83dbSDimitry Andric         // if ((*P)->hasAttr<SafeRefCntblRawPtrAttr>())
1045ffd83dbSDimitry Andric         //  continue;
1055ffd83dbSDimitry Andric 
1065ffd83dbSDimitry Andric         const auto *ArgType = (*P)->getType().getTypePtrOrNull();
1075ffd83dbSDimitry Andric         if (!ArgType)
1085ffd83dbSDimitry Andric           continue; // FIXME? Should we bail?
1095ffd83dbSDimitry Andric 
1105ffd83dbSDimitry Andric         // FIXME: more complex types (arrays, references to raw pointers, etc)
111bdd1243dSDimitry Andric         std::optional<bool> IsUncounted = isUncountedPtr(ArgType);
112e8d8bef9SDimitry Andric         if (!IsUncounted || !(*IsUncounted))
1135ffd83dbSDimitry Andric           continue;
1145ffd83dbSDimitry Andric 
1155ffd83dbSDimitry Andric         const auto *Arg = CE->getArg(ArgIdx);
1165ffd83dbSDimitry Andric 
117*0fca6ea1SDimitry Andric         if (auto *defaultArg = dyn_cast<CXXDefaultArgExpr>(Arg))
118*0fca6ea1SDimitry Andric           Arg = defaultArg->getExpr();
1195ffd83dbSDimitry Andric 
120*0fca6ea1SDimitry Andric         if (isPtrOriginSafe(Arg))
1215ffd83dbSDimitry Andric           continue;
1225ffd83dbSDimitry Andric 
1235ffd83dbSDimitry Andric         reportBug(Arg, *P);
1245ffd83dbSDimitry Andric       }
1255ffd83dbSDimitry Andric     }
1265ffd83dbSDimitry Andric   }
1275ffd83dbSDimitry Andric 
128*0fca6ea1SDimitry Andric   bool isPtrOriginSafe(const Expr *Arg) const {
129*0fca6ea1SDimitry Andric     return tryToFindPtrOrigin(Arg, /*StopAtFirstRefCountedObj=*/true,
130*0fca6ea1SDimitry Andric                               [](const clang::Expr *ArgOrigin, bool IsSafe) {
131*0fca6ea1SDimitry Andric                                 if (IsSafe)
132*0fca6ea1SDimitry Andric                                   return true;
133*0fca6ea1SDimitry Andric                                 if (isa<CXXNullPtrLiteralExpr>(ArgOrigin)) {
134*0fca6ea1SDimitry Andric                                   // foo(nullptr)
135*0fca6ea1SDimitry Andric                                   return true;
136*0fca6ea1SDimitry Andric                                 }
137*0fca6ea1SDimitry Andric                                 if (isa<IntegerLiteral>(ArgOrigin)) {
138*0fca6ea1SDimitry Andric                                   // FIXME: Check the value.
139*0fca6ea1SDimitry Andric                                   // foo(NULL)
140*0fca6ea1SDimitry Andric                                   return true;
141*0fca6ea1SDimitry Andric                                 }
142*0fca6ea1SDimitry Andric                                 if (isASafeCallArg(ArgOrigin))
143*0fca6ea1SDimitry Andric                                   return true;
144*0fca6ea1SDimitry Andric                                 return false;
145*0fca6ea1SDimitry Andric                               });
146*0fca6ea1SDimitry Andric   }
147*0fca6ea1SDimitry Andric 
1485ffd83dbSDimitry Andric   bool shouldSkipCall(const CallExpr *CE) const {
149*0fca6ea1SDimitry Andric     const auto *Callee = CE->getDirectCallee();
150*0fca6ea1SDimitry Andric 
151*0fca6ea1SDimitry Andric     if (BR->getSourceManager().isInSystemHeader(CE->getExprLoc()))
152*0fca6ea1SDimitry Andric       return true;
153*0fca6ea1SDimitry Andric 
154*0fca6ea1SDimitry Andric     if (Callee && TFA.isTrivial(Callee))
155*0fca6ea1SDimitry Andric       return true;
156*0fca6ea1SDimitry Andric 
1575ffd83dbSDimitry Andric     if (CE->getNumArgs() == 0)
1585ffd83dbSDimitry Andric       return false;
1595ffd83dbSDimitry Andric 
1605ffd83dbSDimitry Andric     // If an assignment is problematic we should warn about the sole existence
1615ffd83dbSDimitry Andric     // of object on LHS.
1625ffd83dbSDimitry Andric     if (auto *MemberOp = dyn_cast<CXXOperatorCallExpr>(CE)) {
1635ffd83dbSDimitry Andric       // Note: assignemnt to built-in type isn't derived from CallExpr.
164*0fca6ea1SDimitry Andric       if (MemberOp->getOperator() ==
165*0fca6ea1SDimitry Andric           OO_Equal) { // Ignore assignment to Ref/RefPtr.
166*0fca6ea1SDimitry Andric         auto *callee = MemberOp->getDirectCallee();
167*0fca6ea1SDimitry Andric         if (auto *calleeDecl = dyn_cast<CXXMethodDecl>(callee)) {
168*0fca6ea1SDimitry Andric           if (const CXXRecordDecl *classDecl = calleeDecl->getParent()) {
169*0fca6ea1SDimitry Andric             if (isRefCounted(classDecl))
170*0fca6ea1SDimitry Andric               return true;
171*0fca6ea1SDimitry Andric           }
172*0fca6ea1SDimitry Andric         }
173*0fca6ea1SDimitry Andric       }
1745ffd83dbSDimitry Andric       if (MemberOp->isAssignmentOp())
1755ffd83dbSDimitry Andric         return false;
1765ffd83dbSDimitry Andric     }
1775ffd83dbSDimitry Andric 
1785ffd83dbSDimitry Andric     if (!Callee)
1795ffd83dbSDimitry Andric       return false;
1805ffd83dbSDimitry Andric 
181*0fca6ea1SDimitry Andric     if (isMethodOnWTFContainerType(Callee))
182*0fca6ea1SDimitry Andric       return true;
183*0fca6ea1SDimitry Andric 
1845ffd83dbSDimitry Andric     auto overloadedOperatorType = Callee->getOverloadedOperator();
1855ffd83dbSDimitry Andric     if (overloadedOperatorType == OO_EqualEqual ||
1865ffd83dbSDimitry Andric         overloadedOperatorType == OO_ExclaimEqual ||
1875ffd83dbSDimitry Andric         overloadedOperatorType == OO_LessEqual ||
1885ffd83dbSDimitry Andric         overloadedOperatorType == OO_GreaterEqual ||
1895ffd83dbSDimitry Andric         overloadedOperatorType == OO_Spaceship ||
1905ffd83dbSDimitry Andric         overloadedOperatorType == OO_AmpAmp ||
1915ffd83dbSDimitry Andric         overloadedOperatorType == OO_PipePipe)
1925ffd83dbSDimitry Andric       return true;
1935ffd83dbSDimitry Andric 
1945ffd83dbSDimitry Andric     if (isCtorOfRefCounted(Callee))
1955ffd83dbSDimitry Andric       return true;
1965ffd83dbSDimitry Andric 
1975ffd83dbSDimitry Andric     auto name = safeGetName(Callee);
1985ffd83dbSDimitry Andric     if (name == "adoptRef" || name == "getPtr" || name == "WeakPtr" ||
199*0fca6ea1SDimitry Andric         name == "dynamicDowncast" || name == "downcast" ||
200*0fca6ea1SDimitry Andric         name == "checkedDowncast" || name == "uncheckedDowncast" ||
201*0fca6ea1SDimitry Andric         name == "bitwise_cast" || name == "is" || name == "equal" ||
202*0fca6ea1SDimitry Andric         name == "hash" || name == "isType" ||
2035ffd83dbSDimitry Andric         // FIXME: Most/all of these should be implemented via attributes.
204*0fca6ea1SDimitry Andric         name == "equalIgnoringASCIICase" ||
2055ffd83dbSDimitry Andric         name == "equalIgnoringASCIICaseCommon" ||
206*0fca6ea1SDimitry Andric         name == "equalIgnoringNullity" || name == "toString")
2075ffd83dbSDimitry Andric       return true;
2085ffd83dbSDimitry Andric 
2095ffd83dbSDimitry Andric     return false;
2105ffd83dbSDimitry Andric   }
2115ffd83dbSDimitry Andric 
212*0fca6ea1SDimitry Andric   bool isMethodOnWTFContainerType(const FunctionDecl *Decl) const {
213*0fca6ea1SDimitry Andric     if (!isa<CXXMethodDecl>(Decl))
214*0fca6ea1SDimitry Andric       return false;
215*0fca6ea1SDimitry Andric     auto *ClassDecl = Decl->getParent();
216*0fca6ea1SDimitry Andric     if (!ClassDecl || !isa<CXXRecordDecl>(ClassDecl))
217*0fca6ea1SDimitry Andric       return false;
218*0fca6ea1SDimitry Andric 
219*0fca6ea1SDimitry Andric     auto *NsDecl = ClassDecl->getParent();
220*0fca6ea1SDimitry Andric     if (!NsDecl || !isa<NamespaceDecl>(NsDecl))
221*0fca6ea1SDimitry Andric       return false;
222*0fca6ea1SDimitry Andric 
223*0fca6ea1SDimitry Andric     auto MethodName = safeGetName(Decl);
224*0fca6ea1SDimitry Andric     auto ClsNameStr = safeGetName(ClassDecl);
225*0fca6ea1SDimitry Andric     StringRef ClsName = ClsNameStr; // FIXME: Make safeGetName return StringRef.
226*0fca6ea1SDimitry Andric     auto NamespaceName = safeGetName(NsDecl);
227*0fca6ea1SDimitry Andric     // FIXME: These should be implemented via attributes.
228*0fca6ea1SDimitry Andric     return NamespaceName == "WTF" &&
229*0fca6ea1SDimitry Andric            (MethodName == "find" || MethodName == "findIf" ||
230*0fca6ea1SDimitry Andric             MethodName == "reverseFind" || MethodName == "reverseFindIf" ||
231*0fca6ea1SDimitry Andric             MethodName == "findIgnoringASCIICase" || MethodName == "get" ||
232*0fca6ea1SDimitry Andric             MethodName == "inlineGet" || MethodName == "contains" ||
233*0fca6ea1SDimitry Andric             MethodName == "containsIf" ||
234*0fca6ea1SDimitry Andric             MethodName == "containsIgnoringASCIICase" ||
235*0fca6ea1SDimitry Andric             MethodName == "startsWith" || MethodName == "endsWith" ||
236*0fca6ea1SDimitry Andric             MethodName == "startsWithIgnoringASCIICase" ||
237*0fca6ea1SDimitry Andric             MethodName == "endsWithIgnoringASCIICase" ||
238*0fca6ea1SDimitry Andric             MethodName == "substring") &&
239*0fca6ea1SDimitry Andric            (ClsName.ends_with("Vector") || ClsName.ends_with("Set") ||
240*0fca6ea1SDimitry Andric             ClsName.ends_with("Map") || ClsName == "StringImpl" ||
241*0fca6ea1SDimitry Andric             ClsName.ends_with("String"));
242*0fca6ea1SDimitry Andric   }
243*0fca6ea1SDimitry Andric 
2445ffd83dbSDimitry Andric   void reportBug(const Expr *CallArg, const ParmVarDecl *Param) const {
2455ffd83dbSDimitry Andric     assert(CallArg);
2465ffd83dbSDimitry Andric 
2475ffd83dbSDimitry Andric     SmallString<100> Buf;
2485ffd83dbSDimitry Andric     llvm::raw_svector_ostream Os(Buf);
2495ffd83dbSDimitry Andric 
2505ffd83dbSDimitry Andric     const std::string paramName = safeGetName(Param);
2515ffd83dbSDimitry Andric     Os << "Call argument";
2525ffd83dbSDimitry Andric     if (!paramName.empty()) {
2535ffd83dbSDimitry Andric       Os << " for parameter ";
2545ffd83dbSDimitry Andric       printQuotedQualifiedName(Os, Param);
2555ffd83dbSDimitry Andric     }
2565ffd83dbSDimitry Andric     Os << " is uncounted and unsafe.";
2575ffd83dbSDimitry Andric 
2585ffd83dbSDimitry Andric     const SourceLocation SrcLocToReport =
2595ffd83dbSDimitry Andric         isa<CXXDefaultArgExpr>(CallArg) ? Param->getDefaultArg()->getExprLoc()
2605ffd83dbSDimitry Andric                                         : CallArg->getSourceRange().getBegin();
2615ffd83dbSDimitry Andric 
2625ffd83dbSDimitry Andric     PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
2635ffd83dbSDimitry Andric     auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
2645ffd83dbSDimitry Andric     Report->addRange(CallArg->getSourceRange());
2655ffd83dbSDimitry Andric     BR->emitReport(std::move(Report));
2665ffd83dbSDimitry Andric   }
267*0fca6ea1SDimitry Andric 
268*0fca6ea1SDimitry Andric   void reportBugOnThis(const Expr *CallArg) const {
269*0fca6ea1SDimitry Andric     assert(CallArg);
270*0fca6ea1SDimitry Andric 
271*0fca6ea1SDimitry Andric     const SourceLocation SrcLocToReport = CallArg->getSourceRange().getBegin();
272*0fca6ea1SDimitry Andric 
273*0fca6ea1SDimitry Andric     PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
274*0fca6ea1SDimitry Andric     auto Report = std::make_unique<BasicBugReport>(
275*0fca6ea1SDimitry Andric         Bug, "Call argument for 'this' parameter is uncounted and unsafe.",
276*0fca6ea1SDimitry Andric         BSLoc);
277*0fca6ea1SDimitry Andric     Report->addRange(CallArg->getSourceRange());
278*0fca6ea1SDimitry Andric     BR->emitReport(std::move(Report));
279*0fca6ea1SDimitry Andric   }
2805ffd83dbSDimitry Andric };
2815ffd83dbSDimitry Andric } // namespace
2825ffd83dbSDimitry Andric 
2835ffd83dbSDimitry Andric void ento::registerUncountedCallArgsChecker(CheckerManager &Mgr) {
2845ffd83dbSDimitry Andric   Mgr.registerChecker<UncountedCallArgsChecker>();
2855ffd83dbSDimitry Andric }
2865ffd83dbSDimitry Andric 
2875ffd83dbSDimitry Andric bool ento::shouldRegisterUncountedCallArgsChecker(const CheckerManager &) {
2885ffd83dbSDimitry Andric   return true;
2895ffd83dbSDimitry Andric }
290