1a7eb3692SJan Korous //=======- UncountedCallArgsChecker.cpp --------------------------*- C++ -*-==//
2a7eb3692SJan Korous //
3a7eb3692SJan Korous // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4a7eb3692SJan Korous // See https://llvm.org/LICENSE.txt for license information.
5a7eb3692SJan Korous // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6a7eb3692SJan Korous //
7a7eb3692SJan Korous //===----------------------------------------------------------------------===//
8a7eb3692SJan Korous 
9a7eb3692SJan Korous #include "ASTUtils.h"
10a7eb3692SJan Korous #include "DiagOutputUtils.h"
11a7eb3692SJan Korous #include "PtrTypesSemantics.h"
12a7eb3692SJan Korous #include "clang/AST/CXXInheritance.h"
13a7eb3692SJan Korous #include "clang/AST/Decl.h"
14a7eb3692SJan Korous #include "clang/AST/DeclCXX.h"
15a7eb3692SJan Korous #include "clang/AST/RecursiveASTVisitor.h"
16a7eb3692SJan Korous #include "clang/Basic/SourceLocation.h"
17a7eb3692SJan Korous #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
18a7eb3692SJan Korous #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
19a7eb3692SJan Korous #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
20a7eb3692SJan Korous #include "clang/StaticAnalyzer/Core/Checker.h"
21a7eb3692SJan Korous #include "llvm/ADT/DenseSet.h"
22a7eb3692SJan Korous 
23a7eb3692SJan Korous using namespace clang;
24a7eb3692SJan Korous using namespace ento;
25a7eb3692SJan Korous 
26a7eb3692SJan Korous namespace {
27a7eb3692SJan Korous 
28a7eb3692SJan Korous class UncountedCallArgsChecker
29a7eb3692SJan Korous     : public Checker<check::ASTDecl<TranslationUnitDecl>> {
30a7eb3692SJan Korous   BugType Bug{this,
31a7eb3692SJan Korous             "Uncounted call argument for a raw pointer/reference parameter",
32a7eb3692SJan Korous             "WebKit coding guidelines"};
33a7eb3692SJan Korous   mutable BugReporter *BR;
34a7eb3692SJan Korous 
35a7eb3692SJan Korous public:
36a7eb3692SJan Korous 
checkASTDecl(const TranslationUnitDecl * TUD,AnalysisManager & MGR,BugReporter & BRArg) const37a7eb3692SJan Korous   void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
38a7eb3692SJan Korous                     BugReporter &BRArg) const {
39a7eb3692SJan Korous     BR = &BRArg;
40a7eb3692SJan Korous 
41a7eb3692SJan Korous     // The calls to checkAST* from AnalysisConsumer don't
42a7eb3692SJan Korous     // visit template instantiations or lambda classes. We
43a7eb3692SJan Korous     // want to visit those, so we make our own RecursiveASTVisitor.
44a7eb3692SJan Korous     struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
45a7eb3692SJan Korous       const UncountedCallArgsChecker *Checker;
46a7eb3692SJan Korous       explicit LocalVisitor(const UncountedCallArgsChecker *Checker)
47a7eb3692SJan Korous           : Checker(Checker) {
48a7eb3692SJan Korous         assert(Checker);
49a7eb3692SJan Korous       }
50a7eb3692SJan Korous 
51a7eb3692SJan Korous       bool shouldVisitTemplateInstantiations() const { return true; }
52a7eb3692SJan Korous       bool shouldVisitImplicitCode() const { return false; }
53a7eb3692SJan Korous 
54a7eb3692SJan Korous       bool VisitCallExpr(const CallExpr *CE) {
55a7eb3692SJan Korous         Checker->visitCallExpr(CE);
56a7eb3692SJan Korous         return true;
57a7eb3692SJan Korous       }
58a7eb3692SJan Korous     };
59a7eb3692SJan Korous 
60a7eb3692SJan Korous     LocalVisitor visitor(this);
61a7eb3692SJan Korous     visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
62a7eb3692SJan Korous   }
63a7eb3692SJan Korous 
visitCallExpr(const CallExpr * CE) const64a7eb3692SJan Korous   void visitCallExpr(const CallExpr *CE) const {
65a7eb3692SJan Korous     if (shouldSkipCall(CE))
66a7eb3692SJan Korous       return;
67a7eb3692SJan Korous 
68a7eb3692SJan Korous     if (auto *F = CE->getDirectCallee()) {
69a7eb3692SJan Korous       // Skip the first argument for overloaded member operators (e. g. lambda
70a7eb3692SJan Korous       // or std::function call operator).
71*d0ac215dSKazu Hirata       unsigned ArgIdx = isa<CXXOperatorCallExpr>(CE) && isa_and_nonnull<CXXMethodDecl>(F);
72a7eb3692SJan Korous 
73a7eb3692SJan Korous       for (auto P = F->param_begin();
74a7eb3692SJan Korous            // FIXME: Also check variadic function parameters.
75a7eb3692SJan Korous            // FIXME: Also check default function arguments. Probably a different
76a7eb3692SJan Korous            // checker. In case there are default arguments the call can have
77a7eb3692SJan Korous            // fewer arguments than the callee has parameters.
78a7eb3692SJan Korous            P < F->param_end() && ArgIdx < CE->getNumArgs(); ++P, ++ArgIdx) {
79a7eb3692SJan Korous         // TODO: attributes.
80a7eb3692SJan Korous         // if ((*P)->hasAttr<SafeRefCntblRawPtrAttr>())
81a7eb3692SJan Korous         //  continue;
82a7eb3692SJan Korous 
83a7eb3692SJan Korous         const auto *ArgType = (*P)->getType().getTypePtrOrNull();
84a7eb3692SJan Korous         if (!ArgType)
85a7eb3692SJan Korous           continue; // FIXME? Should we bail?
86a7eb3692SJan Korous 
87a7eb3692SJan Korous         // FIXME: more complex types (arrays, references to raw pointers, etc)
8847e68514SJan Korous         Optional<bool> IsUncounted = isUncountedPtr(ArgType);
8947e68514SJan Korous         if (!IsUncounted || !(*IsUncounted))
90a7eb3692SJan Korous           continue;
91a7eb3692SJan Korous 
92a7eb3692SJan Korous         const auto *Arg = CE->getArg(ArgIdx);
93a7eb3692SJan Korous 
94a7eb3692SJan Korous         std::pair<const clang::Expr *, bool> ArgOrigin =
95a7eb3692SJan Korous             tryToFindPtrOrigin(Arg, true);
96a7eb3692SJan Korous 
97a7eb3692SJan Korous         // Temporary ref-counted object created as part of the call argument
98a7eb3692SJan Korous         // would outlive the call.
99a7eb3692SJan Korous         if (ArgOrigin.second)
100a7eb3692SJan Korous           continue;
101a7eb3692SJan Korous 
102a7eb3692SJan Korous         if (isa<CXXNullPtrLiteralExpr>(ArgOrigin.first)) {
103a7eb3692SJan Korous           // foo(nullptr)
104a7eb3692SJan Korous           continue;
105a7eb3692SJan Korous         }
106a7eb3692SJan Korous         if (isa<IntegerLiteral>(ArgOrigin.first)) {
107a7eb3692SJan Korous           // FIXME: Check the value.
108a7eb3692SJan Korous           // foo(NULL)
109a7eb3692SJan Korous           continue;
110a7eb3692SJan Korous         }
111a7eb3692SJan Korous 
112a7eb3692SJan Korous         if (isASafeCallArg(ArgOrigin.first))
113a7eb3692SJan Korous           continue;
114a7eb3692SJan Korous 
115a7eb3692SJan Korous         reportBug(Arg, *P);
116a7eb3692SJan Korous       }
117a7eb3692SJan Korous     }
118a7eb3692SJan Korous   }
119a7eb3692SJan Korous 
shouldSkipCall(const CallExpr * CE) const120a7eb3692SJan Korous   bool shouldSkipCall(const CallExpr *CE) const {
121a7eb3692SJan Korous     if (CE->getNumArgs() == 0)
122a7eb3692SJan Korous       return false;
123a7eb3692SJan Korous 
124a7eb3692SJan Korous     // If an assignment is problematic we should warn about the sole existence
125a7eb3692SJan Korous     // of object on LHS.
126a7eb3692SJan Korous     if (auto *MemberOp = dyn_cast<CXXOperatorCallExpr>(CE)) {
127a7eb3692SJan Korous       // Note: assignemnt to built-in type isn't derived from CallExpr.
128a7eb3692SJan Korous       if (MemberOp->isAssignmentOp())
129a7eb3692SJan Korous         return false;
130a7eb3692SJan Korous     }
131a7eb3692SJan Korous 
132a7eb3692SJan Korous     const auto *Callee = CE->getDirectCallee();
133a7eb3692SJan Korous     if (!Callee)
134a7eb3692SJan Korous       return false;
135a7eb3692SJan Korous 
136a7eb3692SJan Korous     auto overloadedOperatorType = Callee->getOverloadedOperator();
137a7eb3692SJan Korous     if (overloadedOperatorType == OO_EqualEqual ||
138a7eb3692SJan Korous         overloadedOperatorType == OO_ExclaimEqual ||
139a7eb3692SJan Korous         overloadedOperatorType == OO_LessEqual ||
140a7eb3692SJan Korous         overloadedOperatorType == OO_GreaterEqual ||
141a7eb3692SJan Korous         overloadedOperatorType == OO_Spaceship ||
142a7eb3692SJan Korous         overloadedOperatorType == OO_AmpAmp ||
143a7eb3692SJan Korous         overloadedOperatorType == OO_PipePipe)
144a7eb3692SJan Korous       return true;
145a7eb3692SJan Korous 
146a7eb3692SJan Korous     if (isCtorOfRefCounted(Callee))
147a7eb3692SJan Korous       return true;
148a7eb3692SJan Korous 
149a7eb3692SJan Korous     auto name = safeGetName(Callee);
150a7eb3692SJan Korous     if (name == "adoptRef" || name == "getPtr" || name == "WeakPtr" ||
151a7eb3692SJan Korous         name == "makeWeakPtr" || name == "downcast" || name == "bitwise_cast" ||
152a7eb3692SJan Korous         name == "is" || name == "equal" || name == "hash" ||
153a7eb3692SJan Korous         name == "isType"
154a7eb3692SJan Korous         // FIXME: Most/all of these should be implemented via attributes.
155a7eb3692SJan Korous         || name == "equalIgnoringASCIICase" ||
156a7eb3692SJan Korous         name == "equalIgnoringASCIICaseCommon" ||
157a7eb3692SJan Korous         name == "equalIgnoringNullity")
158a7eb3692SJan Korous       return true;
159a7eb3692SJan Korous 
160a7eb3692SJan Korous     return false;
161a7eb3692SJan Korous   }
162a7eb3692SJan Korous 
reportBug(const Expr * CallArg,const ParmVarDecl * Param) const163a7eb3692SJan Korous   void reportBug(const Expr *CallArg, const ParmVarDecl *Param) const {
164a7eb3692SJan Korous     assert(CallArg);
165a7eb3692SJan Korous 
166a7eb3692SJan Korous     SmallString<100> Buf;
167a7eb3692SJan Korous     llvm::raw_svector_ostream Os(Buf);
168a7eb3692SJan Korous 
169a7eb3692SJan Korous     const std::string paramName = safeGetName(Param);
170a7eb3692SJan Korous     Os << "Call argument";
171a7eb3692SJan Korous     if (!paramName.empty()) {
172a7eb3692SJan Korous       Os << " for parameter ";
173a7eb3692SJan Korous       printQuotedQualifiedName(Os, Param);
174a7eb3692SJan Korous     }
175a7eb3692SJan Korous     Os << " is uncounted and unsafe.";
176a7eb3692SJan Korous 
177a7eb3692SJan Korous     const SourceLocation SrcLocToReport =
178a7eb3692SJan Korous         isa<CXXDefaultArgExpr>(CallArg) ? Param->getDefaultArg()->getExprLoc()
179a7eb3692SJan Korous                                         : CallArg->getSourceRange().getBegin();
180a7eb3692SJan Korous 
181a7eb3692SJan Korous     PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
182a7eb3692SJan Korous     auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
183a7eb3692SJan Korous     Report->addRange(CallArg->getSourceRange());
184a7eb3692SJan Korous     BR->emitReport(std::move(Report));
185a7eb3692SJan Korous   }
186a7eb3692SJan Korous };
187a7eb3692SJan Korous } // namespace
188a7eb3692SJan Korous 
registerUncountedCallArgsChecker(CheckerManager & Mgr)189a7eb3692SJan Korous void ento::registerUncountedCallArgsChecker(CheckerManager &Mgr) {
190a7eb3692SJan Korous   Mgr.registerChecker<UncountedCallArgsChecker>();
191a7eb3692SJan Korous }
192a7eb3692SJan Korous 
shouldRegisterUncountedCallArgsChecker(const CheckerManager &)193a7eb3692SJan Korous bool ento::shouldRegisterUncountedCallArgsChecker(const CheckerManager &) {
194a7eb3692SJan Korous   return true;
195a7eb3692SJan Korous }
196