1*e038c9c4Sjoerg //=======- UncountedLocalVarsChecker.cpp -------------------------*- C++ -*-==//
2*e038c9c4Sjoerg //
3*e038c9c4Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*e038c9c4Sjoerg // See https://llvm.org/LICENSE.txt for license information.
5*e038c9c4Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*e038c9c4Sjoerg //
7*e038c9c4Sjoerg //===----------------------------------------------------------------------===//
8*e038c9c4Sjoerg
9*e038c9c4Sjoerg #include "ASTUtils.h"
10*e038c9c4Sjoerg #include "DiagOutputUtils.h"
11*e038c9c4Sjoerg #include "PtrTypesSemantics.h"
12*e038c9c4Sjoerg #include "clang/AST/CXXInheritance.h"
13*e038c9c4Sjoerg #include "clang/AST/Decl.h"
14*e038c9c4Sjoerg #include "clang/AST/DeclCXX.h"
15*e038c9c4Sjoerg #include "clang/AST/ParentMapContext.h"
16*e038c9c4Sjoerg #include "clang/AST/RecursiveASTVisitor.h"
17*e038c9c4Sjoerg #include "clang/Basic/SourceLocation.h"
18*e038c9c4Sjoerg #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
19*e038c9c4Sjoerg #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
20*e038c9c4Sjoerg #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
21*e038c9c4Sjoerg #include "clang/StaticAnalyzer/Core/Checker.h"
22*e038c9c4Sjoerg #include "llvm/ADT/DenseSet.h"
23*e038c9c4Sjoerg
24*e038c9c4Sjoerg using namespace clang;
25*e038c9c4Sjoerg using namespace ento;
26*e038c9c4Sjoerg
27*e038c9c4Sjoerg namespace {
28*e038c9c4Sjoerg
29*e038c9c4Sjoerg // for ( int a = ...) ... true
30*e038c9c4Sjoerg // for ( int a : ...) ... true
31*e038c9c4Sjoerg // if ( int* a = ) ... true
32*e038c9c4Sjoerg // anything else ... false
isDeclaredInForOrIf(const VarDecl * Var)33*e038c9c4Sjoerg bool isDeclaredInForOrIf(const VarDecl *Var) {
34*e038c9c4Sjoerg assert(Var);
35*e038c9c4Sjoerg auto &ASTCtx = Var->getASTContext();
36*e038c9c4Sjoerg auto parent = ASTCtx.getParents(*Var);
37*e038c9c4Sjoerg
38*e038c9c4Sjoerg if (parent.size() == 1) {
39*e038c9c4Sjoerg if (auto *DS = parent.begin()->get<DeclStmt>()) {
40*e038c9c4Sjoerg DynTypedNodeList grandParent = ASTCtx.getParents(*DS);
41*e038c9c4Sjoerg if (grandParent.size() == 1) {
42*e038c9c4Sjoerg return grandParent.begin()->get<ForStmt>() ||
43*e038c9c4Sjoerg grandParent.begin()->get<IfStmt>() ||
44*e038c9c4Sjoerg grandParent.begin()->get<CXXForRangeStmt>();
45*e038c9c4Sjoerg }
46*e038c9c4Sjoerg }
47*e038c9c4Sjoerg }
48*e038c9c4Sjoerg return false;
49*e038c9c4Sjoerg }
50*e038c9c4Sjoerg
51*e038c9c4Sjoerg // FIXME: should be defined by anotations in the future
isRefcountedStringsHack(const VarDecl * V)52*e038c9c4Sjoerg bool isRefcountedStringsHack(const VarDecl *V) {
53*e038c9c4Sjoerg assert(V);
54*e038c9c4Sjoerg auto safeClass = [](const std::string &className) {
55*e038c9c4Sjoerg return className == "String" || className == "AtomString" ||
56*e038c9c4Sjoerg className == "UniquedString" || className == "Identifier";
57*e038c9c4Sjoerg };
58*e038c9c4Sjoerg QualType QT = V->getType();
59*e038c9c4Sjoerg auto *T = QT.getTypePtr();
60*e038c9c4Sjoerg if (auto *CXXRD = T->getAsCXXRecordDecl()) {
61*e038c9c4Sjoerg if (safeClass(safeGetName(CXXRD)))
62*e038c9c4Sjoerg return true;
63*e038c9c4Sjoerg }
64*e038c9c4Sjoerg if (T->isPointerType() || T->isReferenceType()) {
65*e038c9c4Sjoerg if (auto *CXXRD = T->getPointeeCXXRecordDecl()) {
66*e038c9c4Sjoerg if (safeClass(safeGetName(CXXRD)))
67*e038c9c4Sjoerg return true;
68*e038c9c4Sjoerg }
69*e038c9c4Sjoerg }
70*e038c9c4Sjoerg return false;
71*e038c9c4Sjoerg }
72*e038c9c4Sjoerg
isGuardedScopeEmbeddedInGuardianScope(const VarDecl * Guarded,const VarDecl * MaybeGuardian)73*e038c9c4Sjoerg bool isGuardedScopeEmbeddedInGuardianScope(const VarDecl *Guarded,
74*e038c9c4Sjoerg const VarDecl *MaybeGuardian) {
75*e038c9c4Sjoerg assert(Guarded);
76*e038c9c4Sjoerg assert(MaybeGuardian);
77*e038c9c4Sjoerg
78*e038c9c4Sjoerg if (!MaybeGuardian->isLocalVarDecl())
79*e038c9c4Sjoerg return false;
80*e038c9c4Sjoerg
81*e038c9c4Sjoerg const CompoundStmt *guardiansClosestCompStmtAncestor = nullptr;
82*e038c9c4Sjoerg
83*e038c9c4Sjoerg ASTContext &ctx = MaybeGuardian->getASTContext();
84*e038c9c4Sjoerg
85*e038c9c4Sjoerg for (DynTypedNodeList guardianAncestors = ctx.getParents(*MaybeGuardian);
86*e038c9c4Sjoerg !guardianAncestors.empty();
87*e038c9c4Sjoerg guardianAncestors = ctx.getParents(
88*e038c9c4Sjoerg *guardianAncestors
89*e038c9c4Sjoerg .begin()) // FIXME - should we handle all of the parents?
90*e038c9c4Sjoerg ) {
91*e038c9c4Sjoerg for (auto &guardianAncestor : guardianAncestors) {
92*e038c9c4Sjoerg if (auto *CStmtParentAncestor = guardianAncestor.get<CompoundStmt>()) {
93*e038c9c4Sjoerg guardiansClosestCompStmtAncestor = CStmtParentAncestor;
94*e038c9c4Sjoerg break;
95*e038c9c4Sjoerg }
96*e038c9c4Sjoerg }
97*e038c9c4Sjoerg if (guardiansClosestCompStmtAncestor)
98*e038c9c4Sjoerg break;
99*e038c9c4Sjoerg }
100*e038c9c4Sjoerg
101*e038c9c4Sjoerg if (!guardiansClosestCompStmtAncestor)
102*e038c9c4Sjoerg return false;
103*e038c9c4Sjoerg
104*e038c9c4Sjoerg // We need to skip the first CompoundStmt to avoid situation when guardian is
105*e038c9c4Sjoerg // defined in the same scope as guarded variable.
106*e038c9c4Sjoerg bool HaveSkippedFirstCompoundStmt = false;
107*e038c9c4Sjoerg for (DynTypedNodeList guardedVarAncestors = ctx.getParents(*Guarded);
108*e038c9c4Sjoerg !guardedVarAncestors.empty();
109*e038c9c4Sjoerg guardedVarAncestors = ctx.getParents(
110*e038c9c4Sjoerg *guardedVarAncestors
111*e038c9c4Sjoerg .begin()) // FIXME - should we handle all of the parents?
112*e038c9c4Sjoerg ) {
113*e038c9c4Sjoerg for (auto &guardedVarAncestor : guardedVarAncestors) {
114*e038c9c4Sjoerg if (auto *CStmtAncestor = guardedVarAncestor.get<CompoundStmt>()) {
115*e038c9c4Sjoerg if (!HaveSkippedFirstCompoundStmt) {
116*e038c9c4Sjoerg HaveSkippedFirstCompoundStmt = true;
117*e038c9c4Sjoerg continue;
118*e038c9c4Sjoerg }
119*e038c9c4Sjoerg if (CStmtAncestor == guardiansClosestCompStmtAncestor)
120*e038c9c4Sjoerg return true;
121*e038c9c4Sjoerg }
122*e038c9c4Sjoerg }
123*e038c9c4Sjoerg }
124*e038c9c4Sjoerg
125*e038c9c4Sjoerg return false;
126*e038c9c4Sjoerg }
127*e038c9c4Sjoerg
128*e038c9c4Sjoerg class UncountedLocalVarsChecker
129*e038c9c4Sjoerg : public Checker<check::ASTDecl<TranslationUnitDecl>> {
130*e038c9c4Sjoerg BugType Bug{this,
131*e038c9c4Sjoerg "Uncounted raw pointer or reference not provably backed by "
132*e038c9c4Sjoerg "ref-counted variable",
133*e038c9c4Sjoerg "WebKit coding guidelines"};
134*e038c9c4Sjoerg mutable BugReporter *BR;
135*e038c9c4Sjoerg
136*e038c9c4Sjoerg public:
checkASTDecl(const TranslationUnitDecl * TUD,AnalysisManager & MGR,BugReporter & BRArg) const137*e038c9c4Sjoerg void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
138*e038c9c4Sjoerg BugReporter &BRArg) const {
139*e038c9c4Sjoerg BR = &BRArg;
140*e038c9c4Sjoerg
141*e038c9c4Sjoerg // The calls to checkAST* from AnalysisConsumer don't
142*e038c9c4Sjoerg // visit template instantiations or lambda classes. We
143*e038c9c4Sjoerg // want to visit those, so we make our own RecursiveASTVisitor.
144*e038c9c4Sjoerg struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
145*e038c9c4Sjoerg const UncountedLocalVarsChecker *Checker;
146*e038c9c4Sjoerg explicit LocalVisitor(const UncountedLocalVarsChecker *Checker)
147*e038c9c4Sjoerg : Checker(Checker) {
148*e038c9c4Sjoerg assert(Checker);
149*e038c9c4Sjoerg }
150*e038c9c4Sjoerg
151*e038c9c4Sjoerg bool shouldVisitTemplateInstantiations() const { return true; }
152*e038c9c4Sjoerg bool shouldVisitImplicitCode() const { return false; }
153*e038c9c4Sjoerg
154*e038c9c4Sjoerg bool VisitVarDecl(VarDecl *V) {
155*e038c9c4Sjoerg Checker->visitVarDecl(V);
156*e038c9c4Sjoerg return true;
157*e038c9c4Sjoerg }
158*e038c9c4Sjoerg };
159*e038c9c4Sjoerg
160*e038c9c4Sjoerg LocalVisitor visitor(this);
161*e038c9c4Sjoerg visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
162*e038c9c4Sjoerg }
163*e038c9c4Sjoerg
visitVarDecl(const VarDecl * V) const164*e038c9c4Sjoerg void visitVarDecl(const VarDecl *V) const {
165*e038c9c4Sjoerg if (shouldSkipVarDecl(V))
166*e038c9c4Sjoerg return;
167*e038c9c4Sjoerg
168*e038c9c4Sjoerg const auto *ArgType = V->getType().getTypePtr();
169*e038c9c4Sjoerg if (!ArgType)
170*e038c9c4Sjoerg return;
171*e038c9c4Sjoerg
172*e038c9c4Sjoerg Optional<bool> IsUncountedPtr = isUncountedPtr(ArgType);
173*e038c9c4Sjoerg if (IsUncountedPtr && *IsUncountedPtr) {
174*e038c9c4Sjoerg const Expr *const InitExpr = V->getInit();
175*e038c9c4Sjoerg if (!InitExpr)
176*e038c9c4Sjoerg return; // FIXME: later on we might warn on uninitialized vars too
177*e038c9c4Sjoerg
178*e038c9c4Sjoerg const clang::Expr *const InitArgOrigin =
179*e038c9c4Sjoerg tryToFindPtrOrigin(InitExpr, /*StopAtFirstRefCountedObj=*/false)
180*e038c9c4Sjoerg .first;
181*e038c9c4Sjoerg if (!InitArgOrigin)
182*e038c9c4Sjoerg return;
183*e038c9c4Sjoerg
184*e038c9c4Sjoerg if (isa<CXXThisExpr>(InitArgOrigin))
185*e038c9c4Sjoerg return;
186*e038c9c4Sjoerg
187*e038c9c4Sjoerg if (auto *Ref = llvm::dyn_cast<DeclRefExpr>(InitArgOrigin)) {
188*e038c9c4Sjoerg if (auto *MaybeGuardian =
189*e038c9c4Sjoerg dyn_cast_or_null<VarDecl>(Ref->getFoundDecl())) {
190*e038c9c4Sjoerg const auto *MaybeGuardianArgType =
191*e038c9c4Sjoerg MaybeGuardian->getType().getTypePtr();
192*e038c9c4Sjoerg if (!MaybeGuardianArgType)
193*e038c9c4Sjoerg return;
194*e038c9c4Sjoerg const CXXRecordDecl *const MaybeGuardianArgCXXRecord =
195*e038c9c4Sjoerg MaybeGuardianArgType->getAsCXXRecordDecl();
196*e038c9c4Sjoerg if (!MaybeGuardianArgCXXRecord)
197*e038c9c4Sjoerg return;
198*e038c9c4Sjoerg
199*e038c9c4Sjoerg if (MaybeGuardian->isLocalVarDecl() &&
200*e038c9c4Sjoerg (isRefCounted(MaybeGuardianArgCXXRecord) ||
201*e038c9c4Sjoerg isRefcountedStringsHack(MaybeGuardian)) &&
202*e038c9c4Sjoerg isGuardedScopeEmbeddedInGuardianScope(V, MaybeGuardian)) {
203*e038c9c4Sjoerg return;
204*e038c9c4Sjoerg }
205*e038c9c4Sjoerg
206*e038c9c4Sjoerg // Parameters are guaranteed to be safe for the duration of the call
207*e038c9c4Sjoerg // by another checker.
208*e038c9c4Sjoerg if (isa<ParmVarDecl>(MaybeGuardian))
209*e038c9c4Sjoerg return;
210*e038c9c4Sjoerg }
211*e038c9c4Sjoerg }
212*e038c9c4Sjoerg
213*e038c9c4Sjoerg reportBug(V);
214*e038c9c4Sjoerg }
215*e038c9c4Sjoerg }
216*e038c9c4Sjoerg
shouldSkipVarDecl(const VarDecl * V) const217*e038c9c4Sjoerg bool shouldSkipVarDecl(const VarDecl *V) const {
218*e038c9c4Sjoerg assert(V);
219*e038c9c4Sjoerg if (!V->isLocalVarDecl())
220*e038c9c4Sjoerg return true;
221*e038c9c4Sjoerg
222*e038c9c4Sjoerg if (isDeclaredInForOrIf(V))
223*e038c9c4Sjoerg return true;
224*e038c9c4Sjoerg
225*e038c9c4Sjoerg return false;
226*e038c9c4Sjoerg }
227*e038c9c4Sjoerg
reportBug(const VarDecl * V) const228*e038c9c4Sjoerg void reportBug(const VarDecl *V) const {
229*e038c9c4Sjoerg assert(V);
230*e038c9c4Sjoerg SmallString<100> Buf;
231*e038c9c4Sjoerg llvm::raw_svector_ostream Os(Buf);
232*e038c9c4Sjoerg
233*e038c9c4Sjoerg Os << "Local variable ";
234*e038c9c4Sjoerg printQuotedQualifiedName(Os, V);
235*e038c9c4Sjoerg Os << " is uncounted and unsafe.";
236*e038c9c4Sjoerg
237*e038c9c4Sjoerg PathDiagnosticLocation BSLoc(V->getLocation(), BR->getSourceManager());
238*e038c9c4Sjoerg auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
239*e038c9c4Sjoerg Report->addRange(V->getSourceRange());
240*e038c9c4Sjoerg BR->emitReport(std::move(Report));
241*e038c9c4Sjoerg }
242*e038c9c4Sjoerg };
243*e038c9c4Sjoerg } // namespace
244*e038c9c4Sjoerg
registerUncountedLocalVarsChecker(CheckerManager & Mgr)245*e038c9c4Sjoerg void ento::registerUncountedLocalVarsChecker(CheckerManager &Mgr) {
246*e038c9c4Sjoerg Mgr.registerChecker<UncountedLocalVarsChecker>();
247*e038c9c4Sjoerg }
248*e038c9c4Sjoerg
shouldRegisterUncountedLocalVarsChecker(const CheckerManager &)249*e038c9c4Sjoerg bool ento::shouldRegisterUncountedLocalVarsChecker(const CheckerManager &) {
250*e038c9c4Sjoerg return true;
251*e038c9c4Sjoerg }
252