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