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