clang 20.0.0git
UncountedLambdaCapturesChecker.cpp
Go to the documentation of this file.
1//=======- UncountedLambdaCapturesChecker.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"
18#include <optional>
19
20using namespace clang;
21using namespace ento;
22
23namespace {
24class UncountedLambdaCapturesChecker
25 : public Checker<check::ASTDecl<TranslationUnitDecl>> {
26private:
27 BugType Bug{this, "Lambda capture of uncounted variable",
28 "WebKit coding guidelines"};
29 mutable BugReporter *BR = nullptr;
31
32public:
33 void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
34 BugReporter &BRArg) const {
35 BR = &BRArg;
36
37 // The calls to checkAST* from AnalysisConsumer don't
38 // visit template instantiations or lambda classes. We
39 // want to visit those, so we make our own RecursiveASTVisitor.
40 struct LocalVisitor : DynamicRecursiveASTVisitor {
41 const UncountedLambdaCapturesChecker *Checker;
42 llvm::DenseSet<const DeclRefExpr *> DeclRefExprsToIgnore;
43 llvm::DenseSet<const LambdaExpr *> LambdasToIgnore;
44 QualType ClsType;
45
46 explicit LocalVisitor(const UncountedLambdaCapturesChecker *Checker)
47 : Checker(Checker) {
48 assert(Checker);
49 ShouldVisitTemplateInstantiations = true;
50 ShouldVisitImplicitCode = false;
51 }
52
53 bool TraverseCXXMethodDecl(CXXMethodDecl *CXXMD) override {
54 llvm::SaveAndRestore SavedDecl(ClsType);
55 if (CXXMD->isInstance())
56 ClsType = CXXMD->getThisType();
57 return DynamicRecursiveASTVisitor::TraverseCXXMethodDecl(CXXMD);
58 }
59
60 bool shouldCheckThis() {
61 auto result = !ClsType.isNull() ? isUnsafePtr(ClsType) : std::nullopt;
62 return result && *result;
63 }
64
65 bool VisitLambdaExpr(LambdaExpr *L) override {
66 if (LambdasToIgnore.contains(L))
67 return true;
68 Checker->visitLambdaExpr(L, shouldCheckThis());
69 return true;
70 }
71
72 bool VisitVarDecl(VarDecl *VD) override {
73 auto *Init = VD->getInit();
74 if (!Init)
75 return true;
76 auto *L = dyn_cast_or_null<LambdaExpr>(Init->IgnoreParenCasts());
77 if (!L)
78 return true;
79 LambdasToIgnore.insert(L); // Evaluate lambdas in VisitDeclRefExpr.
80 return true;
81 }
82
83 bool VisitDeclRefExpr(DeclRefExpr *DRE) override {
84 if (DeclRefExprsToIgnore.contains(DRE))
85 return true;
86 auto *VD = dyn_cast_or_null<VarDecl>(DRE->getDecl());
87 if (!VD)
88 return true;
89 auto *Init = VD->getInit();
90 if (!Init)
91 return true;
92 auto *L = dyn_cast_or_null<LambdaExpr>(Init->IgnoreParenCasts());
93 if (!L)
94 return true;
95 LambdasToIgnore.insert(L);
96 Checker->visitLambdaExpr(L, shouldCheckThis());
97 return true;
98 }
99
100 // WTF::switchOn(T, F... f) is a variadic template function and couldn't
101 // be annotated with NOESCAPE. We hard code it here to workaround that.
102 bool shouldTreatAllArgAsNoEscape(FunctionDecl *Decl) {
103 auto *NsDecl = Decl->getParent();
104 if (!NsDecl || !isa<NamespaceDecl>(NsDecl))
105 return false;
106 return safeGetName(NsDecl) == "WTF" && safeGetName(Decl) == "switchOn";
107 }
108
109 bool VisitCallExpr(CallExpr *CE) override {
110 checkCalleeLambda(CE);
111 if (auto *Callee = CE->getDirectCallee()) {
112 bool TreatAllArgsAsNoEscape = shouldTreatAllArgAsNoEscape(Callee);
113 unsigned ArgIndex = 0;
114 for (auto *Param : Callee->parameters()) {
115 if (ArgIndex >= CE->getNumArgs())
116 return true;
117 auto *Arg = CE->getArg(ArgIndex)->IgnoreParenCasts();
118 if (auto *L = findLambdaInArg(Arg)) {
119 LambdasToIgnore.insert(L);
120 if (!Param->hasAttr<NoEscapeAttr>() && !TreatAllArgsAsNoEscape)
121 Checker->visitLambdaExpr(L, shouldCheckThis());
122 }
123 ++ArgIndex;
124 }
125 }
126 return true;
127 }
128
129 LambdaExpr *findLambdaInArg(Expr *E) {
130 if (auto *Lambda = dyn_cast_or_null<LambdaExpr>(E))
131 return Lambda;
132 auto *TempExpr = dyn_cast_or_null<CXXBindTemporaryExpr>(E);
133 if (!TempExpr)
134 return nullptr;
135 E = TempExpr->getSubExpr()->IgnoreParenCasts();
136 if (!E)
137 return nullptr;
138 if (auto *Lambda = dyn_cast<LambdaExpr>(E))
139 return Lambda;
140 auto *CE = dyn_cast_or_null<CXXConstructExpr>(E);
141 if (!CE || !CE->getNumArgs())
142 return nullptr;
143 auto *CtorArg = CE->getArg(0)->IgnoreParenCasts();
144 if (!CtorArg)
145 return nullptr;
146 if (auto *Lambda = dyn_cast<LambdaExpr>(CtorArg))
147 return Lambda;
148 auto *DRE = dyn_cast<DeclRefExpr>(CtorArg);
149 if (!DRE)
150 return nullptr;
151 auto *VD = dyn_cast_or_null<VarDecl>(DRE->getDecl());
152 if (!VD)
153 return nullptr;
154 auto *Init = VD->getInit();
155 if (!Init)
156 return nullptr;
157 TempExpr = dyn_cast<CXXBindTemporaryExpr>(Init->IgnoreParenCasts());
158 if (!TempExpr)
159 return nullptr;
160 return dyn_cast_or_null<LambdaExpr>(TempExpr->getSubExpr());
161 }
162
163 void checkCalleeLambda(CallExpr *CE) {
164 auto *Callee = CE->getCallee();
165 if (!Callee)
166 return;
167 auto *DRE = dyn_cast<DeclRefExpr>(Callee->IgnoreParenCasts());
168 if (!DRE)
169 return;
170 auto *MD = dyn_cast_or_null<CXXMethodDecl>(DRE->getDecl());
171 if (!MD || CE->getNumArgs() < 1)
172 return;
173 auto *Arg = CE->getArg(0)->IgnoreParenCasts();
174 if (auto *L = dyn_cast_or_null<LambdaExpr>(Arg)) {
175 LambdasToIgnore.insert(L); // Calling a lambda upon creation is safe.
176 return;
177 }
178 auto *ArgRef = dyn_cast<DeclRefExpr>(Arg);
179 if (!ArgRef)
180 return;
181 auto *VD = dyn_cast_or_null<VarDecl>(ArgRef->getDecl());
182 if (!VD)
183 return;
184 auto *Init = VD->getInit();
185 if (!Init)
186 return;
187 auto *L = dyn_cast_or_null<LambdaExpr>(Init->IgnoreParenCasts());
188 if (!L)
189 return;
190 DeclRefExprsToIgnore.insert(ArgRef);
191 LambdasToIgnore.insert(L);
192 Checker->visitLambdaExpr(L, shouldCheckThis(),
193 /* ignoreParamVarDecl */ true);
194 }
195 };
196
197 LocalVisitor visitor(this);
198 visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
199 }
200
201 void visitLambdaExpr(LambdaExpr *L, bool shouldCheckThis,
202 bool ignoreParamVarDecl = false) const {
203 if (TFA.isTrivial(L->getBody()))
204 return;
205 for (const LambdaCapture &C : L->captures()) {
206 if (C.capturesVariable()) {
207 ValueDecl *CapturedVar = C.getCapturedVar();
208 if (ignoreParamVarDecl && isa<ParmVarDecl>(CapturedVar))
209 continue;
210 QualType CapturedVarQualType = CapturedVar->getType();
211 auto IsUncountedPtr = isUnsafePtr(CapturedVar->getType());
212 if (IsUncountedPtr && *IsUncountedPtr)
213 reportBug(C, CapturedVar, CapturedVarQualType);
214 } else if (C.capturesThis() && shouldCheckThis) {
215 if (ignoreParamVarDecl) // this is always a parameter to this function.
216 continue;
217 bool hasProtectThis = false;
218 for (const LambdaCapture &OtherCapture : L->captures()) {
219 if (!OtherCapture.capturesVariable())
220 continue;
221 if (auto *ValueDecl = OtherCapture.getCapturedVar()) {
222 if (protectThis(ValueDecl)) {
223 hasProtectThis = true;
224 break;
225 }
226 }
227 }
228 if (!hasProtectThis)
229 reportBugOnThisPtr(C);
230 }
231 }
232 }
233
234 bool protectThis(const ValueDecl *ValueDecl) const {
235 auto *VD = dyn_cast<VarDecl>(ValueDecl);
236 if (!VD)
237 return false;
238 auto *Init = VD->getInit()->IgnoreParenCasts();
239 if (!Init)
240 return false;
241 auto *BTE = dyn_cast<CXXBindTemporaryExpr>(Init);
242 if (!BTE)
243 return false;
244 auto *CE = dyn_cast_or_null<CXXConstructExpr>(BTE->getSubExpr());
245 if (!CE)
246 return false;
247 auto *Ctor = CE->getConstructor();
248 if (!Ctor)
249 return false;
250 auto clsName = safeGetName(Ctor->getParent());
251 if (!isRefType(clsName) || !CE->getNumArgs())
252 return false;
253 auto *Arg = CE->getArg(0)->IgnoreParenCasts();
254 while (auto *UO = dyn_cast<UnaryOperator>(Arg)) {
255 auto OpCode = UO->getOpcode();
256 if (OpCode == UO_Deref || OpCode == UO_AddrOf)
257 Arg = UO->getSubExpr();
258 else
259 break;
260 }
261 return isa<CXXThisExpr>(Arg);
262 }
263
264 void reportBug(const LambdaCapture &Capture, ValueDecl *CapturedVar,
265 const QualType T) const {
266 assert(CapturedVar);
267
269 llvm::raw_svector_ostream Os(Buf);
270
271 if (Capture.isExplicit()) {
272 Os << "Captured ";
273 } else {
274 Os << "Implicitly captured ";
275 }
276 if (T->isPointerType()) {
277 Os << "raw-pointer ";
278 } else {
279 assert(T->isReferenceType());
280 Os << "reference ";
281 }
282
283 printQuotedQualifiedName(Os, Capture.getCapturedVar());
284 Os << " to ref-counted type or CheckedPtr-capable type is unsafe.";
285
287 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
288 BR->emitReport(std::move(Report));
289 }
290
291 void reportBugOnThisPtr(const LambdaCapture &Capture) const {
293 llvm::raw_svector_ostream Os(Buf);
294
295 if (Capture.isExplicit()) {
296 Os << "Captured ";
297 } else {
298 Os << "Implicitly captured ";
299 }
300
301 Os << "raw-pointer 'this' to ref-counted type or CheckedPtr-capable type "
302 "is unsafe.";
303
305 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
306 BR->emitReport(std::move(Report));
307 }
308};
309} // namespace
310
311void ento::registerUncountedLambdaCapturesChecker(CheckerManager &Mgr) {
312 Mgr.registerChecker<UncountedLambdaCapturesChecker>();
313}
314
315bool ento::shouldRegisterUncountedLambdaCapturesChecker(
316 const CheckerManager &mgr) {
317 return true;
318}
Expr * E
Represents a static or instance method of a struct/union/class.
Definition: DeclCXX.h:2078
QualType getThisType() const
Return the type of the this pointer.
Definition: DeclCXX.cpp:2657
bool isInstance() const
Definition: DeclCXX.h:2105
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition: Expr.h:2874
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Definition: Expr.h:3068
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return null.
Definition: Expr.h:3047
Expr * getCallee()
Definition: Expr.h:3024
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
Definition: Expr.h:3055
A reference to a declared variable, function, enum, etc.
Definition: Expr.h:1265
ValueDecl * getDecl()
Definition: Expr.h:1333
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:86
Recursive AST visitor that supports extension via dynamic dispatch.
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
Represents a function declaration or definition.
Definition: Decl.h:1935
Describes the capture of a variable or of this, or of a C++1y init-capture.
Definition: LambdaCapture.h:25
A C++ lambda expression, which produces a function object (of unspecified type) that can be invoked l...
Definition: ExprCXX.h:1954
Stmt * getBody() const
Retrieve the body of the lambda.
Definition: ExprCXX.cpp:1327
capture_range captures() const
Retrieve this lambda's captures.
Definition: ExprCXX.cpp:1352
A (possibly-)qualified type.
Definition: Type.h:929
bool isNull() const
Return true if this QualType doesn't point to a type yet.
Definition: Type.h:996
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
bool isPointerType() const
Definition: Type.h:8186
bool isReferenceType() const
Definition: Type.h:8204
Represent the declaration of a variable (in which case it is an lvalue) a function (in which case it ...
Definition: Decl.h:671
QualType getType() const
Definition: Decl.h:682
Represents a variable declaration or definition.
Definition: Decl.h:882
const Expr * getInit() const
Definition: Decl.h:1319
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.
SourceLocation getLocation() const
Retrieve the location at which this variable was captured.
Definition: ScopeInfo.h:686
The JSON file list parser is used to communicate input to InstallAPI.
void printQuotedQualifiedName(llvm::raw_ostream &Os, const NamedDeclDerivedT &D)
bool isRefType(const std::string &Name)
const FunctionProtoType * T
std::string safeGetName(const T *ASTNode)
Definition: ASTUtils.h:81
std::optional< bool > isUnsafePtr(const QualType T)