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"
215ffd83dbSDimitry Andric #include "llvm/ADT/DenseSet.h"
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 
355ffd83dbSDimitry Andric public:
365ffd83dbSDimitry Andric 
375ffd83dbSDimitry Andric   void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
385ffd83dbSDimitry Andric                     BugReporter &BRArg) const {
395ffd83dbSDimitry Andric     BR = &BRArg;
405ffd83dbSDimitry Andric 
415ffd83dbSDimitry Andric     // The calls to checkAST* from AnalysisConsumer don't
425ffd83dbSDimitry Andric     // visit template instantiations or lambda classes. We
435ffd83dbSDimitry Andric     // want to visit those, so we make our own RecursiveASTVisitor.
445ffd83dbSDimitry Andric     struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
455ffd83dbSDimitry Andric       const UncountedCallArgsChecker *Checker;
465ffd83dbSDimitry Andric       explicit LocalVisitor(const UncountedCallArgsChecker *Checker)
475ffd83dbSDimitry Andric           : Checker(Checker) {
485ffd83dbSDimitry Andric         assert(Checker);
495ffd83dbSDimitry Andric       }
505ffd83dbSDimitry Andric 
515ffd83dbSDimitry Andric       bool shouldVisitTemplateInstantiations() const { return true; }
525ffd83dbSDimitry Andric       bool shouldVisitImplicitCode() const { return false; }
535ffd83dbSDimitry Andric 
545ffd83dbSDimitry Andric       bool VisitCallExpr(const CallExpr *CE) {
555ffd83dbSDimitry Andric         Checker->visitCallExpr(CE);
565ffd83dbSDimitry Andric         return true;
575ffd83dbSDimitry Andric       }
585ffd83dbSDimitry Andric     };
595ffd83dbSDimitry Andric 
605ffd83dbSDimitry Andric     LocalVisitor visitor(this);
615ffd83dbSDimitry Andric     visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
625ffd83dbSDimitry Andric   }
635ffd83dbSDimitry Andric 
645ffd83dbSDimitry Andric   void visitCallExpr(const CallExpr *CE) const {
655ffd83dbSDimitry Andric     if (shouldSkipCall(CE))
665ffd83dbSDimitry Andric       return;
675ffd83dbSDimitry Andric 
685ffd83dbSDimitry Andric     if (auto *F = CE->getDirectCallee()) {
695ffd83dbSDimitry Andric       // Skip the first argument for overloaded member operators (e. g. lambda
705ffd83dbSDimitry Andric       // or std::function call operator).
715ffd83dbSDimitry Andric       unsigned ArgIdx =
725ffd83dbSDimitry Andric           isa<CXXOperatorCallExpr>(CE) && dyn_cast_or_null<CXXMethodDecl>(F);
735ffd83dbSDimitry Andric 
745ffd83dbSDimitry Andric       for (auto P = F->param_begin();
755ffd83dbSDimitry Andric            // FIXME: Also check variadic function parameters.
765ffd83dbSDimitry Andric            // FIXME: Also check default function arguments. Probably a different
775ffd83dbSDimitry Andric            // checker. In case there are default arguments the call can have
785ffd83dbSDimitry Andric            // fewer arguments than the callee has parameters.
795ffd83dbSDimitry Andric            P < F->param_end() && ArgIdx < CE->getNumArgs(); ++P, ++ArgIdx) {
805ffd83dbSDimitry Andric         // TODO: attributes.
815ffd83dbSDimitry Andric         // if ((*P)->hasAttr<SafeRefCntblRawPtrAttr>())
825ffd83dbSDimitry Andric         //  continue;
835ffd83dbSDimitry Andric 
845ffd83dbSDimitry Andric         const auto *ArgType = (*P)->getType().getTypePtrOrNull();
855ffd83dbSDimitry Andric         if (!ArgType)
865ffd83dbSDimitry Andric           continue; // FIXME? Should we bail?
875ffd83dbSDimitry Andric 
885ffd83dbSDimitry Andric         // FIXME: more complex types (arrays, references to raw pointers, etc)
89*e8d8bef9SDimitry Andric         Optional<bool> IsUncounted = isUncountedPtr(ArgType);
90*e8d8bef9SDimitry Andric         if (!IsUncounted || !(*IsUncounted))
915ffd83dbSDimitry Andric           continue;
925ffd83dbSDimitry Andric 
935ffd83dbSDimitry Andric         const auto *Arg = CE->getArg(ArgIdx);
945ffd83dbSDimitry Andric 
955ffd83dbSDimitry Andric         std::pair<const clang::Expr *, bool> ArgOrigin =
965ffd83dbSDimitry Andric             tryToFindPtrOrigin(Arg, true);
975ffd83dbSDimitry Andric 
985ffd83dbSDimitry Andric         // Temporary ref-counted object created as part of the call argument
995ffd83dbSDimitry Andric         // would outlive the call.
1005ffd83dbSDimitry Andric         if (ArgOrigin.second)
1015ffd83dbSDimitry Andric           continue;
1025ffd83dbSDimitry Andric 
1035ffd83dbSDimitry Andric         if (isa<CXXNullPtrLiteralExpr>(ArgOrigin.first)) {
1045ffd83dbSDimitry Andric           // foo(nullptr)
1055ffd83dbSDimitry Andric           continue;
1065ffd83dbSDimitry Andric         }
1075ffd83dbSDimitry Andric         if (isa<IntegerLiteral>(ArgOrigin.first)) {
1085ffd83dbSDimitry Andric           // FIXME: Check the value.
1095ffd83dbSDimitry Andric           // foo(NULL)
1105ffd83dbSDimitry Andric           continue;
1115ffd83dbSDimitry Andric         }
1125ffd83dbSDimitry Andric 
1135ffd83dbSDimitry Andric         if (isASafeCallArg(ArgOrigin.first))
1145ffd83dbSDimitry Andric           continue;
1155ffd83dbSDimitry Andric 
1165ffd83dbSDimitry Andric         reportBug(Arg, *P);
1175ffd83dbSDimitry Andric       }
1185ffd83dbSDimitry Andric     }
1195ffd83dbSDimitry Andric   }
1205ffd83dbSDimitry Andric 
1215ffd83dbSDimitry Andric   bool shouldSkipCall(const CallExpr *CE) const {
1225ffd83dbSDimitry Andric     if (CE->getNumArgs() == 0)
1235ffd83dbSDimitry Andric       return false;
1245ffd83dbSDimitry Andric 
1255ffd83dbSDimitry Andric     // If an assignment is problematic we should warn about the sole existence
1265ffd83dbSDimitry Andric     // of object on LHS.
1275ffd83dbSDimitry Andric     if (auto *MemberOp = dyn_cast<CXXOperatorCallExpr>(CE)) {
1285ffd83dbSDimitry Andric       // Note: assignemnt to built-in type isn't derived from CallExpr.
1295ffd83dbSDimitry Andric       if (MemberOp->isAssignmentOp())
1305ffd83dbSDimitry Andric         return false;
1315ffd83dbSDimitry Andric     }
1325ffd83dbSDimitry Andric 
1335ffd83dbSDimitry Andric     const auto *Callee = CE->getDirectCallee();
1345ffd83dbSDimitry Andric     if (!Callee)
1355ffd83dbSDimitry Andric       return false;
1365ffd83dbSDimitry Andric 
1375ffd83dbSDimitry Andric     auto overloadedOperatorType = Callee->getOverloadedOperator();
1385ffd83dbSDimitry Andric     if (overloadedOperatorType == OO_EqualEqual ||
1395ffd83dbSDimitry Andric         overloadedOperatorType == OO_ExclaimEqual ||
1405ffd83dbSDimitry Andric         overloadedOperatorType == OO_LessEqual ||
1415ffd83dbSDimitry Andric         overloadedOperatorType == OO_GreaterEqual ||
1425ffd83dbSDimitry Andric         overloadedOperatorType == OO_Spaceship ||
1435ffd83dbSDimitry Andric         overloadedOperatorType == OO_AmpAmp ||
1445ffd83dbSDimitry Andric         overloadedOperatorType == OO_PipePipe)
1455ffd83dbSDimitry Andric       return true;
1465ffd83dbSDimitry Andric 
1475ffd83dbSDimitry Andric     if (isCtorOfRefCounted(Callee))
1485ffd83dbSDimitry Andric       return true;
1495ffd83dbSDimitry Andric 
1505ffd83dbSDimitry Andric     auto name = safeGetName(Callee);
1515ffd83dbSDimitry Andric     if (name == "adoptRef" || name == "getPtr" || name == "WeakPtr" ||
1525ffd83dbSDimitry Andric         name == "makeWeakPtr" || name == "downcast" || name == "bitwise_cast" ||
1535ffd83dbSDimitry Andric         name == "is" || name == "equal" || name == "hash" ||
1545ffd83dbSDimitry Andric         name == "isType"
1555ffd83dbSDimitry Andric         // FIXME: Most/all of these should be implemented via attributes.
1565ffd83dbSDimitry Andric         || name == "equalIgnoringASCIICase" ||
1575ffd83dbSDimitry Andric         name == "equalIgnoringASCIICaseCommon" ||
1585ffd83dbSDimitry Andric         name == "equalIgnoringNullity")
1595ffd83dbSDimitry Andric       return true;
1605ffd83dbSDimitry Andric 
1615ffd83dbSDimitry Andric     return false;
1625ffd83dbSDimitry Andric   }
1635ffd83dbSDimitry Andric 
1645ffd83dbSDimitry Andric   void reportBug(const Expr *CallArg, const ParmVarDecl *Param) const {
1655ffd83dbSDimitry Andric     assert(CallArg);
1665ffd83dbSDimitry Andric 
1675ffd83dbSDimitry Andric     SmallString<100> Buf;
1685ffd83dbSDimitry Andric     llvm::raw_svector_ostream Os(Buf);
1695ffd83dbSDimitry Andric 
1705ffd83dbSDimitry Andric     const std::string paramName = safeGetName(Param);
1715ffd83dbSDimitry Andric     Os << "Call argument";
1725ffd83dbSDimitry Andric     if (!paramName.empty()) {
1735ffd83dbSDimitry Andric       Os << " for parameter ";
1745ffd83dbSDimitry Andric       printQuotedQualifiedName(Os, Param);
1755ffd83dbSDimitry Andric     }
1765ffd83dbSDimitry Andric     Os << " is uncounted and unsafe.";
1775ffd83dbSDimitry Andric 
1785ffd83dbSDimitry Andric     const SourceLocation SrcLocToReport =
1795ffd83dbSDimitry Andric         isa<CXXDefaultArgExpr>(CallArg) ? Param->getDefaultArg()->getExprLoc()
1805ffd83dbSDimitry Andric                                         : CallArg->getSourceRange().getBegin();
1815ffd83dbSDimitry Andric 
1825ffd83dbSDimitry Andric     PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
1835ffd83dbSDimitry Andric     auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
1845ffd83dbSDimitry Andric     Report->addRange(CallArg->getSourceRange());
1855ffd83dbSDimitry Andric     BR->emitReport(std::move(Report));
1865ffd83dbSDimitry Andric   }
1875ffd83dbSDimitry Andric };
1885ffd83dbSDimitry Andric } // namespace
1895ffd83dbSDimitry Andric 
1905ffd83dbSDimitry Andric void ento::registerUncountedCallArgsChecker(CheckerManager &Mgr) {
1915ffd83dbSDimitry Andric   Mgr.registerChecker<UncountedCallArgsChecker>();
1925ffd83dbSDimitry Andric }
1935ffd83dbSDimitry Andric 
1945ffd83dbSDimitry Andric bool ento::shouldRegisterUncountedCallArgsChecker(const CheckerManager &) {
1955ffd83dbSDimitry Andric   return true;
1965ffd83dbSDimitry Andric }
197