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