clang 20.0.0git
RawPtrRefLocalVarsChecker.cpp
Go to the documentation of this file.
1//=======- UncountedLocalVarsChecker.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"
13#include "clang/AST/Decl.h"
14#include "clang/AST/DeclCXX.h"
22#include <optional>
23
24using namespace clang;
25using namespace ento;
26
27namespace {
28
29// FIXME: should be defined by anotations in the future
30bool isRefcountedStringsHack(const VarDecl *V) {
31 assert(V);
32 auto safeClass = [](const std::string &className) {
33 return className == "String" || className == "AtomString" ||
34 className == "UniquedString" || className == "Identifier";
35 };
36 QualType QT = V->getType();
37 auto *T = QT.getTypePtr();
38 if (auto *CXXRD = T->getAsCXXRecordDecl()) {
39 if (safeClass(safeGetName(CXXRD)))
40 return true;
41 }
42 if (T->isPointerType() || T->isReferenceType()) {
43 if (auto *CXXRD = T->getPointeeCXXRecordDecl()) {
44 if (safeClass(safeGetName(CXXRD)))
45 return true;
46 }
47 }
48 return false;
49}
50
51struct GuardianVisitor : DynamicRecursiveASTVisitor {
52 const VarDecl *Guardian{nullptr};
53
54 explicit GuardianVisitor(const VarDecl *Guardian) : Guardian(Guardian) {
55 assert(Guardian);
56 }
57
58 bool VisitBinaryOperator(BinaryOperator *BO) override {
59 if (BO->isAssignmentOp()) {
60 if (auto *VarRef = dyn_cast<DeclRefExpr>(BO->getLHS())) {
61 if (VarRef->getDecl() == Guardian)
62 return false;
63 }
64 }
65 return true;
66 }
67
68 bool VisitCXXConstructExpr(CXXConstructExpr *CE) override {
69 if (auto *Ctor = CE->getConstructor()) {
70 if (Ctor->isMoveConstructor() && CE->getNumArgs() == 1) {
71 auto *Arg = CE->getArg(0)->IgnoreParenCasts();
72 if (auto *VarRef = dyn_cast<DeclRefExpr>(Arg)) {
73 if (VarRef->getDecl() == Guardian)
74 return false;
75 }
76 }
77 }
78 return true;
79 }
80
81 bool VisitCXXMemberCallExpr(CXXMemberCallExpr *MCE) override {
82 auto MethodName = safeGetName(MCE->getMethodDecl());
83 if (MethodName == "swap" || MethodName == "leakRef" ||
84 MethodName == "releaseNonNull") {
85 auto *ThisArg = MCE->getImplicitObjectArgument()->IgnoreParenCasts();
86 if (auto *VarRef = dyn_cast<DeclRefExpr>(ThisArg)) {
87 if (VarRef->getDecl() == Guardian)
88 return false;
89 }
90 }
91 return true;
92 }
93
94 bool VisitCXXOperatorCallExpr(CXXOperatorCallExpr *OCE) override {
95 if (OCE->isAssignmentOp()) {
96 assert(OCE->getNumArgs() == 2);
97 auto *ThisArg = OCE->getArg(0)->IgnoreParenCasts();
98 if (auto *VarRef = dyn_cast<DeclRefExpr>(ThisArg)) {
99 if (VarRef->getDecl() == Guardian)
100 return false;
101 }
102 }
103 return true;
104 }
105};
106
107bool isGuardedScopeEmbeddedInGuardianScope(const VarDecl *Guarded,
108 const VarDecl *MaybeGuardian) {
109 assert(Guarded);
110 assert(MaybeGuardian);
111
112 if (!MaybeGuardian->isLocalVarDecl())
113 return false;
114
115 const CompoundStmt *guardiansClosestCompStmtAncestor = nullptr;
116
117 ASTContext &ctx = MaybeGuardian->getASTContext();
118
119 for (DynTypedNodeList guardianAncestors = ctx.getParents(*MaybeGuardian);
120 !guardianAncestors.empty();
121 guardianAncestors = ctx.getParents(
122 *guardianAncestors
123 .begin()) // FIXME - should we handle all of the parents?
124 ) {
125 for (auto &guardianAncestor : guardianAncestors) {
126 if (auto *CStmtParentAncestor = guardianAncestor.get<CompoundStmt>()) {
127 guardiansClosestCompStmtAncestor = CStmtParentAncestor;
128 break;
129 }
130 }
131 if (guardiansClosestCompStmtAncestor)
132 break;
133 }
134
135 if (!guardiansClosestCompStmtAncestor)
136 return false;
137
138 // We need to skip the first CompoundStmt to avoid situation when guardian is
139 // defined in the same scope as guarded variable.
140 const CompoundStmt *FirstCompondStmt = nullptr;
141 for (DynTypedNodeList guardedVarAncestors = ctx.getParents(*Guarded);
142 !guardedVarAncestors.empty();
143 guardedVarAncestors = ctx.getParents(
144 *guardedVarAncestors
145 .begin()) // FIXME - should we handle all of the parents?
146 ) {
147 for (auto &guardedVarAncestor : guardedVarAncestors) {
148 if (auto *CStmtAncestor = guardedVarAncestor.get<CompoundStmt>()) {
149 if (!FirstCompondStmt) {
150 FirstCompondStmt = CStmtAncestor;
151 continue;
152 }
153 if (CStmtAncestor == guardiansClosestCompStmtAncestor) {
154 GuardianVisitor guardianVisitor(MaybeGuardian);
155 auto *GuardedScope = const_cast<CompoundStmt *>(FirstCompondStmt);
156 return guardianVisitor.TraverseCompoundStmt(GuardedScope);
157 }
158 }
159 }
160 }
161
162 return false;
163}
164
165class RawPtrRefLocalVarsChecker
166 : public Checker<check::ASTDecl<TranslationUnitDecl>> {
167 BugType Bug;
168 mutable BugReporter *BR;
170
171public:
172 RawPtrRefLocalVarsChecker(const char *description)
173 : Bug(this, description, "WebKit coding guidelines") {}
174
175 virtual std::optional<bool> isUnsafePtr(const QualType T) const = 0;
176 virtual const char *ptrKind() const = 0;
177
178 void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
179 BugReporter &BRArg) const {
180 BR = &BRArg;
181
182 // The calls to checkAST* from AnalysisConsumer don't
183 // visit template instantiations or lambda classes. We
184 // want to visit those, so we make our own RecursiveASTVisitor.
185 struct LocalVisitor : DynamicRecursiveASTVisitor {
186 const RawPtrRefLocalVarsChecker *Checker;
187 Decl *DeclWithIssue{nullptr};
188
190
191 explicit LocalVisitor(const RawPtrRefLocalVarsChecker *Checker)
192 : Checker(Checker) {
193 assert(Checker);
194 ShouldVisitTemplateInstantiations = true;
195 ShouldVisitImplicitCode = false;
196 }
197
198 bool TraverseDecl(Decl *D) override {
199 llvm::SaveAndRestore SavedDecl(DeclWithIssue);
200 if (D && (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)))
201 DeclWithIssue = D;
203 }
204
205 bool VisitVarDecl(VarDecl *V) override {
206 auto *Init = V->getInit();
207 if (Init && V->isLocalVarDecl())
208 Checker->visitVarDecl(V, Init, DeclWithIssue);
209 return true;
210 }
211
212 bool VisitBinaryOperator(BinaryOperator *BO) override {
213 if (BO->isAssignmentOp()) {
214 if (auto *VarRef = dyn_cast<DeclRefExpr>(BO->getLHS())) {
215 if (auto *V = dyn_cast<VarDecl>(VarRef->getDecl()))
216 Checker->visitVarDecl(V, BO->getRHS(), DeclWithIssue);
217 }
218 }
219 return true;
220 }
221
222 bool TraverseIfStmt(IfStmt *IS) override {
223 if (!TFA.isTrivial(IS))
224 return DynamicRecursiveASTVisitor::TraverseIfStmt(IS);
225 return true;
226 }
227
228 bool TraverseForStmt(ForStmt *FS) override {
229 if (!TFA.isTrivial(FS))
230 return DynamicRecursiveASTVisitor::TraverseForStmt(FS);
231 return true;
232 }
233
234 bool TraverseCXXForRangeStmt(CXXForRangeStmt *FRS) override {
235 if (!TFA.isTrivial(FRS))
236 return DynamicRecursiveASTVisitor::TraverseCXXForRangeStmt(FRS);
237 return true;
238 }
239
240 bool TraverseWhileStmt(WhileStmt *WS) override {
241 if (!TFA.isTrivial(WS))
242 return DynamicRecursiveASTVisitor::TraverseWhileStmt(WS);
243 return true;
244 }
245
246 bool TraverseCompoundStmt(CompoundStmt *CS) override {
247 if (!TFA.isTrivial(CS))
248 return DynamicRecursiveASTVisitor::TraverseCompoundStmt(CS);
249 return true;
250 }
251 };
252
253 LocalVisitor visitor(this);
254 visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
255 }
256
257 void visitVarDecl(const VarDecl *V, const Expr *Value,
258 const Decl *DeclWithIssue) const {
259 if (shouldSkipVarDecl(V))
260 return;
261
262 std::optional<bool> IsUncountedPtr = isUnsafePtr(V->getType());
263 if (IsUncountedPtr && *IsUncountedPtr) {
265 Value, /*StopAtFirstRefCountedObj=*/false,
266 [&](const clang::Expr *InitArgOrigin, bool IsSafe) {
267 if (!InitArgOrigin || IsSafe)
268 return true;
269
270 if (isa<CXXThisExpr>(InitArgOrigin))
271 return true;
272
273 if (isa<CXXNullPtrLiteralExpr>(InitArgOrigin))
274 return true;
275
276 if (isa<IntegerLiteral>(InitArgOrigin))
277 return true;
278
279 if (isConstOwnerPtrMemberExpr(InitArgOrigin))
280 return true;
281
282 if (EFA.isACallToEnsureFn(InitArgOrigin))
283 return true;
284
285 if (auto *Ref = llvm::dyn_cast<DeclRefExpr>(InitArgOrigin)) {
286 if (auto *MaybeGuardian =
287 dyn_cast_or_null<VarDecl>(Ref->getFoundDecl())) {
288 const auto *MaybeGuardianArgType =
289 MaybeGuardian->getType().getTypePtr();
290 if (MaybeGuardianArgType) {
291 const CXXRecordDecl *const MaybeGuardianArgCXXRecord =
292 MaybeGuardianArgType->getAsCXXRecordDecl();
293 if (MaybeGuardianArgCXXRecord) {
294 if (MaybeGuardian->isLocalVarDecl() &&
295 (isRefCounted(MaybeGuardianArgCXXRecord) ||
296 isCheckedPtr(MaybeGuardianArgCXXRecord) ||
297 isRefcountedStringsHack(MaybeGuardian)) &&
298 isGuardedScopeEmbeddedInGuardianScope(
299 V, MaybeGuardian))
300 return true;
301 }
302 }
303
304 // Parameters are guaranteed to be safe for the duration of
305 // the call by another checker.
306 if (isa<ParmVarDecl>(MaybeGuardian))
307 return true;
308 }
309 }
310
311 return false;
312 }))
313 return;
314
315 reportBug(V, Value, DeclWithIssue);
316 }
317 }
318
319 bool shouldSkipVarDecl(const VarDecl *V) const {
320 assert(V);
321 return BR->getSourceManager().isInSystemHeader(V->getLocation());
322 }
323
324 void reportBug(const VarDecl *V, const Expr *Value,
325 const Decl *DeclWithIssue) const {
326 assert(V);
328 llvm::raw_svector_ostream Os(Buf);
329
330 if (dyn_cast<ParmVarDecl>(V)) {
331 Os << "Assignment to an " << ptrKind() << " parameter ";
333 Os << " is unsafe.";
334
335 PathDiagnosticLocation BSLoc(Value->getExprLoc(), BR->getSourceManager());
336 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
337 Report->addRange(Value->getSourceRange());
338 BR->emitReport(std::move(Report));
339 } else {
340 if (V->hasLocalStorage())
341 Os << "Local variable ";
342 else if (V->isStaticLocal())
343 Os << "Static local variable ";
344 else if (V->hasGlobalStorage())
345 Os << "Global variable ";
346 else
347 Os << "Variable ";
349 Os << " is " << ptrKind() << " and unsafe.";
350
351 PathDiagnosticLocation BSLoc(V->getLocation(), BR->getSourceManager());
352 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
353 Report->addRange(V->getSourceRange());
354 Report->setDeclWithIssue(DeclWithIssue);
355 BR->emitReport(std::move(Report));
356 }
357 }
358};
359
360class UncountedLocalVarsChecker final : public RawPtrRefLocalVarsChecker {
361public:
362 UncountedLocalVarsChecker()
363 : RawPtrRefLocalVarsChecker("Uncounted raw pointer or reference not "
364 "provably backed by ref-counted variable") {}
365 std::optional<bool> isUnsafePtr(const QualType T) const final {
366 return isUncountedPtr(T);
367 }
368 const char *ptrKind() const final { return "uncounted"; }
369};
370
371class UncheckedLocalVarsChecker final : public RawPtrRefLocalVarsChecker {
372public:
373 UncheckedLocalVarsChecker()
374 : RawPtrRefLocalVarsChecker("Unchecked raw pointer or reference not "
375 "provably backed by checked variable") {}
376 std::optional<bool> isUnsafePtr(const QualType T) const final {
377 return isUncheckedPtr(T);
378 }
379 const char *ptrKind() const final { return "unchecked"; }
380};
381
382} // namespace
383
384void ento::registerUncountedLocalVarsChecker(CheckerManager &Mgr) {
385 Mgr.registerChecker<UncountedLocalVarsChecker>();
386}
387
388bool ento::shouldRegisterUncountedLocalVarsChecker(const CheckerManager &) {
389 return true;
390}
391
392void ento::registerUncheckedLocalVarsChecker(CheckerManager &Mgr) {
393 Mgr.registerChecker<UncheckedLocalVarsChecker>();
394}
395
396bool ento::shouldRegisterUncheckedLocalVarsChecker(const CheckerManager &) {
397 return true;
398}
#define V(N, I)
Definition: ASTContext.h:3443
const Decl * D
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....
Defines the clang::SourceLocation class and associated facilities.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:188
DynTypedNodeList getParents(const NodeT &Node)
Forwards to get node parents from the ParentMapContext.
A builtin binary operation expression such as "x + y" or "x <= y".
Definition: Expr.h:3909
Expr * getLHS() const
Definition: Expr.h:3959
Expr * getRHS() const
Definition: Expr.h:3961
static bool isAssignmentOp(Opcode Opc)
Definition: Expr.h:4045
Represents a call to a C++ constructor.
Definition: ExprCXX.h:1546
Expr * getArg(unsigned Arg)
Return the specified argument.
Definition: ExprCXX.h:1689
CXXConstructorDecl * getConstructor() const
Get the constructor that this expression will (ultimately) call.
Definition: ExprCXX.h:1609
unsigned getNumArgs() const
Return the number of arguments to the constructor call.
Definition: ExprCXX.h:1686
CXXForRangeStmt - This represents C++0x [stmt.ranged]'s ranged for statement, represented as 'for (ra...
Definition: StmtCXX.h:135
Represents a call to a member function that may be written either with member call syntax (e....
Definition: ExprCXX.h:176
CXXMethodDecl * getMethodDecl() const
Retrieve the declaration of the called method.
Definition: ExprCXX.cpp:722
Expr * getImplicitObjectArgument() const
Retrieve the implicit object argument for the member call.
Definition: ExprCXX.cpp:703
A call to an overloaded operator written using operator syntax.
Definition: ExprCXX.h:81
static bool isAssignmentOp(OverloadedOperatorKind Opc)
Definition: ExprCXX.h:116
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Definition: Expr.h:3068
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
Definition: Expr.h:3055
CompoundStmt - This represents a group of statements like { stmt stmt }.
Definition: Stmt.h:1638
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:86
ASTContext & getASTContext() const LLVM_READONLY
Definition: DeclBase.cpp:520
Container for either a single DynTypedNode or for an ArrayRef to DynTypedNode.
Recursive AST visitor that supports extension via dynamic dispatch.
virtual bool TraverseDecl(Decl *D)
Recursively visit a declaration, by dispatching to Traverse*Decl() based on the argument's dynamic ty...
bool isACallToEnsureFn(const Expr *E) const
Definition: ASTUtils.cpp:198
This represents one expression.
Definition: Expr.h:110
Expr * IgnoreParenCasts() LLVM_READONLY
Skip past any parentheses and casts which might surround this expression until reaching a fixed point...
Definition: Expr.cpp:3095
ForStmt - This represents a 'for (init;cond;inc)' stmt.
Definition: Stmt.h:2818
IfStmt - This represents an if/then/else.
Definition: Stmt.h:2175
A (possibly-)qualified type.
Definition: Type.h:929
const Type * getTypePtr() const
Retrieves a pointer to the underlying (unqualified) type.
Definition: Type.h:7931
bool isInSystemHeader(SourceLocation Loc) const
Returns if a SourceLocation is in a system header.
The top declaration context.
Definition: Decl.h:84
An inter-procedural analysis facility that detects functions with "trivial" behavior with respect to ...
bool isTrivial(const Decl *D) const
CXXRecordDecl * getAsCXXRecordDecl() const
Retrieves the CXXRecordDecl that this type refers to, either because the type is a RecordType or beca...
Definition: Type.cpp:1916
bool isPointerType() const
Definition: Type.h:8186
bool isReferenceType() const
Definition: Type.h:8204
const CXXRecordDecl * getPointeeCXXRecordDecl() const
If this is a pointer or reference to a RecordType, return the CXXRecordDecl that the type refers to.
Definition: Type.cpp:1901
Represents a variable declaration or definition.
Definition: Decl.h:882
bool isLocalVarDecl() const
Returns true for local variable declarations other than parameters.
Definition: Decl.h:1204
WhileStmt - This represents a 'while' stmt.
Definition: Stmt.h:2621
BugReporter is a utility class for generating PathDiagnostics for analysis.
Definition: BugReporter.h:585
const SourceManager & getSourceManager()
Definition: BugReporter.h:623
virtual void emitReport(std::unique_ptr< BugReport > R)
Add the given report to the set of reports tracked by BugReporter.
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
The JSON file list parser is used to communicate input to InstallAPI.
if(T->getSizeExpr()) TRY_TO(TraverseStmt(const_cast< Expr * >(T -> getSizeExpr())))
void printQuotedQualifiedName(llvm::raw_ostream &Os, const NamedDeclDerivedT &D)
const FunctionProtoType * T
std::optional< bool > isUncountedPtr(const QualType T)
bool tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj, std::function< bool(const clang::Expr *, bool)> callback)
This function de-facto defines a set of transformations that we consider safe (in heuristical sense).
Definition: ASTUtils.cpp:25
std::string safeGetName(const T *ASTNode)
Definition: ASTUtils.h:81
std::optional< bool > isUnsafePtr(const QualType T)
bool isConstOwnerPtrMemberExpr(const clang::Expr *E)
Definition: ASTUtils.cpp:153
std::optional< bool > isUncheckedPtr(const QualType T)