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